"To determine whether a drive is a USB-type drive, call
SetupDiGetDeviceRegistryProperty and specify the SPDRP_REMOVAL_POLICY property."
This is not safe. I've seen FireWire drives with CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL and USB drives with CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL (a card reader in a printer).
The better way is to check the BusType for being BusTypeUsb by means of IOCTL_STORAGE_QUERY_PROPERTY.
http://msdn.microsoft.com/en-us/library/ms803642.aspx
Sample:
// szPath without trailing backslash like
// "\\\\.\\X:"
// "\\\\\?\\Volume{433619ed-c6ea-11d9-a3b2-806d6172696f}
// "\\\\.\\PhysicalDrive0"
HANDLE hDevice = CreateFile(szPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if ( hDevice != INVALID_HANDLE_VALUE ) {
DWORD dwOutBytes = 0; // IOCTL output length
STORAGE_PROPERTY_QUERY Query; // input param for query
// specify the query type
Query.PropertyId = StorageDeviceProperty;
Query.QueryType = PropertyStandardQuery;
char OutBuf[1024] = {0}; // good enough, usually about 100 bytes
PSTORAGE_DEVICE_DESCRIPTOR pDevDesc = (PSTORAGE_DEVICE_DESCRIPTOR)OutBuf;
pDevDesc->Size = sizeof(OutBuf);
// Query using IOCTL_STORAGE_QUERY_PROPERTY
BOOL res = DeviceIoControl(hDevice, // device handle
IOCTL_STORAGE_QUERY_PROPERTY, // info of device property
&Query, sizeof(STORAGE_PROPERTY_QUERY), // input data buffer
pDevDesc, pDevDesc->Size, // output data buffer
&dwOutBytes, // out's length
(LPOVERLAPPED)NULL);
CloseHandle(hDevice);
if ( res ) {
// here we are
BusType = pDevDesc->BusType;
}
}