【问题标题】:Cannot determine the drive of my USB Device无法确定我的 USB 设备的驱动器
【发布时间】:2020-04-21 10:32:15
【问题描述】:

我正在编写一个程序来检测何时插入 USB 笔式驱动器。在插入 USB 笔式驱动器时,它被检测为 DBT_DEVTYP_DEVICEINTERFACE 而不是 DBT_DEVTYP_VOLUME。所以我无法获取卷驱动器号(A、B、C、D、...)我该如何解决这个问题,即使其可检测为 DBT_DEVTYP_VOLUME 或以其他方式获取驱动器号。

    LRESULT message_handler(HWND__* hwnd, UINT uint, WPARAM wparam, LPARAM lparam)
    {
       switch (uint)
       {
case WM_NCCREATE: // before window creation
        return true;
        break;

    case WM_CREATE: // the actual creation of the window
    {
        // you can get your creation params here..like GUID..
        LPCREATESTRUCT params = (LPCREATESTRUCT)lparam;
        GUID InterfaceClassGuid = *((GUID*)params->lpCreateParams);
        DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
        ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
        NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
        NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
        for (int i = 0; i < sizeof(GuidDevInterfaceList); i++) {
            NotificationFilter.dbcc_classguid = GuidDevInterfaceList[i];

            HDEVNOTIFY dev_notify = RegisterDeviceNotification(hwnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
            if (dev_notify == NULL) {     // Handle the error by returning correct error in LRESULT format and remove throw...   
                throw std::runtime_error("Could not register for device Notifications!");
            }
        }

    }
    break;

        case WM_DEVICECHANGE:
        {

            PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lparam;   

                                        //DBT_DEVTYP_VOLUME
            if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
            {

                switch (wparam)
                {

                case DBT_DEVICEARRIVAL: 
                {                   
                    cout<<"Device Arrived"<<endl;

                }
    }}}}}

检测到有设备插入

输出:

Device Arrived
Failed

【问题讨论】:

  • dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE 所以当然不能是DBT_DEVTYP_VOLUME
  • 这就是问题所在。我的随身碟被检测为 DBT_DEVTYP_DEVICEINTERFACE 而不是 DBT_DEVTYP_VOLUME。
  • 那你为什么要对不可能的事情进行测试,并期望它起作用?你想做什么?文档是怎么说的?
  • 我已经更正了这个问题..
  • 当你在if (lpdb-&gt;dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) 里面检查(lpdb-&gt;dbch_devicetype == DBT_DEVTYP_VOLUME) 这当然是个问题。在代码逻辑中。你真的需要RegisterDeviceNotificationGUID_DEVINTERFACE_VOLUME 注册(你如何注册不可见)

标签: c++ winapi


【解决方案1】:

确保您使用GUID_DEVINTERFACE_VOLUME 注册设备通知。

解决方法(如果您的 USB 卷属于新驱动器)是在设备到达后使用GetLogicalDrives 检查更改的单元掩码:

LRESULT message_handler(HWND__* hwnd, UINT uint, WPARAM wparam, LPARAM lparam)
{
    static DWORD current_unit_mask = 0;
    switch (uint)
    {
    //...
    case WM_CREATE: // the actual creation of the window
    {
        // you can get your creation params here..like GUID..
        LPCREATESTRUCT params = (LPCREATESTRUCT)lparam;
        GUID InterfaceClassGuid = *((GUID*)params->lpCreateParams);
        DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
        ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
        NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
        NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
        for (int i = 0; i < sizeof(GuidDevInterfaceList); i++) {
            NotificationFilter.dbcc_classguid = GuidDevInterfaceList[i];

            HDEVNOTIFY dev_notify = RegisterDeviceNotification(hwnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
            if (dev_notify == NULL) {     // Handle the error by returning correct error in LRESULT format and remove throw...   
                throw std::runtime_error("Could not register for device Notifications!");
            }
        }
        current_unit_mask = GetLogicalDrives(); //Get the initial value
    }
    break;

    case WM_DEVICECHANGE:
    {

        PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lparam;

        //DBT_DEVTYP_VOLUME
        if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
        {

            switch (wparam)
            {

            case DBT_DEVICEARRIVAL:
            {
                cout << "Device Arrived" << endl;
                DWORD tmpmask = GetLogicalDrives();
                DWORD changed_mask = tmpmask ^ current_unit_mask; //changed_mask stores the uintmask of the changed device
                current_unit_mask = tmpmask; //update current_unit_mask

            }break;
            }
        }
    }
    }
}

感谢@RbMm 指出,驱动器可以有多个卷,并且可以插入一个卷而不添加到新驱动器。 所以上面的逻辑可以是监控volume的变化:

#include <windows.h> 
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <iterator>
using namespace std;
vector<wstring> GetVolumePaths(vector<wstring> volumes)
{
    DWORD  ReturnLength = 0;
    WCHAR Names[MAX_PATH] = { 0 };
    PWCHAR NameIdx = NULL;
    BOOL   Success = FALSE;
    vector<wstring> VolumePaths;
    for (int i = 0; i < volumes.size(); i++)
    {
        for (;;)
        {
            Success = GetVolumePathNamesForVolumeNameW(volumes[i].c_str(), Names, MAX_PATH, &ReturnLength);

            if (Success)
            {
                break;
            }

            if (GetLastError() != ERROR_MORE_DATA)
            {
                break;
            }

        }
        if (Success)
        {
            for (NameIdx = Names;
                NameIdx[0] != L'\0';
                NameIdx += wcslen(NameIdx) + 1)
            {
                wprintf(L"  %s", NameIdx);
                VolumePaths.push_back(NameIdx);
            }
            wprintf(L"\n");
        }

    }
    return VolumePaths;
}
vector<wstring> GetDiff(vector<wstring> v1, vector<wstring> v2)
{
    vector<wstring> diff;
    std::sort(v1.begin(), v1.end());
    std::sort(v2.begin(), v2.end());
    std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(),
        std::inserter(diff, diff.begin()));
    return diff;
}

vector<wstring> FindVolume()
{
    DWORD  CharCount = 0;

    DWORD  Error = ERROR_SUCCESS;
    HANDLE FindHandle = INVALID_HANDLE_VALUE;
    size_t Index = 0;
    BOOL   Success = FALSE;
    WCHAR  VolumeName[MAX_PATH] = L"";
    vector<wstring> volumes;

    FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));
    if (FindHandle == INVALID_HANDLE_VALUE)
    {
        Error = GetLastError();
        wprintf(L"FindFirstVolumeW failed with error code %d\n", Error);
        return volumes;
    }
    volumes.push_back(VolumeName);
    for (;;)
    {
        //
        //  Skip the \\?\ prefix and remove the trailing backslash.
        Index = wcslen(VolumeName) - 1;

        if (VolumeName[0] != L'\\' ||
            VolumeName[1] != L'\\' ||
            VolumeName[2] != L'?' ||
            VolumeName[3] != L'\\' ||
            VolumeName[Index] != L'\\')
        {
            Error = ERROR_BAD_PATHNAME;
            wprintf(L"FindFirstVolumeW/FindNextVolumeW returned a bad path: %s\n", VolumeName);
            break;
        }

        Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));
        if (!Success)
        {
            Error = GetLastError();
            if (Error != ERROR_NO_MORE_FILES)
            {
                wprintf(L"FindNextVolumeW failed with error code %d\n", Error);
                break;
            }
            Error = ERROR_SUCCESS;
            break;
        }
        volumes.push_back(VolumeName);
    }

    FindVolumeClose(FindHandle);
    FindHandle = INVALID_HANDLE_VALUE;
    return volumes;
}


LRESULT message_handler(HWND__* hwnd, UINT uint, WPARAM wparam, LPARAM lparam)
{
    static vector<wstring> current_volumes;
    switch (uint)
    {
    //...
    case WM_CREATE: // the actual creation of the window
    {
        // you can get your creation params here..like GUID..
        LPCREATESTRUCT params = (LPCREATESTRUCT)lparam;
        GUID InterfaceClassGuid = *((GUID*)params->lpCreateParams);
        DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
        ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
        NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
        NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
        for (int i = 0; i < sizeof(GuidDevInterfaceList); i++) {
            NotificationFilter.dbcc_classguid = GuidDevInterfaceList[i];

            HDEVNOTIFY dev_notify = RegisterDeviceNotification(hwnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
            if (dev_notify == NULL) {     // Handle the error by returning correct error in LRESULT format and remove throw...   
                throw std::runtime_error("Could not register for device Notifications!");
            }
        }
        current_unit_mask = FindVolume();
    }
    break;

    case WM_DEVICECHANGE:
    {

        PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lparam;

        //DBT_DEVTYP_VOLUME
        if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
        {

            switch (wparam)
            {

            case DBT_DEVICEARRIVAL:
            {
                cout << "Device Arrived" << endl;
                vector<wstring> tmp = FindVolume();
                vector<wstring> change_volumes = GetDiff(tmp, current_volumes);
                vector<wstring> VolumePaths = GetVolumePaths(change_volumes);
                current_volumes = tmp;
                for(int i = 0; i < VolumePaths.size(); i++)
                {
                    wprintf(L"   %s\n", VolumePaths[i]);
                }
            }break;
            }
        }
    }
    }
}

【讨论】:

  • 这不是正确的方法,因为在此之后可以插入额外的卷。驱动器可以有多个分区/卷。或者根本没有
  • @RbMm 感谢您的指出。似乎 OP 想要获取卷驱动器号。我将我的 USB 分成两卷,E:F:,代码也适用于我。我错过了什么吗?
  • 在您致电GetLogicalDrives 之前,是否可以插入或删除另一个设备,而不是驱动器号根本不分配给设备。这不是可靠的方法。正确方法 - (1) 通过 dbcc_name 打开设备 (2) 查询设备名称 (IOCTL_MOUNTDEV_QUERY_DEVICE_NAME) 和 (3) 通过设备名称查询安装管理器 (IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH)。但首先 OP 必须问另一个问题并解释为什么他想要确切的 dos 卷路径(这只是设备的几个符号链接名称之一)
  • 在检测到 USB Arrival 时,我想返回 USB 设备的驱动器号 (c,d,e,...)。此驱动器号将用于写入原始字节。为了写作,我必须创建一个句柄,它需要格式为 \\.\drive_letter: 的驱动器号
  • 你能使用GUID_DEVINTERFACE_VOLUME 并获得像“\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\”这样的卷 GUID 路径吗?
猜你喜欢
  • 1970-01-01
  • 2013-12-22
  • 1970-01-01
  • 2011-10-02
  • 1970-01-01
  • 2021-12-27
  • 2011-12-18
  • 1970-01-01
相关资源
最近更新 更多