You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3139 lines
92 KiB
3139 lines
92 KiB
#include "vol.h"
|
|
|
|
#include "hwdev.h"
|
|
#include "hnotif.h"
|
|
#include "mtpts.h"
|
|
#include "regnotif.h"
|
|
#include "drvbase.h"
|
|
#include "dtctreg.h"
|
|
#include "users.h"
|
|
|
|
//
|
|
// ISSUE-2001/01/08-StephStm that's bad, vol should not depend on dtct...
|
|
//
|
|
#include "dtct.h"
|
|
|
|
#include "reg.h"
|
|
#include "sfstr.h"
|
|
#include "misc.h"
|
|
|
|
#pragma warning(disable: 4201)
|
|
#include <winioctl.h>
|
|
#pragma warning(default: 4201)
|
|
|
|
#include <ntddcdrm.h>
|
|
#include <ntddmmc.h>
|
|
#include <ioevent.h>
|
|
#include <shpriv.h>
|
|
|
|
#include <setupapi.h>
|
|
|
|
#include "mischlpr.h"
|
|
#include "dbg.h"
|
|
|
|
#include <strsafe.h>
|
|
|
|
#define ARRAYSIZE(a) (sizeof((a))/sizeof((a)[0]))
|
|
|
|
#define STATE_DRIVETYPE 0x00000001
|
|
#define STATE_UPDATECONSTINFO 0x00000002
|
|
#define STATE_DEVICENUMBERINFO 0x00000004
|
|
#define STATE_DEVICEINST 0x00000008
|
|
#define STATE_HWDEVICEINST 0x00000010
|
|
#define STATE_MEDIAINFO 0x00000020
|
|
#define STATE_GVIFAILED 0x10000000
|
|
#define STATE_GFAFAILED 0x20000000
|
|
#define STATE_UPDATEHASMEDIAFAILED 0x40000000
|
|
|
|
#define INVALID_DWORD ((DWORD)-1)
|
|
|
|
#define MPFE_UNDETERMINED ((DWORD)0x0DEF0DEF)
|
|
#define MPFE_FALSE ((DWORD)0)
|
|
#define MPFE_TRUE ((DWORD)1)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Public
|
|
// No need for the critical section, since it will not be added to the
|
|
// NamedList until init is finsihed and has succeeded. Until it's in the
|
|
// namedlist, no code can grab a pointer to this object and call it.
|
|
HRESULT CVolume::Init(LPCWSTR pszElemName)
|
|
{
|
|
HRESULT hres = _cs.Init();
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = _SetName(pszElemName);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
CImpersonateEveryone* pieo;
|
|
|
|
hres = CHWEventDetectorHelper::GetImpersonateEveryone(&pieo);
|
|
|
|
if (SUCCEEDED(hres) && (S_FALSE != hres))
|
|
{
|
|
hres = pieo->Impersonate();
|
|
|
|
if (SUCCEEDED(hres) && (S_FALSE != hres))
|
|
{
|
|
hres = _InitHelper(pszElemName);
|
|
|
|
pieo->RevertToSelf();
|
|
}
|
|
|
|
pieo->RCRelease();
|
|
}
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT _IsDeviceFullyInstalled(LPCWSTR pszDeviceIntfID, BOOL* pfDeviceFullyInstalled)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HDEVINFO hdevinfo = SetupDiCreateDeviceInfoList(NULL, NULL);
|
|
|
|
*pfDeviceFullyInstalled = FALSE;
|
|
|
|
if (INVALID_HANDLE_VALUE != hdevinfo)
|
|
{
|
|
SP_DEVICE_INTERFACE_DATA sdid = {0};
|
|
|
|
sdid.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|
|
|
if (SetupDiOpenDeviceInterface(hdevinfo, pszDeviceIntfID, 0, &sdid))
|
|
{
|
|
DWORD cbsdidd = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) +
|
|
(MAX_DEVICE_ID_LEN * sizeof(WCHAR));
|
|
|
|
SP_DEVINFO_DATA sdd = {0};
|
|
SP_DEVICE_INTERFACE_DETAIL_DATA* psdidd =
|
|
(SP_DEVICE_INTERFACE_DETAIL_DATA*)LocalAlloc(LPTR, cbsdidd);
|
|
|
|
if (psdidd)
|
|
{
|
|
psdidd->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
sdd.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
|
|
// SetupDiGetDeviceInterfaceDetail (below) requires that the
|
|
// cbSize member of SP_DEVICE_INTERFACE_DETAIL_DATA be set
|
|
// to the size of the fixed part of the structure, and to pass
|
|
// the size of the full thing as the 4th param.
|
|
|
|
if (SetupDiGetDeviceInterfaceDetail(hdevinfo, &sdid, psdidd,
|
|
cbsdidd, NULL, &sdd))
|
|
{
|
|
DWORD dw;
|
|
|
|
if (SetupDiGetDeviceRegistryProperty(hdevinfo, &sdd,
|
|
SPDRP_INSTALL_STATE, 0, (PBYTE)&dw, sizeof(dw), NULL))
|
|
{
|
|
TRACE(TF_VOLUME, TEXT(">>>> Got SPDRP_INSTALL_STATE property: 0x%08X"), dw);
|
|
|
|
if (CM_INSTALL_STATE_INSTALLED == dw)
|
|
{
|
|
*pfDeviceFullyInstalled = TRUE;
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
LocalFree((HLOCAL)psdidd);
|
|
}
|
|
|
|
SetupDiDeleteDeviceInterfaceData(hdevinfo, &sdid);
|
|
}
|
|
|
|
SetupDiDestroyDeviceInfoList(hdevinfo);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_InitHelper(LPCWSTR pszElemName)
|
|
{
|
|
BOOL fDeviceFullyInstalled;
|
|
HRESULT hres = _IsDeviceFullyInstalled(_pszElemName, &fDeviceFullyInstalled);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (!fDeviceFullyInstalled)
|
|
{
|
|
TRACE(TF_VOLUME,
|
|
TEXT("!!!!!!!!!!!! Device not fully installed!\n %s"),
|
|
pszElemName);
|
|
|
|
hres = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
HANDLE hDevice;
|
|
BOOL fCloseHandle = TRUE;
|
|
|
|
hres = _GetDeviceHandleSafe(&hDevice, FALSE);
|
|
|
|
if (SUCCEEDED(hres) && (S_FALSE != hres))
|
|
{
|
|
hres = _InitDriveType(hDevice);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
_dwState |= STATE_DRIVETYPE;
|
|
|
|
hres = S_OK;
|
|
|
|
if ((HWDTS_FIXEDDISK != _dwDriveType) &&
|
|
(HWDTS_FLOPPY35 != _dwDriveType) &&
|
|
(HWDTS_FLOPPY525 != _dwDriveType))
|
|
{
|
|
_CloseDeviceHandleSafe(hDevice);
|
|
|
|
fCloseHandle = FALSE;
|
|
|
|
// Removable disk drives + CD require access to
|
|
// the media to query all the required info
|
|
hres = _GetDeviceHandleSafe(&hDevice, TRUE);
|
|
}
|
|
|
|
if (SUCCEEDED(hres) && (S_FALSE != hres))
|
|
{
|
|
hres = _UpdateConstInfo(hDevice);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
_dwState |= STATE_UPDATECONSTINFO;
|
|
}
|
|
|
|
if ((HWDTS_FLOPPY35 != _dwDriveType) &&
|
|
(HWDTS_FLOPPY525 != _dwDriveType))
|
|
{
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
fCloseHandle = TRUE;
|
|
|
|
hres = _GetDeviceNumberInfoFromHandle(hDevice,
|
|
&_devtype, &_ulDeviceNumber,
|
|
&_ulPartitionNumber);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (S_FALSE != hres)
|
|
{
|
|
_dwState |= STATE_DEVICENUMBERINFO;
|
|
}
|
|
|
|
GUID guidDummy;
|
|
|
|
hres = _GetDeviceInstance(pszElemName,
|
|
&_devinst, &guidDummy);
|
|
|
|
if (SUCCEEDED(hres) && (S_FALSE != hres))
|
|
{
|
|
_dwState |= STATE_DEVICEINST;
|
|
|
|
hres = _InitHWDeviceInst();
|
|
|
|
if (SUCCEEDED(hres) && (S_FALSE != hres))
|
|
{
|
|
hres = _UpdateSoftEjectCaps();
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = _UpdateRemovableDevice();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We need to do this anyway, even if the
|
|
// DeviceNumber stuff and/or the deviceInst stuff
|
|
// fails. Otherwise we'll have a very sorry volume.
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = _UpdateMediaInfo(hDevice, FALSE);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (S_FALSE != hres)
|
|
{
|
|
_dwState |= STATE_MEDIAINFO;
|
|
}
|
|
|
|
hres = _RegisterNotif();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = _CreateMountPoints();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_HandleAccessDenied();
|
|
}
|
|
}
|
|
|
|
if (fCloseHandle)
|
|
{
|
|
_CloseDeviceHandleSafe(hDevice);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_HandleAccessDenied();
|
|
}
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
void CVolume::_HandleAccessDenied()
|
|
{
|
|
if (ERROR_ACCESS_DENIED == GetLastError())
|
|
{
|
|
_dwVolumeFlags |= HWDVF_STATE_ACCESSDENIED;
|
|
}
|
|
}
|
|
|
|
// *pdwFloppy
|
|
// 0: not a floppy
|
|
// 35: 3.5" flopy
|
|
// 525: 5.25" floppy
|
|
HRESULT _DeviceIsFloppy(HANDLE hDevice, DWORD* pdwFloppy, BOOL* pfFloppySupportsSoftEject)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
DISK_GEOMETRY dg[12] = {0};
|
|
DWORD cbBuf = sizeof(dg);
|
|
DWORD dwReturned;
|
|
|
|
*pdwFloppy = 0;
|
|
*pfFloppySupportsSoftEject = FALSE;
|
|
|
|
// Should be IOCTL_..._DISK_GEOMETRY...
|
|
if (DeviceIoControl(hDevice, IOCTL_DISK_GET_MEDIA_TYPES,
|
|
NULL, 0, dg, cbBuf, &dwReturned, NULL))
|
|
{
|
|
DWORD cMediaInfo = dwReturned / sizeof(DISK_GEOMETRY);
|
|
|
|
for (DWORD dw = 0; !(*pdwFloppy) && (dw < cMediaInfo); ++dw)
|
|
{
|
|
switch (dg[dw].MediaType)
|
|
{
|
|
case F5_1Pt2_512: // Cap: 1200
|
|
case F5_360_512: // Cap: 360
|
|
case F5_320_512: // Cap: 360
|
|
case F5_320_1024: // Cap: 360
|
|
case F5_180_512: // Cap: 360
|
|
case F5_160_512: // Cap: 360
|
|
*pdwFloppy = 525;
|
|
break;
|
|
|
|
case F3_120M_512: // Cap: 120MB
|
|
*pfFloppySupportsSoftEject = TRUE;
|
|
|
|
case F3_1Pt44_512: // Cap: 1440
|
|
case F3_2Pt88_512: // Cap: 2880
|
|
case F3_20Pt8_512: // Cap: 2880
|
|
case F3_720_512: // Cap: 720
|
|
*pdwFloppy = 35;
|
|
break;
|
|
//
|
|
// Japanese specific device types from here.
|
|
//
|
|
case F5_1Pt23_1024: // Cap: 1200
|
|
case F5_640_512: // Cap: 260
|
|
case F5_720_512: // Cap: 360
|
|
*pdwFloppy = 525;
|
|
break;
|
|
|
|
case F3_640_512: // Cap: 720
|
|
case F3_1Pt2_512: // Cap: 1440
|
|
case F3_1Pt23_1024: // Cap: 1440
|
|
*pdwFloppy = 35;
|
|
break;
|
|
|
|
case F3_128Mb_512: // Cap: ?
|
|
case F3_230Mb_512: // Cap: ?
|
|
case F3_200Mb_512:
|
|
*pdwFloppy = 35;
|
|
break;
|
|
}
|
|
|
|
if (*pdwFloppy)
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT _GetDriveTypeInfo(HANDLE hDevice, DWORD* pdwDriveType, BOOL* pfFloppy);
|
|
|
|
HRESULT CVolume::_InitDriveType(HANDLE hDevice)
|
|
{
|
|
BOOL fFloppy = FALSE;
|
|
|
|
HRESULT hr = _GetDriveTypeInfo(hDevice, &_dwDriveType, &fFloppy);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (fFloppy)
|
|
{
|
|
DWORD dwFloppy;
|
|
BOOL fFloppySupportsSoftEject;
|
|
|
|
hr = _DeviceIsFloppy(hDevice, &dwFloppy, &fFloppySupportsSoftEject);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
if (fFloppySupportsSoftEject)
|
|
{
|
|
_dwDriveCap |= HWDDC_FLOPPYSOFTEJECT;
|
|
}
|
|
|
|
switch (dwFloppy)
|
|
{
|
|
case 35:
|
|
_dwDriveType = HWDTS_FLOPPY35;
|
|
break;
|
|
case 525:
|
|
_dwDriveType = HWDTS_FLOPPY525;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT _GetRemovableDeviceInstRecurs(DEVINST devinst,
|
|
DEVINST* pdevinst)
|
|
{
|
|
BOOL fRemovable;
|
|
HRESULT hres = _DeviceInstIsRemovable(devinst, &fRemovable);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (fRemovable)
|
|
{
|
|
// Found it!
|
|
*pdevinst = devinst;
|
|
}
|
|
else
|
|
{
|
|
// Recurse
|
|
DEVINST devinstParent;
|
|
|
|
CONFIGRET cr = CM_Get_Parent_Ex(&devinstParent, devinst, 0,
|
|
NULL);
|
|
|
|
if (CR_SUCCESS == cr)
|
|
{
|
|
hres = _GetRemovableDeviceInstRecurs(devinstParent, pdevinst);
|
|
}
|
|
else
|
|
{
|
|
hres = S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CVolume::_GetDeviceIDDisk(LPWSTR pszDeviceIDDisk,
|
|
DWORD cchDeviceIDDisk)
|
|
{
|
|
HRESULT hr;
|
|
|
|
_cs.Enter();
|
|
|
|
if (_szDeviceIDDisk[0])
|
|
{
|
|
hr = SafeStrCpyN(pszDeviceIDDisk, _szDeviceIDDisk, cchDeviceIDDisk);
|
|
}
|
|
else
|
|
{
|
|
if (((ULONG)-1) != _ulDeviceNumber)
|
|
{
|
|
CNamedElemList* pnel;
|
|
hr = CHWEventDetectorHelper::GetList(HWEDLIST_DISK, &pnel);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
CNamedElemEnum* penum;
|
|
|
|
hr = pnel->GetEnum(&penum);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CNamedElem* pelem;
|
|
BOOL fFoundIt = FALSE;
|
|
|
|
while (!fFoundIt && SUCCEEDED(hr = penum->Next(&pelem)) &&
|
|
(S_FALSE != hr))
|
|
{
|
|
CDisk* pdisk = (CDisk*)pelem;
|
|
ULONG ulDeviceNumber;
|
|
|
|
hr = pdisk->GetDeviceNumber(&ulDeviceNumber);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
if (_ulDeviceNumber == ulDeviceNumber)
|
|
{
|
|
DEVICE_TYPE devtype;
|
|
|
|
hr = pdisk->GetDeviceType(&devtype);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
if (_devtype == devtype)
|
|
{
|
|
// Use me!
|
|
DWORD cchReq;
|
|
|
|
hr = pelem->GetName(pszDeviceIDDisk,
|
|
cchDeviceIDDisk, &cchReq);
|
|
|
|
fFoundIt = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pelem->RCRelease();
|
|
}
|
|
|
|
penum->RCRelease();
|
|
}
|
|
|
|
pnel->RCRelease();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
if (FAILED(SafeStrCpyN(_szDeviceIDDisk, pszDeviceIDDisk,
|
|
ARRAYSIZE(_szDeviceIDDisk))))
|
|
{
|
|
_szDeviceIDDisk[0] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
_cs.Leave();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_InitHWDeviceInst()
|
|
{
|
|
WCHAR szDeviceIDDisk[MAX_DEVICEID];
|
|
|
|
HRESULT hr = _GetDeviceIDDisk(szDeviceIDDisk, ARRAYSIZE(szDeviceIDDisk));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DEVINST devinstFinal = 0;
|
|
GUID guidDummy;
|
|
|
|
if (S_FALSE != hr)
|
|
{
|
|
DEVINST devinstDisk;
|
|
|
|
hr = _GetDeviceInstance(szDeviceIDDisk, &devinstDisk, &guidDummy);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
hr = _GetRemovableDeviceInstRecurs(devinstDisk, &devinstFinal);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (S_FALSE == hr)
|
|
{
|
|
// Maybe this is not a removable device (not talking
|
|
// about removable disk).
|
|
BOOL fFoundProp;
|
|
WCHAR szProp[1];
|
|
ULONG ulData = sizeof(szProp);
|
|
|
|
// First check if the disk interface has customn properties associated with it.
|
|
CONFIGRET cr = CM_Get_DevNode_Custom_Property(devinstDisk, TEXT("DeviceGroup"),
|
|
NULL, (PBYTE)szProp, &ulData, 0);
|
|
|
|
if ((CR_SUCCESS == cr) || (CR_BUFFER_SMALL == cr))
|
|
{
|
|
fFoundProp = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ulData = sizeof(szProp);
|
|
cr = CM_Get_DevNode_Custom_Property(devinstDisk, TEXT("Icons"),
|
|
NULL, (PBYTE)szProp, &ulData, 0);
|
|
|
|
if ((CR_SUCCESS == cr) || (CR_BUFFER_SMALL == cr))
|
|
{
|
|
fFoundProp = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fFoundProp = FALSE;
|
|
}
|
|
}
|
|
|
|
if (fFoundProp)
|
|
{
|
|
devinstFinal = devinstDisk;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
// Let's get the parent devinst of this devinst.
|
|
if (CR_SUCCESS == CM_Get_Parent_Ex(&devinstFinal,
|
|
devinstDisk, 0, NULL))
|
|
{
|
|
hr = S_OK;
|
|
DIAGNOSTIC((TEXT("[0303]Got DeviceInst from parent of disk")));
|
|
|
|
TRACE(TF_VOLUME,
|
|
TEXT("HWDevInst: Got devinst from parent of Disk for Disk\n %s"),
|
|
_pszElemName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DIAGNOSTIC((TEXT("[0302]Got DeviceInst from Removable Device")));
|
|
|
|
TRACE(TF_VOLUME,
|
|
TEXT("HWDevInst: Got devinst from Removable Device for Volume\n %s"),
|
|
_pszElemName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DIAGNOSTIC((TEXT("[0304]Did NOT get DeviceInst from the disk")));
|
|
|
|
TRACE(TF_VOLUME,
|
|
TEXT("HWDevInst: Did not get a devinst from the Disk for Volume\n %s"),
|
|
_pszElemName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DIAGNOSTIC((TEXT("[0305]Got DeviceInst from Volume itself")));
|
|
|
|
TRACE(TF_VOLUME,
|
|
TEXT("HWDevInst: Did not get a Disk, get devinst from Volume itself for Volume\n (%s)"),
|
|
_pszElemName);
|
|
|
|
// We did not get a device number for the volume
|
|
// Let's get the device instance from the volume then, maybe there's no
|
|
// volume-disk-device hierarchy
|
|
hr = _GetDeviceInstance(_pszElemName, &devinstFinal, &guidDummy);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
hr = _hwdevinst.Init(devinstFinal);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
hr = _hwdevinst.InitInterfaceGUID(&guidVolumeClass);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
_fHWDevInstInited = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::GetHWDeviceInst(CHWDeviceInst** pphwdevinst)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (_fHWDevInstInited)
|
|
{
|
|
*pphwdevinst = &_hwdevinst;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
*pphwdevinst = NULL;
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_ShouldTryAutoplay(BOOL* pfTry)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (_dwMediaState & HWDMS_PRESENT)
|
|
{
|
|
if (_dwMediaState & HWDMS_FORMATTED)
|
|
{
|
|
if (!(_dwMediaCap & HWDMC_HASDVDMOVIE))
|
|
{
|
|
if (!(_dwMediaCap & HWDMC_HASAUTORUNINF) ||
|
|
(_dwMediaCap & HWDMC_HASUSEAUTOPLAY))
|
|
{
|
|
WCHAR szVolumeGUID[MAX_PATH + 50];
|
|
LPWSTR pszFile;
|
|
|
|
if (_dwMediaCap & HWDMC_HASUSEAUTOPLAY)
|
|
{
|
|
DIAGNOSTIC((TEXT("[0316]Autorun.inf, BUT as a UseAutoPLAY entry -> try Autoplay!")));
|
|
}
|
|
|
|
hr = SafeStrCpyN(szVolumeGUID, _szVolumeGUID,
|
|
ARRAYSIZE(szVolumeGUID));
|
|
|
|
*pfTry = TRUE;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HKEY hkey;
|
|
pszFile = szVolumeGUID + lstrlen(szVolumeGUID);
|
|
|
|
TRACE(TF_LEAK, TEXT("Special files - BEGIN"));
|
|
|
|
hr = _RegOpenKey(HKEY_LOCAL_MACHINE,
|
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoplayHandlers\\CancelAutoplay\\Files"),
|
|
&hkey);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
DWORD dwIndex = 0;
|
|
|
|
while ((*pfTry) && SUCCEEDED(hr = _RegEnumStringValue(hkey, dwIndex, pszFile,
|
|
(DWORD)(ARRAYSIZE(szVolumeGUID) - (pszFile - szVolumeGUID)))) &&
|
|
(S_FALSE != hr))
|
|
{
|
|
WIN32_FIND_DATA w32fd;
|
|
HANDLE h;
|
|
|
|
h = FindFirstFile(szVolumeGUID, &w32fd);
|
|
|
|
if (INVALID_HANDLE_VALUE != h)
|
|
{
|
|
FindClose(h);
|
|
|
|
*pfTry = FALSE;
|
|
|
|
DIAGNOSTIC((TEXT("[0307]Detected special file : '%s' (%s) -> No Autoplay!"),
|
|
w32fd.cFileName, pszFile));
|
|
|
|
TRACE(TF_VOLUME,
|
|
TEXT("Detected %s (%s) -> No Autoplay!"),
|
|
w32fd.cFileName , pszFile);
|
|
}
|
|
|
|
++dwIndex;
|
|
}
|
|
|
|
_RegCloseKey(hkey);
|
|
}
|
|
|
|
TRACE(TF_LEAK, TEXT("Special files - END"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DIAGNOSTIC((TEXT("[0313]Autorun.inf -> No Content Autoplay!")));
|
|
hr = S_OK;
|
|
*pfTry = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DIAGNOSTIC((TEXT("[0312]DVD Movie -> No Content Autoplay!")));
|
|
hr = S_OK;
|
|
*pfTry = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DIAGNOSTIC((TEXT("[0317]Media *NOT* formatted -> No Content Autoplay!")));
|
|
hr = S_OK;
|
|
*pfTry = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DIAGNOSTIC((TEXT("[0306]NO media -> No Content Autoplay!")));
|
|
hr = S_OK;
|
|
*pfTry = FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_HandleMediaArrival()
|
|
{
|
|
DIAGNOSTIC((TEXT("[0002]Processing Media Arrival Event: %s"), _pszElemName));
|
|
|
|
_dwMediaPresentFromEvent = MPFE_TRUE;
|
|
|
|
_cs.Enter();
|
|
|
|
HRESULT hr = _UpdateMediaInfo(INVALID_HANDLE_VALUE, TRUE);
|
|
|
|
_cs.Leave();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (_fHWDevInstInited)
|
|
{
|
|
// Try Autoplay?
|
|
BOOL fTryAutoplay;
|
|
|
|
hr = _ShouldTryAutoplay(&fTryAutoplay);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (fTryAutoplay)
|
|
{
|
|
BOOL fHasHandler;
|
|
|
|
hr = CHWEventDetectorImpl::HandleVolumeMediaEvent(
|
|
_pszElemName, &_hwdevinst, TEXT("MediaArrival"),
|
|
&fHasHandler);
|
|
|
|
if (SUCCEEDED(hr) && fHasHandler)
|
|
{
|
|
_dwVolumeFlags |= HWDVF_STATE_HASAUTOPLAYHANDLER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dwVolumeFlags |= HWDVF_STATE_DONOTSNIFFCONTENT;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DIAGNOSTIC((TEXT("[0308]Cannot find hardware device for this volume -> No Autoplay!")));
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_AdviseVolumeChangeHelper(FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_AdviseVolumeMountingEvent(DWORD dwEvent)
|
|
{
|
|
return CHardwareDevicesImpl::_AdviseVolumeMountingEvent(_pszElemName,
|
|
dwEvent);
|
|
}
|
|
|
|
HRESULT CVolume::_AdviseVolumeChangeHelper(BOOL fAdded)
|
|
{
|
|
HRESULT hr;
|
|
VOLUMEINFO2* pvolinfo2;
|
|
|
|
_cs.Enter();
|
|
|
|
hr = _GetVOLUMEINFO2(&pvolinfo2);
|
|
|
|
_cs.Leave();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPWSTR pszMtPts;
|
|
DWORD cchMtPts;
|
|
hr = _GetMountPoints(&pszMtPts, &cchMtPts);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CHardwareDevicesImpl::_AdviseVolumeArrivedOrUpdated(pvolinfo2,
|
|
pszMtPts, cchMtPts, fAdded);
|
|
|
|
if (S_FALSE != hr)
|
|
{
|
|
LocalFree((HLOCAL)pszMtPts);
|
|
}
|
|
}
|
|
|
|
_FreeMemoryChunk<VOLUMEINFO2*>(pvolinfo2);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_HandleMediaRemoval()
|
|
{
|
|
HRESULT hr;
|
|
|
|
DIAGNOSTIC((TEXT("[0003]Processing Media Removal Event: %s"), _pszElemName));
|
|
|
|
_dwMediaPresentFromEvent = MPFE_FALSE;
|
|
|
|
_cs.Enter();
|
|
|
|
hr = _UpdateMediaInfoOnRemove();
|
|
|
|
_cs.Leave();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_AdviseVolumeChangeHelper(FALSE);
|
|
|
|
if (_fHWDevInstInited)
|
|
{
|
|
// Useless in this case, since there's no content, so we won't sniff
|
|
BOOL fHasHandler;
|
|
|
|
hr = CHWEventDetectorImpl::HandleVolumeMediaEvent(_pszElemName,
|
|
&_hwdevinst, TEXT("MediaRemoval"), &fHasHandler);
|
|
}
|
|
else
|
|
{
|
|
DIAGNOSTIC((TEXT("[0309]Cannot find hardware device for this volume -> No Autoplay!")));
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_HandleVolumeChange()
|
|
{
|
|
HRESULT hr;
|
|
|
|
_cs.Enter();
|
|
|
|
hr = _UpdateMediaInfo(INVALID_HANDLE_VALUE, TRUE);
|
|
|
|
_cs.Leave();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_AdviseVolumeChangeHelper(FALSE);
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::HNTHandleEvent(DEV_BROADCAST_HANDLE* pdbh,
|
|
DWORD dwEventType)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
if (DBT_CUSTOMEVENT == dwEventType)
|
|
{
|
|
if (GUID_IO_MEDIA_ARRIVAL == pdbh->dbch_eventguid)
|
|
{
|
|
// This notification arrive very soon when the media is inserted
|
|
// in the device. Often the Volume driver has not been loaded on
|
|
// the drive and calls like GetVolumeInformation will fail.
|
|
// Instead of doing something right now, flag the event, and
|
|
// process on the first GUID_IO_VOLUME_MOUNT below.
|
|
TRACE(TF_SHHWDTCTDTCT,
|
|
TEXT("****CVolume GUID_IO_MEDIA_ARRIVAL"));
|
|
|
|
hres = _HandleMediaArrival();
|
|
}
|
|
else if (GUID_IO_MEDIA_REMOVAL == pdbh->dbch_eventguid)
|
|
{
|
|
TRACE(TF_SHHWDTCTDTCT,
|
|
TEXT("****CVolume GUID_IO_MEDIA_REMOVAL"));
|
|
|
|
hres = _HandleMediaRemoval();
|
|
}
|
|
else if (GUID_IO_VOLUME_MOUNT == pdbh->dbch_eventguid)
|
|
{
|
|
TRACE(TF_SHHWDTCTDTCT,
|
|
TEXT("****CVolume GUID_IO_VOLUME_MOUNT"));
|
|
|
|
if (_dwVolumeFlags & HWDVF_STATE_DISMOUNTED)
|
|
{
|
|
_dwVolumeFlags &= ~HWDVF_STATE_DISMOUNTED;
|
|
|
|
hres = _AdviseVolumeMountingEvent(
|
|
SHHARDWAREEVENT_VOLUMEMOUNTED);
|
|
|
|
_HandleVolumeChange();
|
|
}
|
|
}
|
|
else if (GUID_IO_VOLUME_NAME_CHANGE == pdbh->dbch_eventguid)
|
|
{
|
|
TRACE(TF_SHHWDTCTDTCT,
|
|
TEXT("****CVolume GUID_IO_VOLUME_NAME_CHANGE"));
|
|
|
|
_cs.Enter();
|
|
|
|
hres = _UpdateMountPoints();
|
|
|
|
_cs.Leave();
|
|
}
|
|
else if (GUID_IO_VOLUME_CHANGE == pdbh->dbch_eventguid)
|
|
{
|
|
TRACE(TF_SHHWDTCTDTCT,
|
|
TEXT("****CVolume GUID_IO_VOLUME_CHANGE"));
|
|
|
|
_cs.Enter();
|
|
|
|
// This is for bug 645878. Basically, it's to cover the case of a
|
|
// volume mounted on another volume's folder and then transfered
|
|
// to another clustered machine. We use to miss the volume
|
|
// mounted on the folder. Even thought the right event to send
|
|
// would have been GUID_IO_VOLUME_NAME_CHANGE, it swas not
|
|
// possible unless the cluster guys would have unmounted the volume
|
|
// from the folder and remounted it, which is overkill just for an
|
|
// icon.
|
|
hres = _UpdateMountPoints();
|
|
|
|
_cs.Leave();
|
|
|
|
hres = _HandleVolumeChange();
|
|
}
|
|
else if (GUID_IO_VOLUME_DISMOUNT == pdbh->dbch_eventguid)
|
|
{
|
|
_dwVolumeFlags |= HWDVF_STATE_DISMOUNTED;
|
|
|
|
hres = _AdviseVolumeMountingEvent(
|
|
SHHARDWAREEVENT_VOLUMEDISMOUNTED);
|
|
}
|
|
else if (GUID_IO_VOLUME_DISMOUNT_FAILED == pdbh->dbch_eventguid)
|
|
{
|
|
_dwVolumeFlags &= ~HWDVF_STATE_DISMOUNTED;
|
|
|
|
hres = _AdviseVolumeMountingEvent(
|
|
SHHARDWAREEVENT_VOLUMEMOUNTED);
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CVolume::_GetDeviceHandleSafe(HANDLE* phDevice,
|
|
BOOL fGenericReadRequired)
|
|
{
|
|
ASSERT(!_hdevnotify);
|
|
HRESULT hr;
|
|
HANDLE hDevice = _GetDeviceHandle(_pszElemName,
|
|
fGenericReadRequired ? GENERIC_READ : FILE_READ_ATTRIBUTES);
|
|
|
|
*phDevice = INVALID_HANDLE_VALUE;
|
|
|
|
if (INVALID_HANDLE_VALUE != hDevice)
|
|
{
|
|
DEV_BROADCAST_HANDLE dbhNotifFilter = {0};
|
|
|
|
dbhNotifFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
|
|
dbhNotifFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
|
dbhNotifFilter.dbch_handle = hDevice;
|
|
|
|
hr = CHWEventDetectorHelper::RegisterDeviceNotification(
|
|
&dbhNotifFilter, &_hdevnotify, FALSE);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
*phDevice = hDevice;
|
|
|
|
#ifdef DEBUG
|
|
_fGenericReadRequired = fGenericReadRequired;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
_CloseDeviceHandle(hDevice);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_CloseDeviceHandleSafe(HANDLE hDevice)
|
|
{
|
|
ASSERT(_hdevnotify);
|
|
|
|
_CloseDeviceHandle(hDevice);
|
|
UnregisterDeviceNotification(_hdevnotify);
|
|
|
|
_hdevnotify = NULL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CVolume::_UnregisterNotif()
|
|
{
|
|
CNamedElemList* pnel;
|
|
HRESULT hres = CHWEventDetectorHelper::GetList(HWEDLIST_HANDLENOTIF,
|
|
&pnel);
|
|
|
|
if (S_OK == hres)
|
|
{
|
|
hres = pnel->Remove(_pszElemName);
|
|
|
|
if (_szDeviceIDDisk[0])
|
|
{
|
|
hres = pnel->Remove(_szDeviceIDDisk);
|
|
}
|
|
|
|
pnel->RCRelease();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CVolume::_RegisterNotif()
|
|
{
|
|
CNamedElemList* pnel;
|
|
|
|
HRESULT hres = CHWEventDetectorHelper::GetList(HWEDLIST_HANDLENOTIF,
|
|
&pnel);
|
|
|
|
if (S_OK == hres)
|
|
{
|
|
CNamedElem* pelem;
|
|
|
|
// register for handle notification
|
|
hres = pnel->GetOrAdd(_pszElemName, &pelem);
|
|
|
|
// Was it already there or added?
|
|
if (SUCCEEDED(hres) && (S_FALSE == hres))
|
|
{
|
|
// Added. Initialize it.
|
|
CHandleNotif* phnotif = (CHandleNotif*)pelem;
|
|
|
|
hres = phnotif->InitNotif(this);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (HWDTS_REMOVABLEDISK == _dwDriveType)
|
|
{
|
|
// Removable disk drives receive their notifications for
|
|
// media arrival/removal on the disk interface, not the
|
|
// volume one, so register for this too.
|
|
WCHAR szDeviceIDDisk[MAX_DEVICEID];
|
|
|
|
hres = _GetDeviceIDDisk(szDeviceIDDisk,
|
|
ARRAYSIZE(szDeviceIDDisk));
|
|
|
|
if (SUCCEEDED(hres) && (S_FALSE != hres))
|
|
{
|
|
CNamedElem* pelem2;
|
|
|
|
// register for handle notification
|
|
hres = pnel->GetOrAdd(szDeviceIDDisk, &pelem2);
|
|
|
|
// Was it already there or added?
|
|
if (SUCCEEDED(hres) && (S_FALSE == hres))
|
|
{
|
|
// Added. Initialize it.
|
|
CHandleNotif* phnotif2 = (CHandleNotif*)pelem2;
|
|
|
|
hres = phnotif2->InitNotif(this);
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
pnel->Remove(szDeviceIDDisk);
|
|
}
|
|
|
|
pelem2->RCRelease();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
pnel->Remove(_pszElemName);
|
|
}
|
|
|
|
pelem->RCRelease();
|
|
}
|
|
|
|
pnel->RCRelease();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CVolume::_RemoveMtPt(LPWSTR pszMtPt)
|
|
{
|
|
CNamedElemList* pnel;
|
|
HRESULT hr = CHWEventDetectorHelper::GetList(HWEDLIST_MTPT, &pnel);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = pnel->Remove(pszMtPt);
|
|
|
|
pnel->RCRelease();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_CreateMtPt(LPWSTR pszMtPt)
|
|
{
|
|
CNamedElemList* pnel;
|
|
HRESULT hres = CHWEventDetectorHelper::GetList(HWEDLIST_MTPT, &pnel);
|
|
|
|
if (S_OK == hres)
|
|
{
|
|
CNamedElem* pelem;
|
|
hres = pnel->GetOrAdd(pszMtPt, &pelem);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Init one way or the other
|
|
CMtPt* pmtpt = (CMtPt*)pelem;
|
|
|
|
hres = pmtpt->InitVolume(_pszElemName);
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
pnel->Remove(pszMtPt);
|
|
}
|
|
|
|
pelem->RCRelease();
|
|
}
|
|
|
|
pnel->RCRelease();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CVolume::_AdviseMountPointHelper(LPCWSTR pszMountPoint, BOOL fAdded)
|
|
{
|
|
// I'd like to not call this from outside the crit sect
|
|
ASSERT(_cs.IsInside());
|
|
|
|
WCHAR szDeviceIDVolume[MAX_DEVICEID];
|
|
DWORD cchReq;
|
|
|
|
HRESULT hr = GetName(szDeviceIDVolume, ARRAYSIZE(szDeviceIDVolume), &cchReq);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CHardwareDevicesImpl::_AdviseMountPointHelper(pszMountPoint,
|
|
szDeviceIDVolume, fAdded);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_UpdateMountPoints()
|
|
{
|
|
LPWSTR pszMtPtNew;
|
|
DWORD cchMtPtNew;
|
|
HRESULT hr = _GetMountPoints(&pszMtPtNew, &cchMtPtNew);
|
|
|
|
#ifdef DEBUG
|
|
if (_pszMountPoints)
|
|
{
|
|
TRACE(TF_VOLUME, TEXT("_UpdateMountPoints: OLD mountPoints for %s:"), _pszElemName);
|
|
|
|
for (LPWSTR psz = _pszMountPoints; *psz; psz += lstrlen(psz) + 1)
|
|
{
|
|
TRACE(TF_VOLUME, TEXT(" %s"), psz);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (S_FALSE != hr)
|
|
{
|
|
#ifdef DEBUG
|
|
TRACE(TF_VOLUME, TEXT("_UpdateMountPoints: NEW mountPoints:"));
|
|
|
|
for (LPWSTR psz = pszMtPtNew; *psz; psz += lstrlen(psz) + 1)
|
|
{
|
|
TRACE(TF_VOLUME, TEXT(" %s"), psz);
|
|
}
|
|
#endif
|
|
// Was there at least one?
|
|
if (_pszMountPoints)
|
|
{
|
|
// Yep, find the removed ones
|
|
for (LPWSTR pszOld = _pszMountPoints; *pszOld;
|
|
pszOld += lstrlen(pszOld) + 1)
|
|
{
|
|
BOOL fFoundInNew = FALSE;
|
|
|
|
for (LPWSTR pszNew = pszMtPtNew; !fFoundInNew && *pszNew;
|
|
pszNew += lstrlen(pszNew) + 1)
|
|
{
|
|
if (!lstrcmpi(pszNew, pszOld))
|
|
{
|
|
fFoundInNew = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!fFoundInNew)
|
|
{
|
|
TRACE(TF_VOLUME, TEXT("_UpdateMountPoints: Found DELETED one: %s"), pszOld);
|
|
|
|
// That's a deleted one
|
|
_RemoveMtPt(pszOld);
|
|
|
|
_AdviseMountPointHelper(pszOld, FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE(TF_VOLUME, TEXT("_UpdateMountPoints: There was NO mountpoints before"));
|
|
}
|
|
|
|
// Find the new ones
|
|
for (LPWSTR pszNew = pszMtPtNew; *pszNew;
|
|
pszNew += lstrlen(pszNew) + 1)
|
|
{
|
|
BOOL fFoundInOld = FALSE;
|
|
|
|
if (_pszMountPoints)
|
|
{
|
|
for (LPWSTR pszOld = _pszMountPoints;
|
|
!fFoundInOld && *pszOld; pszOld += lstrlen(pszOld) + 1)
|
|
{
|
|
if (!lstrcmpi(pszNew, pszOld))
|
|
{
|
|
fFoundInOld = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!fFoundInOld)
|
|
{
|
|
TRACE(TF_VOLUME, TEXT("_UpdateMountPoints: Found ADDED one: %s"), pszNew);
|
|
|
|
// That's a new one
|
|
_CreateMtPt(pszNew);
|
|
|
|
_AdviseMountPointHelper(pszNew, TRUE);
|
|
}
|
|
}
|
|
|
|
LocalFree(_pszMountPoints);
|
|
_pszMountPoints = pszMtPtNew;
|
|
_cchMountPoints = cchMtPtNew;
|
|
}
|
|
else
|
|
{
|
|
TRACE(TF_VOLUME, TEXT("_UpdateMountPoints: NO MountPoints left, remove all old ones"));
|
|
|
|
if (_pszMountPoints && *_pszMountPoints)
|
|
{
|
|
for (LPWSTR pszOld = _pszMountPoints; *pszOld;
|
|
pszOld += lstrlen(pszOld) + 1)
|
|
{
|
|
_RemoveMtPt(pszOld);
|
|
|
|
_AdviseMountPointHelper(pszOld, FALSE);
|
|
}
|
|
|
|
LocalFree(_pszMountPoints);
|
|
_pszMountPoints = NULL;
|
|
_cchMountPoints = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_CreateMountPoints()
|
|
{
|
|
HRESULT hr = _GetMountPoints(&_pszMountPoints, &_cchMountPoints);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (S_FALSE != hr)
|
|
{
|
|
for (LPWSTR psz = _pszMountPoints; *psz;
|
|
psz += lstrlen(psz) + 1)
|
|
{
|
|
_CreateMtPt(psz);
|
|
// go on even if error
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Caller must free returned data using LocalFree
|
|
HRESULT CVolume::_GetMountPoints(LPWSTR* ppsz, DWORD* pcch)
|
|
{
|
|
HRESULT hr;
|
|
LPWSTR psz = NULL;
|
|
DWORD cch;
|
|
|
|
*ppsz = NULL;
|
|
*pcch = 0;
|
|
|
|
if (GetVolumePathNamesForVolumeName(_szVolumeGUID, NULL, 0, &cch))
|
|
{
|
|
// no mountpoint, we're done
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Expected, even wanted...
|
|
if (ERROR_MORE_DATA == GetLastError())
|
|
{
|
|
psz = (LPWSTR)LocalAlloc(LPTR, cch * sizeof(WCHAR));
|
|
|
|
if (psz)
|
|
{
|
|
if (GetVolumePathNamesForVolumeName(_szVolumeGUID,
|
|
psz, cch, &cch))
|
|
{
|
|
*ppsz = psz;
|
|
*pcch = cch;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
LocalFree(psz);
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT _DeviceMediaIsAccessible(HANDLE hDevice, BOOL* pfAccessible)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
DWORD dwDummy;
|
|
|
|
*pfAccessible = FALSE;
|
|
|
|
// requires GENERIC_READ access on the handle
|
|
BOOL b = DeviceIoControl(hDevice, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, &dwDummy, NULL);
|
|
if (ERROR_MEDIA_CHANGED == GetLastError())
|
|
{
|
|
// try one more time, ERROR_MEDIA_CHANGED means it's still pending for a little bit.
|
|
b = DeviceIoControl(hDevice, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, &dwDummy, NULL);
|
|
}
|
|
|
|
if (b)
|
|
{
|
|
*pfAccessible = TRUE;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
// ERROR_NOT_READY == GetLastError() means no media
|
|
if (ERROR_NOT_READY == GetLastError())
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hres = S_FALSE;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CVolume::_UpdateHasMedia(HANDLE hDevice)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
|
|
switch (_dwDriveType)
|
|
{
|
|
case HWDTS_FIXEDDISK:
|
|
_dwMediaState |= HWDMS_PRESENT;
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case HWDTS_REMOVABLEDISK:
|
|
case HWDTS_CDROM:
|
|
{
|
|
#if 0
|
|
This does not work on my laptop
|
|
if (_dwDriveCap & HWDDC_CAPABILITY_SUPPORTDETECTION)
|
|
{
|
|
// It's MMC2
|
|
if (_dwMediaCap & (HWDMC_CDROM | HWDMC_CDRECORDABLE |
|
|
HWDMC_CDREWRITABLE | HWDMC_DVDROM | HWDMC_DVDRECORDABLE |
|
|
HWDMC_DVDRAM))
|
|
{
|
|
_dwMediaState |= HWDMS_PRESENT;
|
|
}
|
|
else
|
|
{
|
|
_dwMediaState &= ~HWDMS_PRESENT;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (MPFE_UNDETERMINED != _dwMediaPresentFromEvent)
|
|
{
|
|
hr = S_OK;
|
|
|
|
if (MPFE_TRUE == _dwMediaPresentFromEvent)
|
|
{
|
|
_dwMediaState |= HWDMS_PRESENT;
|
|
}
|
|
else
|
|
{
|
|
_dwMediaState &= ~HWDMS_PRESENT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BOOL fAccessible;
|
|
|
|
ASSERT(_fGenericReadRequired);
|
|
|
|
hr = _DeviceMediaIsAccessible(hDevice, &fAccessible);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
if (fAccessible)
|
|
{
|
|
_dwMediaState |= HWDMS_PRESENT;
|
|
}
|
|
else
|
|
{
|
|
_dwMediaState &= ~HWDMS_PRESENT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dwMediaState &= ~HWDMS_PRESENT;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// ISSUE-2000/10/23-StephStm: We do not handle CD Changer, maybe we should
|
|
//
|
|
// case HWDTS_CDCHANGER:
|
|
case HWDTS_FLOPPY35:
|
|
case HWDTS_FLOPPY525:
|
|
_dwMediaState &= ~HWDMS_PRESENT;
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_ExtractAutorunIconAndLabel()
|
|
{
|
|
WCHAR szInfFile[MAX_PATH + 50];
|
|
LPWSTR pszNext;
|
|
DWORD cchLeft;
|
|
|
|
HRESULT hr = SafeStrCpyNEx(szInfFile, _szVolumeGUID, ARRAYSIZE(szInfFile),
|
|
&pszNext, &cchLeft);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SafeStrCpyN(pszNext, TEXT("Autorun.inf"), cchLeft);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR szDummy[4];
|
|
#if defined(_X86_)
|
|
LPWSTR pszSection = TEXT("AutoRun.x86");
|
|
#elif defined(_AMD64_)
|
|
LPWSTR pszSection = TEXT("AutoRun.Amd64");
|
|
#elif defined(_IA64_)
|
|
LPWSTR pszSection = TEXT("AutoRun.Ia64");
|
|
#else
|
|
#error "No Target Architecture"
|
|
#endif
|
|
|
|
// Flush some buffer somewhere
|
|
WritePrivateProfileString(NULL, NULL, NULL, szInfFile);
|
|
|
|
if (!GetPrivateProfileString(pszSection, TEXT("Icon"), TEXT(""),
|
|
_szAutorunIconLocation, ARRAYSIZE(_szAutorunIconLocation),
|
|
szInfFile))
|
|
{
|
|
pszSection = TEXT("AutoRun");
|
|
|
|
_HandleAccessDenied();
|
|
|
|
if (!GetPrivateProfileString(pszSection, TEXT("Icon"),
|
|
TEXT(""), _szAutorunIconLocation,
|
|
ARRAYSIZE(_szAutorunIconLocation), szInfFile))
|
|
{
|
|
_szAutorunIconLocation[0] = 0;
|
|
_HandleAccessDenied();
|
|
}
|
|
}
|
|
|
|
if (!GetPrivateProfileString(pszSection, TEXT("Label"), TEXT(""),
|
|
_szAutorunLabel, ARRAYSIZE(_szAutorunLabel), szInfFile))
|
|
{
|
|
_szAutorunLabel[0] = 0;
|
|
_HandleAccessDenied();
|
|
}
|
|
|
|
if (GetPrivateProfileString(pszSection, TEXT("Open"), TEXT(""),
|
|
szDummy, ARRAYSIZE(szDummy), szInfFile) ||
|
|
GetPrivateProfileString(pszSection, TEXT("ShellExecute"), TEXT(""),
|
|
szDummy, ARRAYSIZE(szDummy), szInfFile))
|
|
{
|
|
_dwMediaCap |= HWDMC_HASAUTORUNCOMMAND;
|
|
}
|
|
else
|
|
{
|
|
_HandleAccessDenied();
|
|
}
|
|
|
|
if (GetPrivateProfileString(pszSection, TEXT("UseAutoPLAY"), TEXT(""),
|
|
szDummy, ARRAYSIZE(szDummy), szInfFile))
|
|
{
|
|
_dwMediaCap |= HWDMC_HASUSEAUTOPLAY;
|
|
}
|
|
else
|
|
{
|
|
_HandleAccessDenied();
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_UpdateSpecialFilePresence()
|
|
{
|
|
struct SPECIALFILEINFO
|
|
{
|
|
LPCWSTR pszFile;
|
|
DWORD dwCapBit;
|
|
};
|
|
|
|
HRESULT hr;
|
|
WCHAR szPath[50 + 1 + ARRAYSIZE(TEXT("video_ts\\video_ts.ifo"))];
|
|
|
|
static const SPECIALFILEINFO rgsfi[] =
|
|
{
|
|
{ TEXT("autorun.inf"), HWDMC_HASAUTORUNINF },
|
|
{ TEXT("desktop.ini"), HWDMC_HASDESKTOPINI },
|
|
{ TEXT("video_ts\\video_ts.ifo"), HWDMC_HASDVDMOVIE },
|
|
};
|
|
|
|
LPWSTR pszNext;
|
|
DWORD cchLeft;
|
|
|
|
hr = SafeStrCpyNEx(szPath, _szVolumeGUID,
|
|
ARRAYSIZE(szPath), &pszNext, &cchLeft);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
for (DWORD dw = 0; dw < ARRAYSIZE(rgsfi); ++dw)
|
|
{
|
|
hr = SafeStrCpyN(pszNext, rgsfi[dw].pszFile, cchLeft);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DWORD dwGFA = GetFileAttributes(szPath);
|
|
|
|
if (-1 != dwGFA)
|
|
{
|
|
_dwMediaCap |= (rgsfi[dw].dwCapBit);
|
|
}
|
|
else
|
|
{
|
|
_HandleAccessDenied();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// To fix bug 425431
|
|
if (HWDMC_HASDVDMOVIE & _dwMediaCap)
|
|
{
|
|
// This better be a CD/DVD drive.
|
|
if (HWDTS_CDROM != _dwDriveType)
|
|
{
|
|
// No. Remove the flag otherwise, Hard Disks get a Play verb
|
|
// when they have the ts_video\video_ts.ifo file in their root.
|
|
_dwMediaCap &= ~HWDMC_HASDVDMOVIE;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#define TRACK_TYPE_MASK 0x04
|
|
#define AUDIO_TRACK 0x00
|
|
#define DATA_TRACK 0x04
|
|
|
|
HRESULT CVolume::_UpdateTrackInfo(HANDLE hDevice)
|
|
{
|
|
HRESULT hr;
|
|
|
|
ASSERT(!(_dwMediaCap & (HWDMC_HASDATATRACKS | HWDMC_HASAUDIOTRACKS)));
|
|
|
|
hr = S_OK;
|
|
// To be compatible with Win95, we'll only return TRUE from this
|
|
// function if the disc has ONLY audio tracks (and NO data tracks).
|
|
|
|
// FEATURE: Post NT-SUR beta 1, we should consider adding a new
|
|
// DriveType flag for "contains data tracks" and revamp the commands
|
|
// available on a CD-ROM drive. The current code doesn't handle
|
|
// mixed audio/data and audio/autorun discs very usefully. --JonBe
|
|
|
|
// First try the new IOCTL which gives us a ULONG with bits indicating
|
|
// the presence of either/both data & audio tracks
|
|
|
|
CDROM_DISK_DATA data;
|
|
DWORD dwDummy;
|
|
|
|
// Requires GENERIC_READ access on the handle
|
|
if (DeviceIoControl(hDevice, IOCTL_CDROM_DISK_TYPE, NULL, 0, &data,
|
|
sizeof(data), &dwDummy, NULL))
|
|
{
|
|
if (data.DiskData & CDROM_DISK_AUDIO_TRACK)
|
|
{
|
|
_dwMediaCap |= HWDMC_HASAUDIOTRACKS;
|
|
}
|
|
|
|
if (data.DiskData & CDROM_DISK_DATA_TRACK)
|
|
{
|
|
_dwMediaCap |= HWDMC_HASDATATRACKS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// else that failed, so try to look for audio tracks the old way, by
|
|
// looking throught the table of contents manually. Note that data tracks
|
|
// are supposed to be hidden in the TOC by CDFS now on mixed audio/data
|
|
// discs (at least if the data tracks follow the audio tracks).
|
|
CDROM_TOC toc = {0};
|
|
|
|
if (!DeviceIoControl(hDevice, IOCTL_CDROM_READ_TOC, NULL, 0, &toc,
|
|
sizeof(toc), &dwDummy, NULL))
|
|
{
|
|
SUB_Q_CHANNEL_DATA subq = {0};
|
|
CDROM_SUB_Q_DATA_FORMAT df = {0};
|
|
|
|
// We might not have been able to read the TOC because the drive
|
|
// was busy playing audio. Lets try querying the audio position.
|
|
df.Format = IOCTL_CDROM_CURRENT_POSITION;
|
|
df.Track = 0;
|
|
|
|
if (DeviceIoControl(hDevice, IOCTL_CDROM_READ_Q_CHANNEL, &df,
|
|
sizeof(df), &subq, sizeof(subq), &dwDummy, NULL))
|
|
{
|
|
if (AUDIO_DATA_TRACK & subq.CurrentPosition.Control)
|
|
{
|
|
_dwMediaCap |= HWDMC_HASDATATRACKS;
|
|
}
|
|
else
|
|
{
|
|
_dwMediaCap |= HWDMC_HASAUDIOTRACKS;
|
|
}
|
|
}
|
|
|
|
// Is there the equivalent of IOCTL_CDROM_READ_Q_CHANNEL for data?
|
|
_dwMediaCap |= HWDMC_HASDATATRACKS_UNDETERMINED;
|
|
}
|
|
else
|
|
{
|
|
int nTracks = (toc.LastTrack - toc.FirstTrack) + 1;
|
|
int iTrack = 0;
|
|
|
|
// Now iterate through the tracks looking for Audio data
|
|
while ((iTrack < nTracks) &&
|
|
((_dwMediaCap & (HWDMC_HASDATATRACKS | HWDMC_HASDATATRACKS)) !=
|
|
(HWDMC_HASDATATRACKS | HWDMC_HASDATATRACKS))
|
|
)
|
|
{
|
|
if ((toc.TrackData[iTrack].Control & TRACK_TYPE_MASK) == AUDIO_TRACK)
|
|
{
|
|
_dwMediaCap |= HWDMC_HASAUDIOTRACKS;
|
|
}
|
|
else
|
|
{
|
|
if ((toc.TrackData[iTrack].Control & TRACK_TYPE_MASK) == DATA_TRACK)
|
|
{
|
|
_dwMediaCap |= HWDMC_HASDATATRACKS;
|
|
}
|
|
}
|
|
++iTrack;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_UpdateMediaInfoOnRemove()
|
|
{
|
|
_dwMediaCap = 0;
|
|
_dwMediaState = 0;
|
|
|
|
_szAutorunIconLocation[0] = 0;
|
|
_szAutorunLabel[0] = 0;
|
|
_dwSerialNumber = 0xBADBADBD;
|
|
|
|
SafeStrCpyN(_szLabel, TEXT("Invalid"), ARRAYSIZE(_szLabel));
|
|
SafeStrCpyN(_szFileSystem, TEXT("Invalid"), ARRAYSIZE(_szFileSystem));
|
|
_dwRootAttributes = 0xBADBADBD;
|
|
_dwMaxFileNameLen = 0xBADBADBD;
|
|
_dwFileSystemFlags = 0xBADBADBD;
|
|
|
|
_dwVolumeFlags &= ~(HWDVF_STATE_HASAUTOPLAYHANDLER |
|
|
HWDVF_STATE_DONOTSNIFFCONTENT | HWDVF_STATE_JUSTDOCKED);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
inline BOOL _XOR(BOOL bA, BOOL bB)
|
|
{
|
|
return (bA && !bB) || (!bA && bB);
|
|
}
|
|
|
|
HRESULT CVolume::_UpdateMediaInfo(HANDLE hDevice, BOOL fGetYourOwnHandle)
|
|
{
|
|
HRESULT hr;
|
|
CImpersonateEveryone* pieo = NULL;
|
|
|
|
ASSERT(_XOR((hDevice != INVALID_HANDLE_VALUE), fGetYourOwnHandle));
|
|
|
|
if (fGetYourOwnHandle)
|
|
{
|
|
hr = CHWEventDetectorHelper::GetImpersonateEveryone(&pieo);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
hr = pieo->Impersonate();
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
hr = _GetDeviceHandleSafe(&hDevice, TRUE);
|
|
|
|
if (FAILED(hr) || (S_FALSE == hr))
|
|
{
|
|
pieo->RCRelease();
|
|
pieo = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
_dwVolumeFlags &= ~(HWDVF_STATE_HASAUTOPLAYHANDLER |
|
|
HWDVF_STATE_DONOTSNIFFCONTENT | HWDVF_STATE_JUSTDOCKED);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
if (HWDTS_CDROM == _dwDriveType)
|
|
{
|
|
ASSERT(_fGenericReadRequired);
|
|
|
|
// optimization
|
|
if (_fFirstTime)
|
|
{
|
|
// already updated by _UpdateConstInfo
|
|
_fFirstTime = FALSE;
|
|
}
|
|
else
|
|
{
|
|
_dwMediaCap = 0;
|
|
|
|
hr = _UpdateMMC2CDInfo(hDevice);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dwMediaCap = 0;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _UpdateHasMedia(hDevice);
|
|
|
|
if (SUCCEEDED(hr) && (_dwMediaState & HWDMS_PRESENT))
|
|
{
|
|
if (GetVolumeInformation(_szVolumeGUID, _szLabel,
|
|
ARRAYSIZE(_szLabel), &_dwSerialNumber,
|
|
&_dwMaxFileNameLen, &_dwFileSystemFlags, _szFileSystem,
|
|
ARRAYSIZE(_szFileSystem)))
|
|
{
|
|
// use this?
|
|
// UINT err = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
// Root folder attributes
|
|
_dwRootAttributes = GetFileAttributes(_szVolumeGUID);
|
|
|
|
if (-1 != _dwRootAttributes)
|
|
{
|
|
// File presence
|
|
hr = _UpdateSpecialFilePresence();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (HWDTS_CDROM == _dwDriveType)
|
|
{
|
|
hr = _UpdateTrackInfo(hDevice);
|
|
}
|
|
|
|
if (HWDMC_HASAUTORUNINF & _dwMediaCap)
|
|
{
|
|
hr = _ExtractAutorunIconAndLabel();
|
|
|
|
// not critical
|
|
if (FAILED(hr))
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_HandleAccessDenied();
|
|
_dwState |= STATE_GFAFAILED;
|
|
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
_dwMediaState |= HWDMS_FORMATTED;
|
|
}
|
|
else
|
|
{
|
|
if (ERROR_NOT_READY == GetLastError())
|
|
{
|
|
// We get this for offline cluster volumes. Setting
|
|
// dismounted gets us the right Shell behavior.
|
|
_dwVolumeFlags |= HWDVF_STATE_DISMOUNTED;
|
|
}
|
|
else
|
|
{
|
|
_HandleAccessDenied();
|
|
_dwMediaState &= ~HWDMS_FORMATTED;
|
|
}
|
|
|
|
// To help us debug, even in retail
|
|
_dwState |= STATE_GVIFAILED;
|
|
_dwSerialNumber = GetLastError();
|
|
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dwState |= STATE_UPDATEHASMEDIAFAILED;
|
|
|
|
_dwMediaCap = 0;
|
|
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
|
|
if (S_FALSE == hr)
|
|
{
|
|
// We don't care if they fail
|
|
SafeStrCpyN(_szLabel, TEXT("Invalid"), ARRAYSIZE(_szLabel));
|
|
SafeStrCpyN(_szFileSystem, TEXT("Invalid"), ARRAYSIZE(_szFileSystem));
|
|
_dwRootAttributes = 0xBADBADBD;
|
|
_dwMaxFileNameLen = 0xBADBADBD;
|
|
_dwFileSystemFlags = 0xBADBADBD;
|
|
}
|
|
|
|
if (fGetYourOwnHandle)
|
|
{
|
|
_CloseDeviceHandleSafe(hDevice);
|
|
|
|
pieo->RevertToSelf();
|
|
pieo->RCRelease();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
const FEATURE_NUMBER _rgfnInteresting[] =
|
|
{
|
|
// FeatureProfileList needs to remain the first one
|
|
FeatureProfileList, // = 0x0000,
|
|
FeatureCdRead, // = 0x001e,
|
|
FeatureDvdRead, // = 0x001f,
|
|
FeatureRandomWritable, // = 0x0020,
|
|
FeatureIncrementalStreamingWritable, // = 0x0021,
|
|
FeatureFormattable, // = 0x0023,
|
|
FeatureDefectManagement, // = 0x0024,
|
|
FeatureRestrictedOverwrite, // = 0x0026,
|
|
FeatureCdTrackAtOnce, // = 0x002d,
|
|
FeatureCdMastering, // = 0x002e,
|
|
FeatureDvdRecordableWrite, // = 0x002f,
|
|
FeatureCDAudioAnalogPlay, // = 0x0103,
|
|
};
|
|
|
|
struct CAPABILITYFEATURESMAP
|
|
{
|
|
DWORD dwCapability;
|
|
DWORD dwCapabilityDependent;
|
|
const FEATURE_NUMBER* prgFeature;
|
|
DWORD cFeature;
|
|
};
|
|
|
|
const FEATURE_NUMBER rgfnWRITECAP[] =
|
|
{
|
|
FeatureProfileList,
|
|
};
|
|
|
|
const FEATURE_NUMBER rgfnCDROM[] =
|
|
{
|
|
FeatureCdRead,
|
|
};
|
|
|
|
const FEATURE_NUMBER rgfnCDRECORDABLE[] =
|
|
{
|
|
FeatureIncrementalStreamingWritable,
|
|
FeatureCdTrackAtOnce,
|
|
FeatureCdMastering,
|
|
};
|
|
|
|
const FEATURE_NUMBER rgfnCDREWRITABLE[] =
|
|
{
|
|
FeatureFormattable,
|
|
};
|
|
|
|
const FEATURE_NUMBER rgfnDVDROM[] =
|
|
{
|
|
FeatureDvdRead,
|
|
};
|
|
|
|
const FEATURE_NUMBER rgfnDVDRECORDABLE[] =
|
|
{
|
|
FeatureDvdRecordableWrite,
|
|
};
|
|
|
|
const FEATURE_NUMBER rgfnDVDREWRITABLE[] =
|
|
{
|
|
FeatureFormattable,
|
|
};
|
|
|
|
const FEATURE_NUMBER rgfnDVDRAM[] =
|
|
{
|
|
FeatureRandomWritable,
|
|
FeatureDefectManagement,
|
|
};
|
|
|
|
const FEATURE_NUMBER rgfnANALOGAUDIOOUT[] =
|
|
{
|
|
FeatureCDAudioAnalogPlay,
|
|
};
|
|
|
|
const CAPABILITYFEATURESMAP _rgcapfeaturemap[] =
|
|
{
|
|
{
|
|
HWDMC_WRITECAPABILITY_SUPPORTDETECTION,
|
|
0,
|
|
rgfnWRITECAP,
|
|
ARRAYSIZE(rgfnWRITECAP),
|
|
},
|
|
{
|
|
HWDMC_CDROM,
|
|
HWDMC_WRITECAPABILITY_SUPPORTDETECTION,
|
|
rgfnCDROM,
|
|
ARRAYSIZE(rgfnCDROM),
|
|
},
|
|
{
|
|
HWDMC_CDRECORDABLE,
|
|
HWDMC_WRITECAPABILITY_SUPPORTDETECTION,
|
|
rgfnCDRECORDABLE,
|
|
ARRAYSIZE(rgfnCDRECORDABLE),
|
|
},
|
|
{
|
|
HWDMC_CDREWRITABLE,
|
|
HWDMC_CDRECORDABLE,
|
|
rgfnCDREWRITABLE,
|
|
ARRAYSIZE(rgfnCDREWRITABLE),
|
|
},
|
|
{
|
|
HWDMC_DVDROM,
|
|
HWDMC_WRITECAPABILITY_SUPPORTDETECTION,
|
|
rgfnDVDROM,
|
|
ARRAYSIZE(rgfnDVDROM),
|
|
},
|
|
{
|
|
HWDMC_DVDRECORDABLE,
|
|
HWDMC_WRITECAPABILITY_SUPPORTDETECTION,
|
|
rgfnDVDRECORDABLE,
|
|
ARRAYSIZE(rgfnDVDRECORDABLE),
|
|
},
|
|
{
|
|
HWDMC_DVDREWRITABLE,
|
|
HWDMC_DVDRECORDABLE,
|
|
rgfnDVDREWRITABLE,
|
|
ARRAYSIZE(rgfnDVDREWRITABLE),
|
|
},
|
|
{
|
|
HWDMC_DVDRAM,
|
|
HWDMC_DVDROM,
|
|
rgfnDVDRAM,
|
|
ARRAYSIZE(rgfnDVDRAM),
|
|
},
|
|
{
|
|
HWDMC_ANALOGAUDIOOUT,
|
|
HWDMC_WRITECAPABILITY_SUPPORTDETECTION,
|
|
rgfnANALOGAUDIOOUT,
|
|
ARRAYSIZE(rgfnANALOGAUDIOOUT),
|
|
},
|
|
};
|
|
|
|
#define MMC2_NOTSUPPORTED 0
|
|
#define MMC2_DRIVESUPPORTED 1
|
|
#define MMC2_MEDIASUPPORTED 2
|
|
|
|
HRESULT CVolume::_FillMMC2CD(HANDLE hDevice)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (!_prgMMC2Features)
|
|
{
|
|
_prgMMC2Features = (DWORD*)LocalAlloc(LPTR,
|
|
ARRAYSIZE(_rgfnInteresting) * sizeof(DWORD));
|
|
}
|
|
|
|
if (_prgMMC2Features)
|
|
{
|
|
DWORD cbHeader = sizeof(GET_CONFIGURATION_HEADER) +
|
|
sizeof(FEATURE_HEADER);
|
|
GET_CONFIGURATION_HEADER* pheader = (GET_CONFIGURATION_HEADER*)
|
|
LocalAlloc(LPTR, cbHeader);
|
|
|
|
if (pheader)
|
|
{
|
|
GET_CONFIGURATION_IOCTL_INPUT gcii;
|
|
|
|
gcii.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE;
|
|
gcii.Reserved[0] = NULL;
|
|
gcii.Reserved[1] = NULL;
|
|
|
|
for (DWORD dw = 0; dw < ARRAYSIZE(_rgfnInteresting); ++dw)
|
|
{
|
|
FEATURE_HEADER* pfh;
|
|
|
|
DWORD cbReturned;
|
|
gcii.Feature = _rgfnInteresting[dw];
|
|
|
|
_prgMMC2Features[dw] = MMC2_NOTSUPPORTED;
|
|
|
|
// Requires GENERIC_READ access on the handle
|
|
if (DeviceIoControl(hDevice, IOCTL_CDROM_GET_CONFIGURATION,
|
|
&gcii, sizeof(GET_CONFIGURATION_IOCTL_INPUT), pheader,
|
|
cbHeader, &cbReturned, NULL))
|
|
{
|
|
pfh = (FEATURE_HEADER*)(pheader->Data);
|
|
|
|
WORD w = (pfh->FeatureCode[0]) << 8 | (pfh->FeatureCode[1]);
|
|
|
|
if (w == _rgfnInteresting[dw])
|
|
{
|
|
_prgMMC2Features[dw] = MMC2_DRIVESUPPORTED;
|
|
|
|
if (pfh->Current)
|
|
{
|
|
_prgMMC2Features[dw] |= MMC2_MEDIASUPPORTED;
|
|
}
|
|
else
|
|
{
|
|
_prgMMC2Features[dw] &= ~MMC2_MEDIASUPPORTED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(pheader);
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Rainier drive exposes features which perfectly matches DVD-RAM
|
|
// required feature set. But they are CD-R/RW. For drives
|
|
// we think are DVD-RAM, check if they also expose a DVD_RAM profile.
|
|
// We cannot use profiles all the way because they are not reliable.
|
|
// The same Rainier drive that expose bug 446652 exposes only
|
|
// the CDRewritable profile but it does support CDRecordable and
|
|
// CD-ROM.
|
|
|
|
HRESULT CVolume::_DVDRAMHack(HANDLE hDevice)
|
|
{
|
|
BOOL fSupported = FALSE;
|
|
BOOL fCurrent = FALSE;
|
|
|
|
if (HWDDC_DVDRAM & _dwDriveCap)
|
|
{
|
|
// Do the check
|
|
const DWORD cbHeaderInitial = sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER);
|
|
|
|
DWORD cbReturned;
|
|
DWORD cbHeader = cbHeaderInitial;
|
|
BYTE rgbBuffer[cbHeaderInitial];
|
|
GET_CONFIGURATION_IOCTL_INPUT input;
|
|
GET_CONFIGURATION_HEADER* pheader = (GET_CONFIGURATION_HEADER*)rgbBuffer;
|
|
|
|
ZeroMemory(&input, sizeof(GET_CONFIGURATION_IOCTL_INPUT));
|
|
ZeroMemory(rgbBuffer, sizeof(rgbBuffer));
|
|
|
|
// Ask for the profile list
|
|
input.Feature = FeatureProfileList;
|
|
|
|
// We want only this feature back
|
|
input.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL;
|
|
|
|
BOOL f = DeviceIoControl(hDevice,
|
|
IOCTL_CDROM_GET_CONFIGURATION,
|
|
&input,
|
|
sizeof(GET_CONFIGURATION_IOCTL_INPUT),
|
|
pheader,
|
|
cbHeader,
|
|
&cbReturned,
|
|
NULL);
|
|
|
|
if (f)
|
|
{
|
|
cbHeader = pheader->DataLength[0] << 24 |
|
|
pheader->DataLength[1] << 16 |
|
|
pheader->DataLength[2] << 8 |
|
|
pheader->DataLength[3] << 0;
|
|
|
|
GET_CONFIGURATION_HEADER* pheader2 = (GET_CONFIGURATION_HEADER*)LocalAlloc(LPTR,
|
|
cbHeader);
|
|
|
|
if (pheader2)
|
|
{
|
|
// We want all the profiles back
|
|
input.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL;
|
|
|
|
f = DeviceIoControl(hDevice,
|
|
IOCTL_CDROM_GET_CONFIGURATION,
|
|
&input,
|
|
sizeof(GET_CONFIGURATION_IOCTL_INPUT),
|
|
pheader2,
|
|
cbHeader,
|
|
&cbReturned,
|
|
NULL);
|
|
|
|
if (f)
|
|
{
|
|
FEATURE_DATA_PROFILE_LIST* pproflist =
|
|
(FEATURE_DATA_PROFILE_LIST*)pheader2->Data;
|
|
|
|
for (DWORD dw = 0; dw < (DWORD)(pproflist->Header.AdditionalLength / 4); ++dw)
|
|
{
|
|
FEATURE_DATA_PROFILE_LIST_EX* pprofile = &(pproflist->Profiles[dw]);
|
|
|
|
if (ProfileDvdRam == (pprofile->ProfileNumber[0] << 8 |
|
|
pprofile->ProfileNumber[1] << 0))
|
|
{
|
|
// It does support it! Is it current?
|
|
fSupported = TRUE;
|
|
|
|
if (pprofile->Current)
|
|
{
|
|
fCurrent = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(pheader2);
|
|
}
|
|
}
|
|
|
|
if (fSupported)
|
|
{
|
|
if (!fCurrent)
|
|
{
|
|
_dwMediaCap &= ~HWDMC_DVDRAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dwDriveCap &= ~HWDDC_DVDRAM;
|
|
_dwMediaCap &= ~HWDMC_DVDRAM;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Not worth updating only Const Info vs Media Info, do both
|
|
HRESULT CVolume::_UpdateMMC2CDInfo(HANDLE hDevice)
|
|
{
|
|
HRESULT hr = _FillMMC2CD(hDevice);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
for (DWORD dwCap = 0; dwCap < ARRAYSIZE(_rgcapfeaturemap); ++dwCap)
|
|
{
|
|
DWORD dwGoOn = MMC2_NOTSUPPORTED;
|
|
|
|
if (_rgcapfeaturemap[dwCap].dwCapabilityDependent)
|
|
{
|
|
// This capability is dependent on another one, let's
|
|
// check if the other is on
|
|
if (_dwDriveCap &
|
|
_rgcapfeaturemap[dwCap].dwCapabilityDependent)
|
|
{
|
|
dwGoOn |= MMC2_DRIVESUPPORTED;
|
|
|
|
if (_dwMediaCap &
|
|
_rgcapfeaturemap[dwCap].dwCapabilityDependent)
|
|
{
|
|
dwGoOn |= MMC2_MEDIASUPPORTED;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwGoOn = MMC2_DRIVESUPPORTED | MMC2_MEDIASUPPORTED;
|
|
}
|
|
|
|
for (DWORD dwFeature = 0;
|
|
dwFeature < (_rgcapfeaturemap[dwCap].cFeature);
|
|
++dwFeature)
|
|
{
|
|
for (DWORD dwFeatureOn = 0; (MMC2_DRIVESUPPORTED & dwGoOn) &&
|
|
(dwFeatureOn < ARRAYSIZE(_rgfnInteresting));
|
|
++dwFeatureOn)
|
|
{
|
|
if (_rgfnInteresting[dwFeatureOn] ==
|
|
_rgcapfeaturemap[dwCap].prgFeature[dwFeature])
|
|
{
|
|
if (_prgMMC2Features[dwFeatureOn] &
|
|
MMC2_DRIVESUPPORTED)
|
|
{
|
|
if (!(dwGoOn & MMC2_MEDIASUPPORTED) ||
|
|
!(_prgMMC2Features[dwFeatureOn] &
|
|
MMC2_MEDIASUPPORTED))
|
|
{
|
|
dwGoOn &= ~MMC2_MEDIASUPPORTED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwGoOn = MMC2_NOTSUPPORTED;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (MMC2_DRIVESUPPORTED & dwGoOn)
|
|
{
|
|
_dwDriveCap |= _rgcapfeaturemap[dwCap].dwCapability;
|
|
|
|
if (MMC2_MEDIASUPPORTED & dwGoOn)
|
|
{
|
|
_dwMediaCap |= _rgcapfeaturemap[dwCap].
|
|
dwCapability;
|
|
}
|
|
else
|
|
{
|
|
_dwMediaCap &= ~(_rgcapfeaturemap[dwCap].
|
|
dwCapability);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dwDriveCap &= ~(_rgcapfeaturemap[dwCap].dwCapability);
|
|
_dwMediaCap &= ~(_rgcapfeaturemap[dwCap].dwCapability);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_DVDRAMHack(hDevice);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_UpdateRemovableDevice()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (_fHWDevInstInited)
|
|
{
|
|
BOOL fRemovable;
|
|
|
|
hr = _hwdevinst.IsRemovableDevice(&fRemovable);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (fRemovable)
|
|
{
|
|
_dwDriveCap |= HWDDC_REMOVABLEDEVICE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_UpdateSoftEjectCaps()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (_fHWDevInstInited)
|
|
{
|
|
DWORD dw;
|
|
DWORD dwType;
|
|
|
|
hr = _GetDevicePropertyGeneric(&_hwdevinst, TEXT("NoSoftEject"),
|
|
FALSE, &dwType, (PBYTE)&dw, sizeof(dw));
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
hr = S_OK;
|
|
|
|
if (REG_DWORD == dwType)
|
|
{
|
|
if (1 == dw)
|
|
{
|
|
_dwDriveCap |= HWDDC_NOSOFTEJECT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::_UpdateConstInfo(HANDLE hDevice)
|
|
{
|
|
HRESULT hr;
|
|
|
|
_dwVolumeFlags = 0;
|
|
|
|
if (HWDTS_CDROM == _dwDriveType)
|
|
{
|
|
_dwVolumeFlags = HWDVF_STATE_SUPPORTNOTIFICATION;
|
|
|
|
hr = _UpdateMMC2CDInfo(hDevice);
|
|
}
|
|
else
|
|
{
|
|
if (HWDTS_REMOVABLEDISK == _dwDriveType)
|
|
{
|
|
_dwVolumeFlags = HWDVF_STATE_SUPPORTNOTIFICATION;
|
|
}
|
|
else
|
|
{
|
|
if ((HWDTS_FLOPPY35 != _dwDriveType) &&
|
|
(HWDTS_FLOPPY525 != _dwDriveType))
|
|
{
|
|
// Do something for the 120MB floppy
|
|
_dwVolumeFlags = HWDVF_STATE_SUPPORTNOTIFICATION;
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _GetVolumeName(_pszElemName, _szVolumeGUID,
|
|
ARRAYSIZE(_szVolumeGUID));
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (HWDTS_CDROM != _dwDriveType)
|
|
{
|
|
_dwDriveCap |= HWDMC_WRITECAPABILITY_SUPPORTDETECTION |
|
|
HWDDC_RANDOMWRITE;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::GetVolumeConstInfo(LPWSTR pszVolumeGUID, DWORD cchVolumeGUID,
|
|
DWORD* pdwVolumeFlags, DWORD* pdwDriveType, DWORD* pdwDriveCap)
|
|
{
|
|
HRESULT hr;
|
|
|
|
_cs.Enter();
|
|
|
|
hr = SafeStrCpyN(pszVolumeGUID, _szVolumeGUID, cchVolumeGUID);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!(_dwVolumeFlags & HWDVF_STATE_ACCESSDENIED))
|
|
{
|
|
*pdwVolumeFlags = _dwVolumeFlags;
|
|
*pdwDriveType = _dwDriveType;
|
|
*pdwDriveCap = _dwDriveCap;
|
|
}
|
|
else
|
|
{
|
|
*pdwVolumeFlags = HWDVF_STATE_ACCESSDENIED;
|
|
*pdwDriveType = 0xBADBADBD;
|
|
*pdwDriveCap = 0xBADBADBD;
|
|
}
|
|
}
|
|
|
|
_cs.Leave();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::GetVolumeMediaInfo(LPWSTR pszLabel, DWORD cchLabel,
|
|
LPWSTR pszFileSystem, DWORD cchFileSystem, DWORD* pdwFileSystemFlags,
|
|
DWORD* pdwMaxFileNameLen, DWORD* pdwRootAttributes, DWORD* pdwSerialNumber,
|
|
DWORD* pdwDriveState, DWORD* pdwMediaState, DWORD* pdwMediaCap)
|
|
{
|
|
HRESULT hr;
|
|
|
|
_cs.Enter();
|
|
|
|
if (!(_dwVolumeFlags & HWDVF_STATE_ACCESSDENIED))
|
|
{
|
|
if (_dwMediaState & HWDMS_PRESENT)
|
|
{
|
|
hr = SafeStrCpyN(pszLabel, _szLabel, cchLabel);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SafeStrCpyN(pszFileSystem, _szFileSystem, cchFileSystem);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pdwFileSystemFlags = _dwFileSystemFlags;
|
|
*pdwMaxFileNameLen = _dwMaxFileNameLen;
|
|
*pdwRootAttributes = _dwRootAttributes;
|
|
*pdwSerialNumber = _dwSerialNumber;
|
|
|
|
*pdwDriveState = _dwDriveState;
|
|
|
|
*pdwMediaState = _dwMediaState;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pdwMediaState = _dwMediaState;
|
|
|
|
// We don't care if they fail
|
|
SafeStrCpyN(pszLabel, TEXT("Invalid"), cchLabel);
|
|
SafeStrCpyN(pszFileSystem, TEXT("Invalid"), cchFileSystem);
|
|
*pdwSerialNumber = 0xBADBADBD;
|
|
*pdwMaxFileNameLen = 0xBADBADBD;
|
|
*pdwFileSystemFlags = 0xBADBADBD;
|
|
*pdwRootAttributes = 0xBADBADBD;
|
|
*pdwDriveState = 0xBADBADBD;
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
*pdwMediaCap = _dwMediaCap;
|
|
}
|
|
else
|
|
{
|
|
*pdwMediaState = 0xBADBADBD;
|
|
|
|
// We don't care if they fail
|
|
SafeStrCpyN(pszLabel, TEXT("Access Denied"), cchLabel);
|
|
SafeStrCpyN(pszFileSystem, TEXT("Access Denied"), cchFileSystem);
|
|
*pdwSerialNumber = 0xBADBADBD;
|
|
*pdwMaxFileNameLen = 0xBADBADBD;
|
|
*pdwFileSystemFlags = 0xBADBADBD;
|
|
*pdwRootAttributes = 0xBADBADBD;
|
|
*pdwDriveState = 0xBADBADBD;
|
|
*pdwMediaCap = 0xBADBADBD;
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
_cs.Leave();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::GetIconAndLabelInfo(LPWSTR pszAutorunIconLocation,
|
|
DWORD cchAutorunIconLocation, LPWSTR pszAutorunLabel, DWORD cchAutorunLabel,
|
|
LPWSTR pszIconLocationFromService, DWORD cchIconLocationFromService,
|
|
LPWSTR pszNoMediaIconLocationFromService, DWORD cchNoMediaIconLocationFromService,
|
|
LPWSTR pszLabelFromService, DWORD cchLabelFromService)
|
|
{
|
|
HRESULT hrTmp;
|
|
|
|
*pszAutorunIconLocation = NULL;
|
|
*pszAutorunLabel = NULL;
|
|
*pszIconLocationFromService = NULL;
|
|
*pszNoMediaIconLocationFromService = NULL;
|
|
*pszLabelFromService = NULL;
|
|
|
|
_cs.Enter();
|
|
|
|
if (!(_dwVolumeFlags & HWDVF_STATE_ACCESSDENIED))
|
|
{
|
|
if (_dwMediaCap & HWDMC_HASAUTORUNINF)
|
|
{
|
|
if (_szAutorunIconLocation[0])
|
|
{
|
|
hrTmp = SafeStrCpyN(pszAutorunIconLocation, _szAutorunIconLocation,
|
|
cchAutorunIconLocation);
|
|
|
|
if (FAILED(hrTmp))
|
|
{
|
|
*pszAutorunIconLocation = 0;
|
|
}
|
|
}
|
|
|
|
if (_szAutorunLabel[0])
|
|
{
|
|
hrTmp = SafeStrCpyN(pszAutorunLabel, _szAutorunLabel,
|
|
cchAutorunLabel);
|
|
|
|
if (FAILED(hrTmp))
|
|
{
|
|
*pszAutorunLabel = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_fHWDevInstInited)
|
|
{
|
|
WORD_BLOB* pblob;
|
|
|
|
hrTmp = _GetDevicePropertyGenericAsMultiSz(&_hwdevinst, TEXT("Icons"),
|
|
TRUE, &pblob);
|
|
|
|
if (SUCCEEDED(hrTmp) && (S_FALSE != hrTmp))
|
|
{
|
|
hrTmp = SafeStrCpyN(pszIconLocationFromService, pblob->asData,
|
|
cchIconLocationFromService);
|
|
|
|
if (FAILED(hrTmp))
|
|
{
|
|
*pszIconLocationFromService = 0;
|
|
}
|
|
|
|
CoTaskMemFree(pblob);
|
|
}
|
|
else
|
|
{
|
|
*pszIconLocationFromService = 0;
|
|
hrTmp = S_FALSE;
|
|
}
|
|
|
|
hrTmp = _GetDevicePropertyGenericAsMultiSz(&_hwdevinst, TEXT("NoMediaIcons"),
|
|
TRUE, &pblob);
|
|
|
|
if (SUCCEEDED(hrTmp) && (S_FALSE != hrTmp))
|
|
{
|
|
hrTmp = SafeStrCpyN(pszNoMediaIconLocationFromService, pblob->asData,
|
|
cchNoMediaIconLocationFromService);
|
|
|
|
if (FAILED(hrTmp))
|
|
{
|
|
*pszNoMediaIconLocationFromService = 0;
|
|
}
|
|
|
|
CoTaskMemFree(pblob);
|
|
}
|
|
else
|
|
{
|
|
*pszNoMediaIconLocationFromService = 0;
|
|
hrTmp = S_FALSE;
|
|
}
|
|
|
|
if (SUCCEEDED(hrTmp))
|
|
{
|
|
hrTmp = _GetDevicePropertyAsString(&_hwdevinst, TEXT("Label"),
|
|
pszLabelFromService, cchLabelFromService);
|
|
|
|
if (FAILED(hrTmp) || (S_FALSE == hrTmp))
|
|
{
|
|
*pszLabelFromService = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_cs.Leave();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CVolume::_GetVOLUMEINFO2(VOLUMEINFO2** ppvolinfo2)
|
|
{
|
|
ASSERT(_cs.IsInside());
|
|
|
|
DWORD cchReq;
|
|
|
|
// We allocate this buffer just to be stack friendly, otherwise it could
|
|
// have been on the stack
|
|
VOLUMEINFO2* pvolinfo2;
|
|
const DWORD cbvolinfo2 = MAX_VOLUMEINFO2;
|
|
|
|
HRESULT hr = _AllocMemoryChunk<VOLUMEINFO2*>(cbvolinfo2, &pvolinfo2);
|
|
|
|
*ppvolinfo2 = NULL;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pvolinfo2->cbSize = MAX_VOLUMEINFO2;
|
|
|
|
hr = GetName(pvolinfo2->szDeviceIDVolume,
|
|
ARRAYSIZE(pvolinfo2->szDeviceIDVolume), &cchReq);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Const info
|
|
hr = SafeStrCpyN(pvolinfo2->szVolumeGUID, _szVolumeGUID,
|
|
ARRAYSIZE(pvolinfo2->szVolumeGUID));
|
|
|
|
if (_dwVolumeFlags & HWDVF_STATE_ACCESSDENIED)
|
|
{
|
|
pvolinfo2->dwVolumeFlags = HWDVF_STATE_ACCESSDENIED;
|
|
}
|
|
else
|
|
{
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pvolinfo2->dwVolumeFlags = _dwVolumeFlags;
|
|
pvolinfo2->dwDriveType = _dwDriveType;
|
|
pvolinfo2->dwDriveCapability = _dwDriveCap;
|
|
}
|
|
|
|
pvolinfo2->dwState = _dwState;
|
|
|
|
// Media info
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// This fct should be called from within the Volume critsect
|
|
if (_dwMediaState & HWDMS_PRESENT)
|
|
{
|
|
hr = SafeStrCpyN(pvolinfo2->szLabel, _szLabel,
|
|
ARRAYSIZE(pvolinfo2->szLabel));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SafeStrCpyN(pvolinfo2->szFileSystem, _szFileSystem,
|
|
ARRAYSIZE(pvolinfo2->szFileSystem));
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pvolinfo2->dwFileSystemFlags = _dwFileSystemFlags;
|
|
pvolinfo2->dwMaxFileNameLen = _dwMaxFileNameLen;
|
|
pvolinfo2->dwRootAttributes = _dwRootAttributes;
|
|
pvolinfo2->dwSerialNumber = _dwSerialNumber;
|
|
|
|
pvolinfo2->dwDriveState = _dwDriveState;
|
|
pvolinfo2->dwMediaCap = _dwMediaCap;
|
|
|
|
pvolinfo2->dwMediaState = _dwMediaState;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pvolinfo2->dwMediaState = _dwMediaState;
|
|
|
|
// We don't care if they fail
|
|
SafeStrCpyN(pvolinfo2->szLabel, TEXT("Invalid"),
|
|
ARRAYSIZE(pvolinfo2->szLabel));
|
|
SafeStrCpyN(pvolinfo2->szFileSystem, TEXT("Invalid"),
|
|
ARRAYSIZE(pvolinfo2->szFileSystem));
|
|
pvolinfo2->dwSerialNumber = 0xBADBADBD;
|
|
pvolinfo2->dwMaxFileNameLen = 0xBADBADBD;
|
|
pvolinfo2->dwFileSystemFlags = 0xBADBADBD;
|
|
pvolinfo2->dwRootAttributes = 0xBADBADBD;
|
|
pvolinfo2->dwDriveState = 0xBADBADBD;
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
pvolinfo2->dwMediaCap = _dwMediaCap;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR szAutorunIconLocation[MAX_ICONLOCATION];
|
|
WCHAR szAutorunLabel[MAX_LABEL];
|
|
WCHAR szIconLocationFromService[MAX_ICONLOCATION];
|
|
WCHAR szNoMediaIconLocationFromService[MAX_ICONLOCATION];
|
|
// We can now have a @%SystemRoot%\system32\shell32.dll,-1785 for MUI stuff
|
|
WCHAR szLabelFromService[MAX_ICONLOCATION];
|
|
|
|
pvolinfo2->oAutorunIconLocation = INVALID_DWORD;
|
|
pvolinfo2->oAutorunLabel = INVALID_DWORD;
|
|
pvolinfo2->oIconLocationFromService = INVALID_DWORD;
|
|
pvolinfo2->oNoMediaIconLocationFromService = INVALID_DWORD;
|
|
pvolinfo2->oLabelFromService = INVALID_DWORD;
|
|
|
|
hr = GetIconAndLabelInfo(szAutorunIconLocation,
|
|
ARRAYSIZE(szAutorunIconLocation), szAutorunLabel,
|
|
ARRAYSIZE(szAutorunLabel), szIconLocationFromService,
|
|
ARRAYSIZE(szIconLocationFromService), szNoMediaIconLocationFromService,
|
|
ARRAYSIZE(szNoMediaIconLocationFromService), szLabelFromService,
|
|
ARRAYSIZE(szLabelFromService));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPWSTR pszNext = pvolinfo2->szOptionalStrings;
|
|
size_t cchLeft = (cbvolinfo2 - sizeof(*pvolinfo2) +
|
|
sizeof(pvolinfo2->szOptionalStrings)) / sizeof(WCHAR);
|
|
size_t cchLeftBeginWith = cchLeft;
|
|
|
|
// The following five strings are optional
|
|
if (szAutorunIconLocation[0])
|
|
{
|
|
pvolinfo2->oAutorunIconLocation = (DWORD)(cchLeftBeginWith - cchLeft);
|
|
|
|
hr = StringCchCopyEx(pszNext, cchLeft, szAutorunIconLocation, &pszNext, &cchLeft, 0);
|
|
|
|
++pszNext;
|
|
--cchLeft;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && szAutorunLabel[0])
|
|
{
|
|
pvolinfo2->oAutorunLabel = (DWORD)(cchLeftBeginWith - cchLeft);
|
|
|
|
hr = StringCchCopyEx(pszNext, cchLeft, szAutorunLabel, &pszNext, &cchLeft, 0);
|
|
|
|
++pszNext;
|
|
--cchLeft;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && szIconLocationFromService[0])
|
|
{
|
|
pvolinfo2->oIconLocationFromService = (DWORD)(cchLeftBeginWith - cchLeft);
|
|
|
|
hr = StringCchCopyEx(pszNext, cchLeft, szIconLocationFromService, &pszNext, &cchLeft, 0);
|
|
|
|
++pszNext;
|
|
--cchLeft;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && szNoMediaIconLocationFromService[0])
|
|
{
|
|
pvolinfo2->oNoMediaIconLocationFromService = (DWORD)(cchLeftBeginWith - cchLeft);
|
|
|
|
hr = StringCchCopyEx(pszNext, cchLeft, szNoMediaIconLocationFromService, &pszNext, &cchLeft, 0);
|
|
|
|
++pszNext;
|
|
--cchLeft;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && szLabelFromService[0])
|
|
{
|
|
pvolinfo2->oLabelFromService = (DWORD)(cchLeftBeginWith - cchLeft);
|
|
|
|
hr = StringCchCopy(pszNext, cchLeft, szLabelFromService);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppvolinfo2 = pvolinfo2;
|
|
}
|
|
else
|
|
{
|
|
_FreeMemoryChunk<VOLUMEINFO2*>(pvolinfo2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::HandleRemoval()
|
|
{
|
|
WCHAR szDeviceIDVolume[MAX_DEVICEID];
|
|
DWORD cchReq;
|
|
|
|
HRESULT hr = GetName(szDeviceIDVolume, ARRAYSIZE(szDeviceIDVolume), &cchReq);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// This will launch a thread
|
|
CHardwareDevicesImpl::_AdviseVolumeRemoved(szDeviceIDVolume,
|
|
_pszMountPoints, _cchMountPoints);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVolume::HandleArrival()
|
|
{
|
|
BOOL fJustDocked = FALSE;
|
|
|
|
HRESULT hr = CHWEventDetectorHelper::DockStateChanged(&fJustDocked);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// ISSUE-2001/01/13-StephStm Pass the Docking change in there so
|
|
// that the Shell knows if it should
|
|
// Autorun or not.
|
|
//
|
|
if (_fHWDevInstInited)
|
|
{
|
|
BOOL fTryAutoplay = TRUE;
|
|
|
|
if ((_dwDriveType & HWDTS_CDROM) ||
|
|
(_dwDriveType & HWDTS_REMOVABLEDISK))
|
|
{
|
|
if (_dwMediaState & HWDMS_PRESENT)
|
|
{
|
|
// Not removable disk, but device
|
|
BOOL fRemovable;
|
|
|
|
hr = _hwdevinst.IsRemovableDevice(&fRemovable);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (fRemovable)
|
|
{
|
|
// We need to Autoplay these since cameras
|
|
// are removable-disk devices. And we want
|
|
// to autorun them when they come in.
|
|
fTryAutoplay = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// For removable-disk drives/CD drives with
|
|
// media inserted when they are plugged,
|
|
// we do not Autoplay.
|
|
fTryAutoplay = FALSE;
|
|
|
|
DIAGNOSTIC((TEXT("[0311]Non removable device plugged with media in it -> No Autoplay!")));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!fJustDocked && fTryAutoplay)
|
|
{
|
|
HANDLE hDevice;
|
|
|
|
// Get a handle to regsiter for notification so that the
|
|
// FindFirstFile does not veto a PnP/Driver transition
|
|
hr = _GetDeviceHandleSafe(&hDevice, FALSE);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr))
|
|
{
|
|
hr = _ShouldTryAutoplay(&fTryAutoplay);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (fTryAutoplay)
|
|
{
|
|
BOOL fHasHandler;
|
|
|
|
hr = CHWEventDetectorImpl::HandleVolumeMediaEvent(
|
|
_pszElemName, &_hwdevinst, TEXT("DeviceArrival"),
|
|
&fHasHandler);
|
|
|
|
if (SUCCEEDED(hr) && fHasHandler)
|
|
{
|
|
_dwVolumeFlags |= HWDVF_STATE_HASAUTOPLAYHANDLER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dwVolumeFlags |= HWDVF_STATE_DONOTSNIFFCONTENT;
|
|
}
|
|
}
|
|
|
|
_CloseDeviceHandleSafe(hDevice);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fJustDocked)
|
|
{
|
|
DIAGNOSTIC((TEXT("[0301]Just docked -> No Autoplay!")));
|
|
TRACE(TF_VOLUME, TEXT("Just docked -> No Autoplay!"));
|
|
|
|
_dwVolumeFlags |= HWDVF_STATE_JUSTDOCKED;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DIAGNOSTIC((TEXT("[0310]Cannot find hardware device for this volume -> No Autoplay!")));
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_AdviseVolumeChangeHelper(TRUE);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
CVolume::CVolume() : _dwMediaState(0),
|
|
_devtype((DEVICE_TYPE)-1), _ulDeviceNumber((ULONG)-1),
|
|
_ulPartitionNumber((ULONG)-1), _fHWDevInstInited(FALSE), _dwDriveType(0),
|
|
_dwDriveCap(0), _dwVolumeFlags(0), _prgMMC2Features(NULL),
|
|
_fFirstTime(TRUE), _dwMediaCap(0), _pszMountPoints(NULL), _dwDriveState(0),
|
|
_hdevnotify(NULL), _dwState(0),
|
|
_dwMediaPresentFromEvent(MPFE_UNDETERMINED)
|
|
{
|
|
_szVolumeGUID[0] = 0;
|
|
_szDeviceIDDisk[0] = 0;
|
|
}
|
|
|
|
CVolume::~CVolume()
|
|
{
|
|
_UnregisterNotif();
|
|
|
|
if (_pszMountPoints)
|
|
{
|
|
CNamedElemList* pnel;
|
|
HRESULT hres = CHWEventDetectorHelper::GetList(HWEDLIST_MTPT, &pnel);
|
|
|
|
if (S_OK == hres)
|
|
{
|
|
for (LPWSTR psz = _pszMountPoints; *psz; psz += (lstrlen(psz) + 1))
|
|
{
|
|
hres = pnel->Remove(psz);
|
|
}
|
|
|
|
pnel->RCRelease();
|
|
}
|
|
|
|
LocalFree(_pszMountPoints);
|
|
}
|
|
|
|
if (_prgMMC2Features)
|
|
{
|
|
LocalFree(_prgMMC2Features);
|
|
}
|
|
|
|
_cs.Delete();
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//static
|
|
HRESULT CVolume::Create(CNamedElem** ppelem)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
*ppelem = new CVolume();
|
|
|
|
if (!(*ppelem))
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
//static
|
|
HRESULT CVolume::GetFillEnum(CFillEnum** ppfillenum)
|
|
{
|
|
HRESULT hres;
|
|
|
|
CVolumeFillEnum* pfillenum = new CVolumeFillEnum();
|
|
|
|
if (pfillenum)
|
|
{
|
|
hres = pfillenum->_Init();
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
delete pfillenum;
|
|
pfillenum = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
|
|
*ppfillenum = pfillenum;
|
|
|
|
return hres;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
HRESULT CVolumeFillEnum::Next(LPWSTR pszElemName, DWORD cchElemName,
|
|
DWORD* pcchRequired)
|
|
{
|
|
return _intffillenum.Next(pszElemName, cchElemName, pcchRequired);
|
|
}
|
|
|
|
HRESULT CVolumeFillEnum::_Init()
|
|
{
|
|
return _intffillenum._Init(&guidVolumeClass, NULL);
|
|
}
|