Find current connected USB storage path in C++

前端 未结 2 1418
無奈伤痛
無奈伤痛 2021-01-16 10:14

My Env:

Qt 5.3.1
Windows 10

I need to find the path of mounted USB storage devices. Through the path, I can copy the files via Qt. I know t

相关标签:
2条回答
  • 2021-01-16 10:37

    first we need enumerate all devices which support interface GUID_DEVINTERFACE_DISK. then we can open file on this interface and query for it STORAGE_ADAPTER_DESCRIPTOR or STORAGE_DEVICE_DESCRIPTOR and look for

    BusType

    Specifies a value of type STORAGE_BUS_TYPE that indicates the type of the bus to which the device is connected.

    for usb this will be BusTypeUsb

    static volatile UCHAR guz;
    
    CONFIGRET EnumUsbStor()
    {
        CONFIGRET err;
    
        PVOID stack = alloca(guz);
        ULONG BufferLen = 0, NeedLen = 256;
    
        union {
            PVOID buf;
            PWSTR pszDeviceInterface;
        };
    
        for(;;) 
        {
            if (BufferLen < NeedLen)
            {
                BufferLen = RtlPointerToOffset(buf = alloca((NeedLen - BufferLen) * sizeof(WCHAR)), stack) / sizeof(WCHAR);
            }
    
            switch (err = CM_Get_Device_Interface_ListW(const_cast<PGUID>(&GUID_DEVINTERFACE_DISK), 
                0, pszDeviceInterface, BufferLen, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
            {
            case CR_BUFFER_SMALL:
                if (err = CM_Get_Device_Interface_List_SizeW(&NeedLen, const_cast<PGUID>(&GUID_DEVINTERFACE_DISK), 
                    0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
                {
            default:
                return err;
                }
                continue;
    
            case CR_SUCCESS:
    
                while (*pszDeviceInterface)
                {
                    BOOLEAN bIsUsb = FALSE;
                    HANDLE hFile = CreateFile(pszDeviceInterface, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
    
                    if (hFile != INVALID_HANDLE_VALUE)
                    {
                        STORAGE_PROPERTY_QUERY spq = { StorageAdapterProperty, PropertyStandardQuery }; 
                        STORAGE_ADAPTER_DESCRIPTOR sad;
    
                        ULONG n;
                        if (DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), &sad, sizeof(sad), &n, 0))
                        {
                            bIsUsb = sad.BusType == BusTypeUsb;
                        }
                        CloseHandle(hFile);
                    }
    
                    pszDeviceInterface += 1 + wcslen(pszDeviceInterface);
                }
                return 0;
            }
        }
    }
    

    also we can look for EnumeratorName in interface string - are this is USBSTOR. fast end simply:

    wcsstr(_wcsupr(pszDeviceInterface), L"\\USBSTOR#");
    

    search for \USBSTOR# substring in interface name. or more correct - get Device_InstanceId from interface name and query it for DEVPKEY_Device_EnumeratorName

    CONFIGRET IsUsbStor(DEVINST dnDevInst, BOOLEAN& bUsbStor)
    {
        ULONG cb = 0, rcb = 256;
    
        PVOID stack = alloca(guz);
        DEVPROPTYPE PropertyType;
    
        CONFIGRET status;
    
        union {
            PVOID pv;
            PWSTR EnumeratorName;
            PBYTE pb;
        };
    
        do 
        {
            if (cb < rcb)
            {
                rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
            }
    
            status = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_Device_EnumeratorName, &PropertyType,
                pb, &rcb, 0);
    
            if (status == CR_SUCCESS)
            {
                if (PropertyType == DEVPROP_TYPE_STRING)
                {
                    DbgPrint("EnumeratorName = %S\n", EnumeratorName);
                    bUsbStor = !_wcsicmp(L"USBSTOR", EnumeratorName);
                }
                else
                {
                    status = CR_WRONG_TYPE;
                }
    
                break;
            }
    
        } while (status == CR_BUFFER_SMALL);
    
        return status;
    }
    
    CONFIGRET IsUsbStor(PCWSTR pszDeviceInterface, BOOLEAN& bUsbStor)
    {
        ULONG cb = 0, rcb = 256;
    
        PVOID stack = alloca(guz);
        DEVPROPTYPE PropertyType;
    
        CONFIGRET status;
    
        union {
            PVOID pv;
            PWSTR DeviceID;
            PBYTE pb;
        };
    
        do 
        {
            if (cb < rcb)
            {
                rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
            }
    
            status = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0);
    
            if (status == CR_SUCCESS)
            {
                if (PropertyType == DEVPROP_TYPE_STRING)
                {
                    DbgPrint("DeviceID = %S\n", DeviceID);
    
                    DEVINST dnDevInst;
    
                    status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL);
    
                    if (status == CR_SUCCESS)
                    {
                        status = IsUsbStor(dnDevInst, bUsbStor);
                    }
                }
                else
                {
                    status = CR_WRONG_TYPE;
                }
    
                break;
            }
    
        } while (status == CR_BUFFER_SMALL);
    
        return status;
    }
    
    0 讨论(0)
  • 2021-01-16 10:38

    First you need to get removable drives:

    void EnumUsbDrives() {
        DWORD drv = ::GetLogicalDrives();
        if (drv == 0) return;
    
        DWORD mask = 1;
        TCHAR szDrive[] = _TEXT("?:\\");
    
        for (uint_t i = 0; i < ('Z' - 'A' + 1); i++, mask <<= 1) {
            if (drv & mask) {
                szDrive[0] = (TCHAR)(_T('A') + i);
                if (::GetDriveType(szDrive) == DRIVE_REMOVABLE) {
                    bool bUSB = IsDriveUSB(szDrive);
                    if (bUSB) {
                        // Time do to something useful
                    }
                }
            }
        }
    }
    

    Function IsDriveUSB is a bit more complicated. I have teared it from an in-house library; the function uses custom helper classes xregistry and xstring_nocase. Their purpose is pretty obvious, I believe you will have no trouble replacing it with other similar classes or API calls.

    bool IsDriveUSB (LPCTSTR szDrive) throw() {
        TCHAR szLogicalDrive[] = _TEXT("\\\\.\\x:");
        szLogicalDrive[4] = szDrive[0];
        HANDLE hDrive = ::CreateFile(szLogicalDrive, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
        if (hDrive == INVALID_HANDLE_VALUE) return false;       // Can't open drive so we have to assume the drive is fixed
    
        VOLUME_DISK_EXTENTS vde;
        DWORD dwBytesReturned = 0;
        BOOL br = ::DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &vde, sizeof(vde), &dwBytesReturned, NULL);
        ::CloseHandle(hDrive);
        if (!br) return false;      // Can't get extents info so we have to assume the drive is fixed
    
        if (vde.NumberOfDiskExtents != 1) return false;
        ULONG uPhysDrive = vde.Extents[0].DiskNumber;
        TCHAR szPhysDrive[16];
        _stprintf(szPhysDrive, _TEXT("%u"), uPhysDrive);
    
        try {
            xregistry rk(HKEY_LOCAL_MACHINE, OS.Is64bit());
            rk.open(_TEXT("SYSTEM\\CurrentControlSet\\services\\Disk\\Enum"), KEY_QUERY_VALUE);
            if (!rk.value_exists(szPhysDrive)) return false;
            xstring_nocase strInterface = rk.get_string(szPhysDrive).substring(0, 7);
            return strInterface == _TEXT("USBSTOR");
        }
        catch (...) {
            return false;
        }
    }
    
    0 讨论(0)
提交回复
热议问题