根据其VID/PID查找并弹出USB设备 [英] Find and eject a USB device based on its VID/PID

查看:120
本文介绍了根据其VID/PID查找并弹出USB设备的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想向其USB设备的VID和PID标识的特定USB设备发送弹出命令.我可以使用SetupDiEnumDeviceInfo()和SetupDiGetDeviceRegistryProperty()并匹配HARDWAREID字符串中的VID/PID号来找到设备,但是据我所知.

I want to send an eject command to a specific USB device identified by it's VID and PID. I can find the device by using SetupDiEnumDeviceInfo() and SetupDiGetDeviceRegistryProperty() and matching the VID/PID numbers in the HARDWAREID string but that's as far as I've got.

我有一个SP_DEVINFO_DATA结构和一个HDEVINFO句柄.我如何将它们与驱动器号或卷路径相关联,以便可以向其发送弹出命令?

I have a SP_DEVINFO_DATA struct and a HDEVINFO handle. How would I relate these to a drive letter or volume path so I can send it an eject command?

推荐答案

好吧,我知道了.卢克(Luke)链接到的CodeProject文章展示了如何将驱动器号与设备接口相匹配,而该接口仅是设备接口的一半,因此我将+1这个答案,但不能解决整个问题.

Well, I figured it out. The CodeProject article linked to by Luke shows how to match the drive letter to a device interface which is half the way there so I'll +1 that answer but it doesn't solve the whole problem.

我需要弄清楚如何为我的USB设备查找设备实例,以及找到一种将其与设备接口进行匹配的方法.CM_Locate_DevNode()和CM_Get_Child()函数是实现此目的的关键.最后,我可以使用IOCTL弹出设备.

I needed to figure out how to find the device instance for my USB device and find a way to match that to the device interface. The CM_Locate_DevNode() and CM_Get_Child() functions were the key to this. Finally I can use an IOCTL to eject the device.

我要处理的设备是USB CD-ROM驱动器,这就是为什么我将设备类型硬编码为CDROM的原因.我不敢相信要完成我认为相当简单的任务需要多少代码(我引用我的客户2个小时来编写此代码,花了我四天的时间才能解决!).这是最终的工作代码,希望能使你们中的一个摆脱与我刚遇到的同样的麻烦:

The device I am dealing with is a USB CD-ROM drive which is why I have hard-coded the device type to CDROM. I can't believe how much code is required to do what I thought would be a fairly straightforward task (I quoted my client 2 hours to write this code, it's taken me four days to figure it all out!). Here's the final working code which will hopefully save one of you out there from going through the same hell as I just have:

#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);
        }   
    }
}

这篇关于根据其VID/PID查找并弹出USB设备的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆