Related
I need to get the following information for all of the physical disks in the system when Windows 10 storage spaces is enabled.
Model
Serial Number
Firmware Version
Capacity
Index of the Disk
Pnp Id of the disk (to get the SCSI controller name using CM_Get_Parent)
Location information (Bus number, Target Id and LUN)
What I have tried so far:
Used WMI class MSFT_PhysicalDisk
Although this class gives me the adapter number (so i can do without the disk PNP), the location information it gives is not complete when a disk is connected to a different PCI storage controller (such as the Marvell 92xx SATA 6g controller).
Used SetupDiGetClassDevs with GUID_DEVINTERFACE_DISK, passed the handle to SetupDiGetDeviceInterface and used SetupDiGetDeviceInterfaceDetail for location information (Bus/Target Id/LUN), PNP Id, and Device Path. I can pass the device path to CreateFile and get the rest of the information (similar to this approach). The problem with this is that it does not give me all the physical disks. The disks under the storage spaces pool are omitted.
Use an approach similar to the second one but instead of SetupDiGetDeviceInterface and SetupDiGetDeviceInterfaceDetail, use SetupDiEnumDeviceInfo and CM_Get_DevNode_Registry_Property (using the Disk Drives Guid from here). Although this gives me the location and PNP id for all of the physical disks, I can't use anything here (that I know of) to call CreateFile and get the rest of the details.
How can I get the above details for each of the physical disks when storage spaces is enabled?
As a side note, if there is a way to get the disk PNP id from the disk index using the CreateFile and DeviceIoControl, that can also be very helpful for me.
at first we need enumerate all disks in system by call CM_Get_Device_Interface_ListW and CM_Get_Device_Interface_List_SizeW with GUID_DEVINTERFACE_DISK
#include <Shlwapi.h>
#include <cfgmgr32.h>
#undef _NTDDSTOR_H_
#include <ntddstor.h>
#include <ntdddisk.h>
static volatile UCHAR guz;
CONFIGRET EnumDisks(PCSTR prefix, PGUID InterfaceClassGuid)
{
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(InterfaceClassGuid,
0, pszDeviceInterface, BufferLen, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
case CR_BUFFER_SMALL:
if (err = CM_Get_Device_Interface_List_SizeW(&NeedLen, InterfaceClassGuid,
0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
default:
return err;
}
continue;
case CR_SUCCESS:
while (*pszDeviceInterface)
{
DbgPrint("Interface=[%S]\n", pszDeviceInterface);
HANDLE hFile = CreateFileW(pszDeviceInterface, FILE_GENERIC_READ,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
GetDiskPropertyByHandle(hFile);
CloseHandle(hFile);
}
GetPropertyByInterface(prefix, pszDeviceInterface);
pszDeviceInterface += 1 + wcslen(pszDeviceInterface);
}
return CR_SUCCESS;
}
}
}
CONFIGRET EnumDisks()
{
char prefix[256];
memset(prefix, '\t', sizeof(prefix));
prefix[sizeof(prefix) - 1] = 0;
prefix[0] = 0;
return EnumDisks(prefix + sizeof(prefix) - 1, const_cast<PGUID>(&GUID_DEVINTERFACE_DISK));
}
CM_Get_Device_Interface_ListW return multiple, NULL-terminated Unicode strings, each representing the symbolic link name of an interface instance.
from one side this symbolic link name can be passed to CreateFileW for open disk device. after this we can set some ioctl to disk - for get
Index of the Disk
Capacity
Serial Number
Partition Information
example:
void GetDiskPropertyByHandle(HANDLE hDisk)
{
HANDLE hPartition;
IO_STATUS_BLOCK iosb;
STORAGE_DEVICE_NUMBER sdn;
GET_LENGTH_INFORMATION li;
NTSTATUS status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, &sdn, sizeof(sdn));
if (0 <= status && sdn.DeviceType == FILE_DEVICE_DISK && !sdn.PartitionNumber)
{
DbgPrint("\\Device\\Harddisk%d\n", sdn.DeviceNumber);
WCHAR sz[64], *c = sz + swprintf(sz, L"\\Device\\Harddisk%d\\Partition", sdn.DeviceNumber);
WCHAR szSize[32];
if (0 <= (status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_DISK_GET_LENGTH_INFO, 0, 0, &li, sizeof(li))))
{
DbgPrint("Length = %S (%I64x)\n",
StrFormatByteSizeW(li.Length.QuadPart, szSize, RTL_NUMBER_OF(szSize)),
li.Length.QuadPart);
}
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };
PVOID stack = alloca(guz);
union {
PVOID buf;
PDRIVE_LAYOUT_INFORMATION_EX pdli;
PSTORAGE_DEVICE_DESCRIPTOR psdd;
PCSTR psz;
};
STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
ULONG cb = 0, rcb = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x40, PartitionCount = 4;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
switch (status = (NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, cb)))
{
case STATUS_SUCCESS:
case STATUS_BUFFER_OVERFLOW:
if (psdd->Version == sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
if (psdd->Size > cb)
{
rcb = psdd->Size;
status = STATUS_BUFFER_OVERFLOW;
}
else
{
if (psdd->SerialNumberOffset)
{
DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
}
}
}
else
{
status = STATUS_INVALID_PARAMETER;
}
break;
}
} while (status == STATUS_BUFFER_OVERFLOW);
for (;;)
{
if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (0 <= (status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_DISK_GET_DRIVE_LAYOUT_EX, 0, 0, buf, cb)))
{
if (PartitionCount = pdli->PartitionCount)
{
PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;
do
{
if (!PartitionEntry->PartitionNumber)
{
continue;
}
_itow(PartitionEntry->PartitionNumber, c, 10);
RtlInitUnicodeString(&ObjectName, sz);
DbgPrint("%wZ\nOffset=%S ", &ObjectName,
StrFormatByteSizeW(PartitionEntry->StartingOffset.QuadPart, szSize, RTL_NUMBER_OF(szSize)));
DbgPrint("Length=%S\n",
StrFormatByteSizeW(PartitionEntry->PartitionLength.QuadPart, szSize, RTL_NUMBER_OF(szSize)));
char PartitionName[256], *szPartitionName;
switch (PartitionEntry->PartitionStyle)
{
case PARTITION_STYLE_MBR:
DbgPrint("MBR: type=%x boot=%x", PartitionEntry->Mbr.PartitionType, PartitionEntry->Mbr.BootIndicator);
break;
case PARTITION_STYLE_GPT:
if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_ENTRY_UNUSED_GUID))
{
szPartitionName = "UNUSED";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_SYSTEM_GUID))
{
szPartitionName = "SYSTEM";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_RESERVED_GUID))
{
szPartitionName = "RESERVED";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_BASIC_DATA_GUID))
{
szPartitionName = "DATA";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_RECOVERY_GUID))
{
szPartitionName = "RECOVERY";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_SNAPSHOT_GUID))
{
szPartitionName = "SNAPSHOT";
}
else
{
sprintf(szPartitionName = PartitionName, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
PartitionEntry->Gpt.PartitionType.Data1,
PartitionEntry->Gpt.PartitionType.Data2,
PartitionEntry->Gpt.PartitionType.Data3,
PartitionEntry->Gpt.PartitionType.Data4[0],
PartitionEntry->Gpt.PartitionType.Data4[1],
PartitionEntry->Gpt.PartitionType.Data4[2],
PartitionEntry->Gpt.PartitionType.Data4[3],
PartitionEntry->Gpt.PartitionType.Data4[4],
PartitionEntry->Gpt.PartitionType.Data4[5],
PartitionEntry->Gpt.PartitionType.Data4[6],
PartitionEntry->Gpt.PartitionType.Data4[7]);
}
DbgPrint("[%s] %I64x \"%S\"",
szPartitionName,
PartitionEntry->Gpt.Attributes,
PartitionEntry->Gpt.Name);
break;
}
if (0 <= NtOpenFile(&hPartition, FILE_GENERIC_READ, &oa, &iosb,
FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT))
{
union {
BYTE bb[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 32*sizeof(WCHAR) ];
FILE_FS_ATTRIBUTE_INFORMATION ffai;
};
switch (NtQueryVolumeInformationFile(hPartition, &iosb, &ffai, sizeof(bb), FileFsAttributeInformation))
{
case STATUS_SUCCESS:
case STATUS_BUFFER_OVERFLOW:
DbgPrint(" \"%.*S\"\n", ffai.FileSystemNameLength >> 1 , ffai.FileSystemName);
break;
}
NtClose(hPartition);
}
} while (PartitionEntry++, --PartitionCount);
}
return ;
}
switch (status)
{
case STATUS_BUFFER_OVERFLOW:
PartitionCount = pdli->PartitionCount;
continue;
case STATUS_INFO_LENGTH_MISMATCH:
case STATUS_BUFFER_TOO_SMALL:
PartitionCount <<= 1;
continue;
default:
return ;
}
}
}
}
from another size we can get Device Instance ID from interface string by call CM_Get_Device_Interface_PropertyW with DEVPKEY_Device_InstanceId. after this we call CM_Locate_DevNodeW for get device instance handle.
CONFIGRET GetPropertyByInterface(PCSTR prefix, PCWSTR pszDeviceInterface)
{
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("%sDeviceID = %S\n", prefix, DeviceID);
DEVINST dnDevInst;
if (CR_SUCCESS == (status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL)))
{
GetPropertyByDeviceID(prefix, dnDevInst);
}
}
else
{
status = CR_WRONG_TYPE;
}
break;
}
} while (status == CR_BUFFER_SMALL);
return status;
}
with device instance handle we can query many device properties via CM_Get_DevNode_PropertyW like:
DEVPKEY_Device_LocationInfo, DEVPKEY_NAME, DEVPKEY_Device_PDOName, DEVPKEY_Device_FirmwareVersion, DEVPKEY_Device_Model, DEVPKEY_Device_DriverVersion and many others - look full list in devpkey.h
finally we can call CM_Get_Parent and recursive query all this properties for parent device(s) until we not rich top of stack:
#define OPEN_PDO
void GetPropertyByDeviceID(PCSTR prefix, DEVINST dnDevInst)
{
#ifdef OPEN_PDO
HANDLE hFile;
IO_STATUS_BLOCK iosb;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };
#endif
CONFIGRET status;
ULONG cb = 0, rcb = 0x80;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
union {
PVOID pv;
PWSTR sz;
PBYTE pb;
};
static struct
{
CONST DEVPROPKEY *PropertyKey;
PCWSTR PropertyName;
} PropertyKeys[] = {
{ &DEVPKEY_Device_PDOName, L"PDOName"},
{ &DEVPKEY_Device_Parent, L"Parent"},
{ &DEVPKEY_Device_DriverVersion, L"DriverVersion"},
{ &DEVPKEY_Device_LocationInfo, L"LocationInfo"},
{ &DEVPKEY_Device_FirmwareVersion, L"FirmwareVersion"},
{ &DEVPKEY_Device_Model, L"Model"},
{ &DEVPKEY_NAME, L"NAME"},
{ &DEVPKEY_Device_InstanceId, L"DeviceID"}
};
do
{
int n = RTL_NUMBER_OF(PropertyKeys);
do
{
CONST DEVPROPKEY *PropertyKey = PropertyKeys[--n].PropertyKey;
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_DevNode_PropertyW(dnDevInst, PropertyKey, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("%s%S=[%S]\n", prefix, PropertyKeys[n].PropertyName, sz);
#ifdef OPEN_PDO
if (!n)
{
// DEVPKEY_Device_PDOName can use in NtOpenFile
RtlInitUnicodeString(&ObjectName, sz);
if (0 <= NtOpenFile(&hFile, FILE_READ_ATTRIBUTES|SYNCHRONIZE, &oa,
&iosb, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT))
{
NtClose(hFile);
}
}
#endif
}
}
} while (status == CR_BUFFER_SMALL);
} while (n);
if (!*--prefix) break;
} while (CM_Get_Parent(&dnDevInst, dnDevInst, 0) == CR_SUCCESS);
}
also string returned by DEVPKEY_Device_PDOName we can use in NtOpenFile call for open PDO device.
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 there is a cross-platform libusb. But want to know any simple solution.
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;
}
}
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;
}
I was wondering if it is possible to get the USB device descriptor with the SetupAPI functions (like SetupDiGetDeviceRegistryProperty)?
Thank you!
EDIT
So far I am only able to receive the windows friendly name:
SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC,
&dwPropertyRegDataType, (BYTE*)szDesc, sizeof(szDesc), &dwSize)
we need have/got string representing Device Instance ID of a device. with this we first obtains a device instance handle to the device node via CM_Locate_DevNode and then call CM_Get_DevNode_Property with DEVPKEY_NAME:
The retrieved property value is the same as the value of the
DEVPKEY_Device_FriendlyName device property, if
DEVPKEY_Device_FriendlyName is set. Otherwise, the value of
DEVPKEY_NAME is same as the value of the DEVPKEY_Device_DeviceDesc
device property.
static volatile UCHAR guz;
CONFIGRET PrintFriendlyNameByDeviceID(PWSTR DeviceID)
{
DEVINST dnDevInst;
CONFIGRET status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL);
if (status == CR_SUCCESS)
{
ULONG cb = 0, rcb = 16;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
union {
PVOID pv;
PWSTR sz;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_NAME, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("NAME = %S\n", sz);
}
else
{
status = CR_WRONG_TYPE;
}
}
} while (status == CR_BUFFER_SMALL);
}
return status;
}
if we have the string that identifies the device interface instance - we can obtaining the device instance identifier from it via call CM_Get_Device_Interface_Property with DEVPKEY_Device_InstanceId key and then call PrintFriendlyNameByDeviceID
CONFIGRET PrintFriendlyNameByInterface(PCWSTR pszDeviceInterface)
{
ULONG cb = 0, rcb = 64;
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);
status = PrintFriendlyNameByDeviceID(DeviceID);
}
else
{
status = CR_WRONG_TYPE;
}
break;
}
} while (status == CR_BUFFER_SMALL);
return status;
}
and at begin we have 2 choice: of just retrieves a list of device instance IDs via call CM_Get_Device_ID_List with CM_GETIDLIST_FILTER_CLASS|CM_GETIDLIST_FILTER_PRESENT and use "{36fc9e60-c465-11cf-8056-444553540000}" as filter - this is string representation of well known GUID_DEVCLASS_USB defined in devguid.h :
void PrintFriendlyNames(PCWSTR pszFilter)
{
CONFIGRET status;
ULONG len = 0, cb = 0, rcb;
PVOID stack = alloca(guz);
PWSTR buf = 0;
do
{
if (status = CM_Get_Device_ID_List_SizeW(&len, pszFilter, CM_GETIDLIST_FILTER_CLASS|CM_GETIDLIST_FILTER_PRESENT))
{
break;
}
if (cb < (rcb = len * sizeof(WCHAR)))
{
len = (cb = RtlPointerToOffset(buf = (PWSTR)alloca(rcb - cb), stack)) / sizeof(WCHAR);
}
status = CM_Get_Device_ID_ListW(pszFilter, buf, len, CM_GETIDLIST_FILTER_CLASS|CM_GETIDLIST_FILTER_PRESENT);
if (status == CR_SUCCESS)
{
while (*buf)
{
DbgPrint("DeviceID = %S\n", buf);
PrintFriendlyNameByDeviceID(buf);
buf += 1 + wcslen(buf);
}
}
} while (status == CR_BUFFER_SMALL);
}
PrintFriendlyNames(L"{36fc9e60-c465-11cf-8056-444553540000}");
or enumerate device interfaces via CM_Get_Device_Interface_List and call PrintFriendlyNameByInterface for every device interface.
void PrintFriendlyNames(PGUID InterfaceClassGuid)
{
CONFIGRET status;
ULONG len = 0, cb = 0, rcb;
PVOID stack = alloca(guz);
PWSTR buf = 0;
do
{
if (status = CM_Get_Device_Interface_List_SizeW(&len, InterfaceClassGuid, 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
break;
}
if (cb < (rcb = len * sizeof(WCHAR)))
{
len = (cb = RtlPointerToOffset(buf = (PWSTR)alloca(rcb - cb), stack)) / sizeof(WCHAR);
}
status = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 0, buf, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (status == CR_SUCCESS)
{
while (*buf)
{
DbgPrint("Interface = %S\n", buf);
PrintFriendlyNameByInterface(buf);
buf += 1 + wcslen(buf);
}
}
} while (status == CR_BUFFER_SMALL);
}
you can use say GUID_DEVINTERFACE_USB_DEVICE
PrintFriendlyNames(const_cast<PGUID>(&GUID_DEVINTERFACE_USB_DEVICE));
the result of methods (which devices /interfaces) will be listed can be different. say om my comp when enum by GUID_DEVINTERFACE_USB_DEVICE:
Interface = \\?\USB#VID_046D&PID_C52E#5&18d671f8&0&4#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
DeviceID = USB\VID_046D&PID_C52E\5&18d671f8&0&4
NAME = USB Composite Device
Interface = \\?\USB#VID_051D&PID_0002#5B1120T12418__#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
DeviceID = USB\VID_051D&PID_0002\5B1120T12418__
NAME = American Power Conversion USB UPS
Interface = \\?\USB#VID_045E&PID_077B#5&18d671f8&0&3#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
DeviceID = USB\VID_045E&PID_077B\5&18d671f8&0&3
NAME = USB Input Device
and when enum by GUID_DEVCLASS_USB string filter:
DeviceID = USB\VID_1F75&PID_0916\120709860570000024
NAME = USB Mass Storage Device
DeviceID = USB\ROOT_HUB30\4&33ed72c&0&0
NAME = USB Root Hub (xHCI)
DeviceID = USB\VID_0951&PID_168F\001A92053B6A0CA101340008
NAME = USB Mass Storage Device
DeviceID = PCI\VEN_8086&DEV_A2AF&SUBSYS_7A741462&REV_00\3&11583659&0&A0
NAME = Intel(R) USB 3.0 eXtensible Host Controller - 1.0 (Microsoft)
DeviceID = USB\VID_046D&PID_C52E\5&18d671f8&0&4
NAME = USB Composite Device
i would like to check every devices (like wifi or bluetooth) status,knowing they are working fine or not or they lost when i do reboot stress test, how can i get the devices status(like the property of device in devices management)?does the windows has API to get that?
for get get the devices status you need call CM_Get_DevNode_Status
for get DEVINST dnDevInst you can enumerate all devices with CM_Get_Device_ID_ListW + CM_Locate_DevNode or alternative use CM_Locate_DevNode + CM_Get_Child + CM_Get_Sibling
for example:
void enumDN(DEVINST dnDevInst)
{
union {
PVOID buf;
PBYTE pb;
PWSTR sz;
};
ULONG cb = 0, rcb = 256;
static volatile UCHAR guz;
PVOID stack = alloca(guz);
WCHAR Name[MAX_DEVICE_ID_LEN];
CONFIGRET err;
if (CM_Get_Device_ID(dnDevInst, Name, RTL_NUMBER_OF(Name), 0) == CR_SUCCESS)
{
DEVPROPTYPE PropertyType;
ULONG Status, ulProblemNumber;
if (CM_Get_DevInst_Status(&Status, &ulProblemNumber, dnDevInst, 0) == CR_SUCCESS)
{
PWSTR FriendlyName = NULL;
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if ((err = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_Device_FriendlyName,
&PropertyType, pb, &rcb, 0)) == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
FriendlyName = sz;
}
}
} while (err == CR_BUFFER_SMALL);
DbgPrint("%08x %S %S\n", Status, Name, FriendlyName);
}
}
if ((err = CM_Get_Child(&dnDevInst, dnDevInst, 0)) == CR_SUCCESS)
{
do
{
enumDN(dnDevInst);
} while ((err = CM_Get_Sibling(&dnDevInst, dnDevInst, 0)) == CR_SUCCESS);
}
}
void enumDN()
{
DEVINST dnDevInst;
if (CM_Locate_DevInstW(&dnDevInst, NULL, 0) == CR_SUCCESS)
{
enumDN(dnDevInst);
}
}
From the device manager, I have a USB device node. I extracted its "Physical Device Object name" (e.g. \Device\0000010f).
Fighting for hours with NtOpenDirectoryObject, NtQueryDirectoryObject, NtOpenSymbolicLinkObject, NtQuerySymbolicLinkObject and QueryDosDevice, I couldn't find a way to get from that "Physical Device Object name" to the actual drive-letter (C:, D:, ...).
I'm looking for any storage solution (USB/SATA/...). How do I do that?
(There are many similar questions, none of them answers e.g. how to get from Physical Device Object name to \Device\HarddiskVolumeXYZ or to Volume{SOME_GUID})
what you view \Device\0000010f this is PDO (Physical Device Object) created by some bus driver (it have flag DO_BUS_ENUMERATED_DEVICE)
to it can be attached some FDO (Functional Device Object). if this is from storage stack (based on CompatibleIDs strings returned by bus device for this PDO ) typical FDO name have form \Device\Harddisk%d\DR%d and well known symbolic link to it \Device\Harddisk%d\Partition0
disk driver FDO enumerate partitions on volume and for every partition create PDO device object ( with well known symbolic link \Device\Harddisk%d\Partition%d where partition number always > 0, Partition0 is refer to whole disk FDO)
usual partition is same as volume but not always (Partitions and Volumes) also note IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS - it return array of DISK_EXTENT structures - look here for DiskNumber -The number of the disk that contains this extent. so volume can placed on several disks. but in 99%+ IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS return to you only one DISK_EXTENT
so what you can do if you have path to PDO in storage stack ?
open the device - if use ZwOpenFile (of course this worked in
user mode) we can use \Device\0000010f as is. if we want use win32
api we need use prefix \\?\GLOBALROOT for all names. really we open by this bus PDO but because disk FDO is attached to PDO all our requests will be send via FDO and handled here. desired access ? SYNCHRONIZE is enough (in case CreateFile if we not set FILE_FLAG_OVERLAPPED api implicity add this flag to DESIRED_ACCESS )
send IOCTL_STORAGE_GET_DEVICE_NUMBER to device. check that
DeviceType == FILE_DEVICE_DISK && sdn.PartitionNumber == 0
send IOCTL_DISK_GET_DRIVE_LAYOUT_EX for get a variable-sized
array of PARTITION_INFORMATION_EX structures
based on DeviceNumber (which we get at step 2) and
PartitionNumber (which we get at step 3) format symbolic link to
partition PDO - \\?\GLOBALROOT\Device\Harddisk%d\Partition%d
open partition PDO with SYNCHRONIZE access (enough because all
IOCTL which we use have FILE_ANY_ACCESS type
send IOCTL_MOUNTDEV_QUERY_DEVICE_NAME to partition
now we need handle to MountManager device (\\.\MountPointManager)
for send him IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH this IOCTL defined in mountmgr.h in input it require MOUNTDEV_NAME
which we get at step 6. on output we receive MOUNTMGR_VOLUME_PATHS
structure (also defined in mountmgr.h) alternatively we can use
IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS - if all ok we got list of
drive letters like C:, D:, etc..
for enumerate disk drives in system we can use
CM_Get_Device_ID_ListW with
{4d36e967-e325-11ce-bfc1-08002be10318} filter, open every device
instance by CM_Locate_DevNodeW and finally query for
DEVPKEY_Device_PDOName by call
CM_Get_DevNode_Property
ok, here the code example correct which do all this:
#include <mountmgr.h>
// guz == 0 always, volatile for prevent CL "optimization" - it can drop alloca(0) call
static volatile UCHAR guz;
ULONG QueryPartitionW32(HANDLE hPartition, HANDLE hMountManager)
{
MOUNTDEV_STABLE_GUID guid;
ULONG dwBytesRet;
if (DeviceIoControl(hPartition, IOCTL_MOUNTDEV_QUERY_STABLE_GUID, 0, 0, &guid, sizeof(guid), &dwBytesRet, NULL))
{
DbgPrint("StableGuid = \\\\?\\Volume{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
guid.StableGuid.Data1, guid.StableGuid.Data2, guid.StableGuid.Data3,
guid.StableGuid.Data4[0],
guid.StableGuid.Data4[1],
guid.StableGuid.Data4[2],
guid.StableGuid.Data4[3],
guid.StableGuid.Data4[4],
guid.StableGuid.Data4[5],
guid.StableGuid.Data4[6],
guid.StableGuid.Data4[7]
);
}
// assume NumberOfDiskExtents == 1
VOLUME_DISK_EXTENTS vde;
if (DeviceIoControl(hPartition, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, 0, 0, &vde, sizeof(vde), &dwBytesRet, 0))
{
if (vde.NumberOfDiskExtents)
{
DbgPrint("ofs=%I64u, len=%I64u\n", vde.Extents->StartingOffset.QuadPart, vde.Extents->ExtentLength.QuadPart);
}
}
PVOID stack = alloca(guz);
union {
PVOID buf;
PMOUNTDEV_NAME pmdn;
};
ULONG err;
ULONG cb = 0, rcb = sizeof(MOUNTDEV_NAME) + 0x10, InputBufferLength;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (DeviceIoControl(hPartition, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, 0, 0, buf, cb, &dwBytesRet, NULL))
{
DbgPrint("%.*S\n", pmdn->NameLength >> 1, pmdn->Name);
union {
PVOID pv;
PMOUNTMGR_VOLUME_PATHS pmvp;
};
cb = 0, rcb = sizeof(MOUNTMGR_VOLUME_PATHS) + 0x10, InputBufferLength = sizeof(MOUNTDEV_NAME) + pmdn->NameLength;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(pv = alloca(rcb - cb), pmdn);
}
if (DeviceIoControl(hMountManager, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
pmdn, InputBufferLength, pv, cb, &dwBytesRet, NULL))
{
PWSTR sz = pmvp->MultiSz;
while(*sz)
{
DbgPrint("%S\n", sz);
sz += 1 + wcslen(sz);
}
return NOERROR;
}
rcb = sizeof(MOUNTMGR_VOLUME_PATHS) + pmvp->MultiSzLength;
} while ((err = GetLastError()) == ERROR_MORE_DATA);
break;
}
rcb = sizeof(MOUNTDEV_NAME) + pmdn->NameLength;
} while ((err = GetLastError()) == ERROR_MORE_DATA);
return err;
}
ULONG EnumDiskPartitionsW32(HANDLE hDisk, HANDLE hMountManager)
{
STORAGE_DEVICE_NUMBER sdn;
ULONG dwBytesRet;
if (!DeviceIoControl(hDisk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesRet, NULL))
{
return GetLastError();
}
if (sdn.DeviceType != FILE_DEVICE_DISK || sdn.PartitionNumber != 0)
{
return ERROR_GEN_FAILURE;
}
WCHAR sz[128], *c = sz + swprintf(sz, L"\\\\?\\GLOBALROOT\\Device\\Harddisk%d\\Partition", sdn.DeviceNumber);
PVOID stack = alloca(guz);
union {
PVOID buf;
PDRIVE_LAYOUT_INFORMATION_EX pdli;
};
ULONG cb = 0, rcb, PartitionCount = 4;
for (;;)
{
if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, buf, cb, &dwBytesRet, NULL))
{
if (PartitionCount = pdli->PartitionCount)
{
PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;
do
{
if (!PartitionEntry->PartitionNumber)
{
continue;
}
_itow(PartitionEntry->PartitionNumber, c, 10);
DbgPrint("%S\n", sz);
HANDLE hPartition = CreateFile(sz, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hPartition != INVALID_HANDLE_VALUE)
{
QueryPartitionW32(hPartition, hMountManager);
CloseHandle(hPartition);
}
} while (PartitionEntry++, --PartitionCount);
}
return NOERROR;
}
switch (ULONG err = GetLastError())
{
case ERROR_MORE_DATA:
PartitionCount = pdli->PartitionCount;
continue;
case ERROR_BAD_LENGTH:
case ERROR_INSUFFICIENT_BUFFER:
PartitionCount <<= 1;
continue;
default:
return err;
}
}
}
void DiskEnumW32(HANDLE hMountManager)
{
static const WCHAR DEVCLASS_DISK[] = L"{4d36e967-e325-11ce-bfc1-08002be10318}";
enum { flags = CM_GETIDLIST_FILTER_CLASS|CM_GETIDLIST_FILTER_PRESENT };
ULONG len;
if (!CM_Get_Device_ID_List_SizeW(&len, DEVCLASS_DISK, flags))
{
PWSTR buf = (PWSTR)alloca(len * sizeof(WCHAR));
if (!CM_Get_Device_ID_ListW(DEVCLASS_DISK, buf, len, flags))
{
PVOID stack = buf;
static const WCHAR prefix[] = L"\\\\?\\GLOBALROOT";
ULONG cb = 0, rcb = sizeof(prefix) + 0x20;
while (*buf)
{
DbgPrint("%S\n", buf);
DEVINST dnDevInst;
if (!CM_Locate_DevNodeW(&dnDevInst, buf, CM_LOCATE_DEVNODE_NORMAL))
{
DEVPROPTYPE PropertyType;
int err;
union {
PVOID pv;
PWSTR sz;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
rcb -= sizeof(prefix) - sizeof(WCHAR);
if (!(err = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_Device_PDOName, &PropertyType,
pb + sizeof(prefix) - sizeof(WCHAR), &rcb, 0)))
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
memcpy(pv, prefix, sizeof(prefix) - sizeof(WCHAR));
DbgPrint("%S\n", sz);
HANDLE hDisk = CreateFile(sz, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hDisk != INVALID_HANDLE_VALUE)
{
EnumDiskPartitionsW32(hDisk, hMountManager);
CloseHandle(hDisk);
}
}
else
{
err = ERROR_GEN_FAILURE;
}
break;
}
rcb += sizeof(prefix) - sizeof(WCHAR);
} while (err == CR_BUFFER_SMALL);
}
buf += 1 + wcslen(buf);
}
}
}
}
void DiskEnumW32()
{
HANDLE hMountManager = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hMountManager != INVALID_HANDLE_VALUE)
{
DiskEnumW32(hMountManager);
CloseHandle(hMountManager);
}
}
Another possible solution for you, based on PowerShell and WMI alone:
$PDO = "\Device\00000052"
$DiskDriverData = Get-WmiObject Win32_PNPSignedDriver | Where {$_.PDO -eq $PDO}
$DeviceID = """" + $DiskDriverData.DeviceID.Replace("\","\\") + """"
$ComputerInfo = Get-WmiObject Win32_Computersystem
$name = $ComputerInfo.Name
$FullString = "\\$name\root\cimv2:Win32_PnPEntity.DeviceID=$DeviceID"
$PNPDevice = Get-WmiObject Win32_PNPDevice | Where {$_.SystemElement -eq $FullString}
$DiskDriveToPartition = Get-WmiObject -Class Win32_DiskDriveToDiskPartition | where {$_.Antecedent -eq $PNPDevice.SameElement}
foreach ($i in $DiskDriveToPartition) {
$Partition = Get-WmiObject -Class Win32_LogicalDiskToPartition | Where {$_.Antecedent -eq $i.Dependent}
$PartitionLetter = $Partition.Dependent.split('"')[1]
Write-Host -ForegroundColor Green "Detected Partition for the given PDO: $PartitionLetter"
}