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