好吧,我想出来了。由Luke链接到的CodeProject文章展示了如何将驱动器号与设备接口相匹配,这个接口只有一半,所以我将+1这个答案,但它并不能解决整个问题。
我需要找出如何找到我的USB设备的设备实例,并找到一种方法来匹配设备接口。CM_Locate_DevNode()和CM_Get_Child()函数是实现这一点的关键。最后我可以使用IOCTL来弹出设备。
我正在处理的设备是一个USB CD-ROM驱动器,这就是为什么我将设备类型硬编码为CDROM的原因。我真不敢相信,要完成我认为相当简单的任务,需要多少代码(我引用了我的客户2个小时来编写这段代码,我花了4天时间才弄清楚!)。这是最终的工作代码,希望能让你们中的一个免于和我一样的痛苦:
#include <SetupAPI.h>
#include <cfgmgr32.h>
#include <winioctl.h>
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber)
{
const GUID *guid = &GUID_DEVINTERFACE_CDROM;
HDEVINFO hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if(hDevInfo == INVALID_HANDLE_VALUE)
return 0;
BYTE buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)buf;
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
DWORD dwSize;
spdid.cbSize = sizeof(spdid);
for(DWORD i = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL,guid, i, &spdid); i++)
{
dwSize = 0;
SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &dwSize, NULL);
if(dwSize == 0 || dwSize > sizeof(buf))
continue;
pspdidd->cbSize = sizeof(*pspdidd);
ZeroMemory((PVOID)&spdd, sizeof(spdd));
spdd.cbSize = sizeof(spdd);
if(!SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, pspdidd,
dwSize, &dwSize, &spdd))
continue;
HANDLE hDrive = CreateFile(pspdidd->DevicePath,0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if(hDrive == INVALID_HANDLE_VALUE)
continue;
STORAGE_DEVICE_NUMBER sdn;
dwSize = 0;
if(DeviceIoControl(hDrive,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL, 0, &sdn, sizeof(sdn),
&dwSize, NULL))
{
if(DeviceNumber == (long)sdn.DeviceNumber)
{
CloseHandle(hDrive);
SetupDiDestroyDeviceInfoList(hDevInfo);
return spdd.DevInst;
}
}
CloseHandle(hDrive);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return 0;
}
bool matchDevInstToUsbDevice(DEVINST device, DWORD vid, DWORD pid)
{
TCHAR hwid[64];
_stprintf(hwid, _T("VID_%04X&PID_%04X"), vid, pid);
ULONG ulLen;
CM_Get_Device_ID_List_Size(&ulLen, NULL, CM_GETIDLIST_FILTER_NONE);
TCHAR *pszBuffer = new TCHAR[ulLen];
CM_Get_Device_ID_List(NULL, pszBuffer, ulLen, CM_GETIDLIST_FILTER_NONE);
for(LPTSTR pszDeviceID = pszBuffer; *pszDeviceID; pszDeviceID += _tcslen(pszDeviceID) + 1)
{
for(int i = 0; pszDeviceID[i]; i++)
pszDeviceID[i] = toupper(pszDeviceID[i]);
if(_tcsstr(pszDeviceID, hwid))
{
DEVINST MSDInst = 0;
if(CR_SUCCESS == CM_Locate_DevNode(&MSDInst, pszDeviceID, CM_LOCATE_DEVNODE_NORMAL))
{
DEVINST DiskDriveInst = 0;
if(CR_SUCCESS == CM_Get_Child(&DiskDriveInst, MSDInst, 0))
{
if(device == DiskDriveInst)
return true;
}
}
}
}
return false;
}
void ejectDrive(TCHAR driveletter)
{
TCHAR devicepath[16];
_tcscpy(devicepath, _T("\\\\.\\?:"));
devicepath[4] = driveletter;
DWORD dwRet = 0;
HANDLE hVol = CreateFile(devicepath, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(hVol == INVALID_HANDLE_VALUE)
return;
if(!DeviceIoControl(hVol, FSCTL_LOCK_VOLUME, 0, 0, 0, 0, &dwRet, 0))
return;
if(!DeviceIoControl(hVol, FSCTL_DISMOUNT_VOLUME, 0, 0, 0, 0, &dwRet, 0))
return;
DeviceIoControl(hVol, IOCTL_STORAGE_EJECT_MEDIA, 0, 0, 0, 0, &dwRet, 0);
CloseHandle(hVol);
}
void usbEjectDevice(unsigned vid, unsigned pid)
{
TCHAR devicepath[8];
_tcscpy(devicepath, _T("\\\\.\\?:"));
TCHAR drivepath[4];
_tcscpy(drivepath, _T("?:\\"));
for(TCHAR driveletter = _T('A'); driveletter <= _T('Z'); driveletter++)
{
drivepath[0] = driveletter;
if(DRIVE_CDROM != GetDriveType(drivepath))
continue;
long DeviceNumber = -1;
devicepath[4] = driveletter;
HANDLE hVolume = CreateFile(devicepath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if(INVALID_HANDLE_VALUE == hVolume)
continue;
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
if(DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL))
DeviceNumber = sdn.DeviceNumber;
CloseHandle(hVolume);
if(DeviceNumber < 0)
continue;
DEVINST DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber);
if(DevInst)
{
if(matchDevInstToUsbDevice(DevInst, vid, pid))
ejectDrive(driveletter);
}
}
}