如何使用设备的设备实例 ID 打开设备的句柄?



我正在更新我的问题,以更好地反映我的实际追求。为了快速陈述我最初混淆的事实,说"设备接口类 GUID"和设备实例 ID 之间存在一对一关系是不正确的。一个设备可以有多个设备接口。正如Ben Voigt在评论中指出的那样,有关更多信息,请参阅此处。


调用CM_Get_Child (...)函数后如何打开子设备的句柄?

以下面的代码截图为例:

#pragma comment (lib, "Setupapi.lib")
#pragma comment (lib, "Cfgmgr32.lib")
#include <iostream>
#include <Windows.h>
#include <Setupapi.h>
#include <Cfgmgr32.h> 
#define GUID_STRING_SIZE 40
int main ()
{
CONFIGRET CMResult = CR_SUCCESS;
WCHAR DeviceInstanceID[] = L"USB\VID_2109&PID_0813\8&216C1825&0&4"; // Parent Device Instance ID.
DEVNODE ParentDeviceNode = (DWORD) 0; // A device instance handle. This handle is bounded to the local machine.
CMResult = CM_Locate_DevNode ((PDEVINST) &ParentDeviceNode, DeviceInstanceID, CM_LOCATE_DEVNODE_NORMAL);
if (CMResult != CR_SUCCESS)
{
std::cout << "No parent device node found." << std::endl;
return -1;
}
else
{
DEVINST NextChildDeviceNode = (DWORD) 0;
CMResult = CM_Get_Child ((PDEVINST) &NextChildDeviceNode, ParentDeviceNode, 0x0);    // Gets the first child of the parent node. If this returns "CR_NO_SUCH_DEVNODE," then there is no child attached.
if (CMResult != CR_SUCCESS)
{
std::cout << "No child device node found." << std::endl;
return -2;
}
else
{
ULONG ChildInstanceIDBuffLength = 0;
CMResult = CM_Get_Device_ID_Size (&ChildInstanceIDBuffLength, NextChildDeviceNode, 0x0);
if (CMResult != CR_SUCCESS)
{
std::cout << "Could not get the size of the device instance ID of child device." << std::endl;
return -3;
}
else
{
WCHAR * ChildInstanceIDBuff = (WCHAR *) malloc (ChildInstanceIDBuffLength);
CMResult = CM_Get_Device_IDW (NextChildDeviceNode, ChildInstanceIDBuff, ChildInstanceIDBuffLength, 0x0);
if (CMResult != CR_SUCCESS)
{
std::cout << "Could not actual device instance ID string of child device" << std::endl;
return -4;
}
else
{
std::cout << "Found child device instance ID: ";
std::wcout << ChildInstanceIDBuff << std::endl;
/*
*  Open handle to the child device node now!
*/
}
free (ChildInstanceIDBuff);
}
}
}
return 0;
}

如何使用新获取的子设备实例 ID 打开设备的句柄?CreateFile (...)需要完整的设备路径,其中包括缺少的"设备接口类 GUID"。

更具体地说,设备路径具有以下格式:
\?usb#vid_2109&pid_0813#7&3981C8D6&0&2#{[DEVICE_INTERFACE_GUID]},其中:

  1. [DEVICE_INTERFACE_GUID]- 这是">设备接口类 GUID"。这与">设备安装程序类 GUID"不同。

如果没有一定程度的蛮力(例如CM_Enumerate_Classes (...)使用 CM_ENUMERATE_CLASSES_INTERFACE 标志(。是否可以调用某个函数来仅使用设备的">设备实例 ID"来获取设备的句柄,以便我可以调用DeviceIoControl (...)并查询有关设备的信息?

如果我们需要通过设备实例 ID 打开设备的句柄 -

  • 首次调用CM_Locate_DevNodeW获取设备的函数 与 本地计算机上指定的设备实例 ID。
  • 然后我们需要调用CM_Get_DevNode_PropertyW函数DEVPKEY_Device_PDOName- 表示设备的物理名称对象(PDO(的返回名称,我们可以在调用NtOpenFile中使用它。当然,如果非常想要 - 可以使用和调用CreateFileW如果添加L"\\?\Global\GLOBALROOT"到名称,但不能查看任何意义这样做。

volatile UCHAR guz = 0;
ULONG OpenDeviceByDeviceID(_Out_ PHANDLE FileHandle, _In_ PWSTR DeviceID)
{
DEVINST dnDevInst;
CONFIGRET CmReturnCode = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL);
if (CmReturnCode == CR_SUCCESS)
{
ULONG cb = 0, rcb = 128;
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);
}
CmReturnCode = CM_Get_DevNode_PropertyW(dnDevInst, 
&DEVPKEY_Device_PDOName, &PropertyType, pb, &rcb, 0);
if (CmReturnCode == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("PDOName = %Sn", sz);
#if 1
IO_STATUS_BLOCK iosb;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName };
RtlInitUnicodeString(&ObjectName, sz);
NTSTATUS status = NtOpenFile(FileHandle, 
FILE_GENERIC_READ, &oa, &iosb, 0, 0);
return 0 > status ? RtlNtStatusToDosError(status) : NOERROR;
#else
static WCHAR prefix[] = L"\\?\Global\GLOBALROOT";
alloca(sizeof(prefix) - sizeof(WCHAR));
PWSTR fileName = sz - _countof(prefix) + 1;
memcpy(fileName, prefix, sizeof(prefix) - sizeof(WCHAR));
HANDLE hFile = CreateFileW(fileName, FILE_GENERIC_READ, 
0, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
*FileHandle = hFile;
return NOERROR;
#endif
}
else
{
CmReturnCode = CR_WRONG_TYPE;
}
}
} while (CmReturnCode == CR_BUFFER_SMALL);
}
return CM_MapCrToWin32Err(CmReturnCode, 0);
}

您可以将CM_Enumerate_Classes函数与CM_ENUMERATE_CLASSES_INTERFACE标志一起使用(需要 Windows 8(,以获取可能的值作为SetupDiEnumDeviceInterfaces的第三个参数传递。

从 Windows 8 及更高版本的操作系统开始,调用方可以使用ulFlags成员来指定CM_Enumerate_Classes应返回的设备类。在 Windows 8 之前,CM_Enumerate_Classes只返回设备安装程序类。

请注意,查找设备上的所有接口对于诊断驱动程序问题和/或对从报废中救出的随机外设进行逆向工程非常有用。 但是你应该在调用CreateFile之前很久就知道你正在处理什么接口类。

最新更新