1

我正在更新我的问题,以更好地反映我实际追求的目标。为了快速说明我最初的困惑,说“设备接口类 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\0"; // 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_INTERFACE 标志),似乎没有一种简单的方法来获得这个“设备接口类 GUID ”。是否可以调用一个函数来仅使用设备的“设备实例 ID ”来获取设备的句柄,以便我可以调用和查询有关设备的信息?CM_Enumerate_Classes (...)DeviceIoControl (...)

4

2 回答 2

0

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

  • 第一个调用CM_Locate_DevNodeW函数获取与本地机器上的指定设备实例 ID 关联的设备节点的设备实例句柄。
  • 然后我们需要调用CM_Get_DevNode_PropertyW函数 with DEVPKEY_Device_PDOName-this 返回代表设备的物理名称对象 ( 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 = %S\n", 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);
}
于 2020-01-14T22:34:26.527 回答
0

您可以使用带有标志的CM_Enumerate_Classes函数CM_ENUMERATE_CLASSES_INTERFACE(需要 Windows 8)来获取可能的值作为SetupDiEnumDeviceInterfaces.

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

请注意,查找设备上的所有接口对于诊断驱动程序问题和/或逆向工程从废品中挽救的随机外围设备非常有用。但是,在您开始调用之前,您应该知道您正在处理的接口类CreateFile

于 2020-01-14T21:55:39.903 回答