2010-10-31 272 views
8

我想將彈出命令發送到由其VID和PID標識的特定USB設備。我可以使用SetupDiEnumDeviceInfo()和SetupDiGetDeviceRegistryProperty()找到設備,並匹配HARDWAREID字符串中的VID/PID號碼,但這是我所知道的。根據其VID/PID查找並彈出USB設備

我有一個SP_DEVINFO_DATA結構和一個HDEVINFO句柄。我如何將這些與驅動器號或卷路徑相關聯,以便我可以發送一個彈出命令?

+2

知道爲什麼這是downvoted兩次會是有趣的。 – 2010-10-31 22:33:19

+0

這是爲什麼被拒絕? – 2010-10-31 22:33:54

回答

8

嗯,我想通了。 Luke鏈接的CodeProject文章展示瞭如何將驅動器盤符與設備接口相匹配,因此我會對該答案進行+1,但並不能解決整個問題。

我需要弄清楚如何找到我的USB設備的設備實例,並找到一種方法將其與設備接口相匹配。 CM_Locate_DevNode()和CM_Get_Child()函數是這個關鍵。最後,我可以使用IOCTL彈出設備。

我正在處理的設備是一個USB CD-ROM驅動器,這就是爲什麼我已將設備類型硬編碼到CDROM。我無法相信需要多少代碼才能完成我認爲是相當直接的任務(我引用了我的客戶2個小時來編寫代碼,這讓我花了四天時間才弄清楚了這一切!)。下面是最終的工作代碼,希望能夠幫助你省去你們之中的一個人,因爲我只能通過同樣的地獄:

#include <SetupAPI.h> 
#include <cfgmgr32.h> 
#include <winioctl.h> 

// Finds the device interface for the CDROM drive with the given interface number. 
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber) 
{ 
    const GUID *guid = &GUID_DEVINTERFACE_CDROM; 

// Get device interface info set handle 
// for all devices attached to system 
    HDEVINFO hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 
    if(hDevInfo == INVALID_HANDLE_VALUE) 
     return 0; 

// Retrieve a context structure for a device interface of a device information set. 
    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); 

// Iterate through all the interfaces and try to match one based on 
// the device number. 
    for(DWORD i = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL,guid, i, &spdid); i++) 
    { 
    // Get the device path. 
     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; 

    // Open the device. 
     HANDLE hDrive = CreateFile(pspdidd->DevicePath,0, 
            FILE_SHARE_READ | FILE_SHARE_WRITE, 
            NULL, OPEN_EXISTING, 0, NULL); 
     if(hDrive == INVALID_HANDLE_VALUE) 
      continue; 

    // Get the device number. 
     STORAGE_DEVICE_NUMBER sdn; 
     dwSize = 0; 
     if(DeviceIoControl(hDrive, 
          IOCTL_STORAGE_GET_DEVICE_NUMBER, 
          NULL, 0, &sdn, sizeof(sdn), 
          &dwSize, NULL)) 
     { 
     // Does it match? 
      if(DeviceNumber == (long)sdn.DeviceNumber) 
      { 
       CloseHandle(hDrive); 
       SetupDiDestroyDeviceInfoList(hDevInfo); 
       return spdd.DevInst; 
      } 
     } 
     CloseHandle(hDrive); 
    } 

    SetupDiDestroyDeviceInfoList(hDevInfo); 
    return 0; 
} 


// Returns true if the given device instance belongs to the USB device with the given VID and PID. 
bool matchDevInstToUsbDevice(DEVINST device, DWORD vid, DWORD pid) 
{ 
// This is the string we will be searching for in the device harware IDs. 
    TCHAR hwid[64]; 
    _stprintf(hwid, _T("VID_%04X&PID_%04X"), vid, pid); 

// Get a list of hardware IDs for all USB devices. 
    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); 

// Iterate through the list looking for our ID. 
    for(LPTSTR pszDeviceID = pszBuffer; *pszDeviceID; pszDeviceID += _tcslen(pszDeviceID) + 1) 
    { 
    // Some versions of Windows have the string in upper case and other versions have it 
    // in lower case so just make it all upper. 
     for(int i = 0; pszDeviceID[i]; i++) 
      pszDeviceID[i] = toupper(pszDeviceID[i]); 

     if(_tcsstr(pszDeviceID, hwid)) 
     { 
     // Found the device, now we want the grandchild device, which is the "generic volume" 
      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)) 
       { 
       // Now compare the grandchild node against the given device instance. 
        if(device == DiskDriveInst) 
         return true; 
       } 
      } 
     } 
    } 

    return false; 
} 

// Eject the given drive. 
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); 
} 

// Find a USB device by it's Vendor and Product IDs. When found, eject it. 
void usbEjectDevice(unsigned vid, unsigned pid) 
{ 
    TCHAR devicepath[8]; 
    _tcscpy(devicepath, _T("\\\\.\\?:")); 

    TCHAR drivepath[4]; 
    _tcscpy(drivepath, _T("?:\\")); 

// Iterate through every drive letter and check if it is our device. 
    for(TCHAR driveletter = _T('A'); driveletter <= _T('Z'); driveletter++) 
    { 
    // We are only interested in CDROM drives. 
     drivepath[0] = driveletter; 
     if(DRIVE_CDROM != GetDriveType(drivepath)) 
      continue; 

    // Get the "storage device number" for the current drive. 
     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; 

    // Use the data we have collected so far on our drive to find a device instance. 
     DEVINST DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber); 

    // If the device instance corresponds to the USB device we are looking for, eject it. 
     if(DevInst) 
     { 
      if(matchDevInstToUsbDevice(DevInst, vid, pid)) 
       ejectDrive(driveletter); 
     } 
    } 
}