WDM驱动 PDO 设备属性疑问

我需要在 WDM 驱动中 获取 PDO 的物理信息 设备属性,如 PhysicalDeviceObjectName HardwareID DevicePropertyDeviceDescription

我知道可以用 IoGetDeviceProperty 获取,但是我的PDO 是ACPI 驱动 创建的 ,我需要获取我PDOI 的 _UID , 但是 IoGetDeviceProperty 只支持如下的信息获取,没有 UID 的类型。

img

我怎么获取PDO设备的_UID 呢?

给一个通过 IRP 请求 获取 UID 的代码示例

引用chatgpt内容作答:
在 WDM 驱动中,您可以使用其他方法来获取 ACPI PDO 设备的 _UID(唯一 ID)。由于 IoGetDeviceProperty 函数不直接提供 _UID 属性的查询,您可以尝试以下方法来获取 _UID:

1、使用 IRP_MN_QUERY_ID 请求:
您可以向 ACPI PDO 设备发送 IRP_MN_QUERY_ID 请求,并指定 QueryType 为 BusQueryDeviceID。这将使设备栈返回设备的标识符字符串,其中包含设备的 _UID。

2、使用 IRP_MN_QUERY_DEVICE_TEXT 请求:
同样,您可以向 ACPI PDO 设备发送 IRP_MN_QUERY_DEVICE_TEXT 请求,并指定 RequestType 为 DeviceTextDescription。这将使设备栈返回设备的描述字符串,其中可能包含设备的 _UID。

3、解析 ACPI 配置:
ACPI 设备对象通常包含一个名为 _HID 的属性,它是硬件 ID。该 ID 通常与设备的 _UID 相对应。您可以通过解析 ACPI 配置来获取这些属性,并从中提取 _UID。

以下是第一种方法的示例代码:

NTSTATUS GetAcpiDeviceUid(PDEVICE_OBJECT pdo, PWSTR* pUid)
{
    NTSTATUS status = STATUS_SUCCESS;
    PIRP irp = NULL;
    KEVENT event;
    IO_STATUS_BLOCK ioStatusBlock;

    PAGED_CODE();

    *pUid = NULL;

    KeInitializeEvent(&event, NotificationEvent, FALSE);
    irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, pdo, NULL, 0, NULL, &event, &ioStatusBlock);

    if (irp == NULL)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation(irp);
    irpSp->MinorFunction = IRP_MN_QUERY_ID;
    irpSp->Parameters.QueryId.IdType = BusQueryDeviceID;

    status = IoCallDriver(pdo, irp);
    if (status == STATUS_PENDING)
    {
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = ioStatusBlock.Status;
    }

    if (NT_SUCCESS(status))
    {
        *pUid = ExAllocatePoolWithTag(PagedPool, ioStatusBlock.Information, 'myTag');
        if (*pUid != NULL)
        {
            RtlCopyMemory(*pUid, ioStatusBlock.Information, ioStatusBlock.Information);
        }
        else
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    return status;
}

这只是获取 ACPI PDO 设备 _UID 的一种方法。在实际实现中,您可能需要根据具体的设备和驱动情况进行适当的调整。同时,记得在使用 ExAllocatePoolWithTag 分配的内存后,在不再使用时使用 ExFreePool 释放它,以避免内存泄漏。

ACPI 设备的 _UID 并非硬件标识符,而是与 ACPI 设备节点相关的标识符。如果您需要硬件 ID,请使用 IRP_MN_QUERY_ID 请求并指定 QueryType 为 BusQueryHardwareIDs。

结合chatgpt
对于ACPI驱动创建的PDO,确实无法直接通过IoGetDeviceProperty获取_UID属性。在这种情况下,您需要使用其他方法来获取PDO的其他物理信息和设备属性。

一种可行的方法是使用WMI (Windows Management Instrumentation) 查询。您可以通过执行WMI查询来获取设备的其他属性,包括_UID。以下是一个示例代码片段,演示如何使用WMI查询获取设备的物理信息和_UID属性:

c
#include <Windows.h>
#include <wbemidl.h>

#pragma comment(lib, "wbemuuid.lib")

int main()
{
    HRESULT hres;

    // 初始化COM
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres))
    {
        return 1;
    }

    // 初始化WMI
    hres = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE,
        NULL
    );
    if (FAILED(hres))
    {
        CoUninitialize();
        return 1;
    }

    IWbemLocator* pLoc = NULL;
    hres = CoCreateInstance(
        CLSID_WbemLocator,
        0,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator,
        (LPVOID*)&pLoc
    );
    if (FAILED(hres))
    {
        CoUninitialize();
        return 1;
    }

    IWbemServices* pSvc = NULL;
    hres = pLoc->ConnectServer(
        _bstr_t(L"ROOT\\CIMV2"),
        NULL,
        NULL,
        0,
        NULL,
        0,
        0,
        &pSvc
    );
    if (FAILED(hres))
    {
        pLoc->Release();
        CoUninitialize();
        return 1;
    }

    // 设置WMI查询语句
    _bstr_t query = _bstr_t("SELECT * FROM Win32_PnPEntity");

    // 执行WMI查询
    IEnumWbemClassObject* pEnumerator = NULL;
    hres = pSvc->ExecQuery(
        bstr_t("WQL"),
        query,
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pEnumerator
    );
    if (FAILED(hres))
    {
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return 1;
    }

    // 遍历查询结果
    IWbemClassObject* pclsObj = NULL;
    ULONG uReturn = 0;
    while (pEnumerator)
    {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
        if (0 == uReturn)
        {
            break;
        }

        VARIANT vtProp;

        // 获取设备描述
        hr = pclsObj->Get(L"Description", 0, &vtProp, 0, 0);
        if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR)
        {
            wprintf(L"Device Description: %s\n", vtProp.bstrVal);
            VariantClear(&vtProp);
        }

        // 获取硬件ID
        hr = pclsObj->Get(L"HardwareID", 0, &vtProp, 0, 0);
        if (SUCCEEDED(hr) && (vtProp.vt == (VT_ARRAY | VT_BSTR)))
        {
            SAFEARRAY* pSafeArray = vtProp.parray;
            LONG lLBound, lUBound;
            SafeArrayGetLBound(pSafeArray, 1, &lLBound);
            SafeArrayGetUBound(pSafeArray, 1, &lUBound);

            for (LONG i = lLBound; i <= lUBound; i++)
            {
                BSTR bstrValue;
                SafeArrayGetElement(pSafeArray, &i, &bstrValue);
                wprintf(L"Hardware ID: %s\n", bstrValue);
            }

            VariantClear(&vtProp);
        }

        // 获取_UID属性
        hr = pclsObj->Get(L"_UID", 0, &vtProp, 0, 0);
        if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR)
        {
            wprintf(L"_UID: %s\n", vtProp.bstrVal);
            VariantClear(&vtProp);
        }

        pclsObj->Release();
    }

    pSvc->Release();
    pLoc->Release();
    pEnumerator->Release();
    Co

该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:

在 WDM 驱动中,可以通过以下步骤获取 PDO 设备的 _UID 属性:

  1. 使用 IoGetDeviceProperty 函数获取 PDO 设备的 DEVPKEY_Device_PDOName 属性,该属性包含了 PDO 设备的名称。

  2. 使用 IoGetDeviceObjectPointer 函数获取 PDO 设备的设备对象指针和设备扩展对象指针。

  3. 使用 IoGetDeviceProperty 函数获取 PDO 设备的 DEVPKEY_Device_EnumeratorName 属性,该属性包含了 PDO 设备所属的枚举器名称。

  4. 使用 IoGetDeviceProperty 函数获取枚举器设备的 DEVPKEY_Device_Enumerator 属性,该属性包含了枚举器设备的设备描述符。

  5. 使用 CM_Get_Device_ID 函数获取设备描述符中的 _UID 属性,该属性包含了 PDO 设备的唯一标识符。

以下是获取 PDO 设备 _UID 属性的代码示例:


NTSTATUS GetPdoUid(PDEVICE_OBJECT pdo, PWSTR uid, ULONG uidSize)
{
    NTSTATUS status;
    PDEVICE_OBJECT deviceObject = NULL;
    PDEVICE_OBJECT parentDeviceObject = NULL;
    UNICODE_STRING pdoName;
    UNICODE_STRING enumeratorName;
    DEVPROPTYPE propertyType;
    ULONG enumeratorNameLength;
    ULONG deviceDescriptionLength;
    ULONG ulSize = 0;
    ULONG ulPropertySize = 0;
    ULONG ulResult = 0;
    PVOID pPropertyBuffer = NULL;
    CONFIGRET configRet;
    PWSTR deviceDescription = NULL;
    ULONG uidLength = 0;
    PWSTR uidBuffer = NULL;
    ULONG requiredSize = 0;
    CONFIGRET cr;
    DEVINST devinst;
    CONFIGRET status;
    ULONG ulLength = 0;

    // 获取 PDO 设备名称
    status = IoGetDeviceProperty(pdo, &DEVPKEY_Device_PDOName, sizeof(UNICODE_STRING), &pdoName, &ulPropertySize);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    // 获取 PDO 设备对象指针和设备扩展对象指针
    status = IoGetDeviceObjectPointer(&pdoName, FILE_READ_ATTRIBUTES, &deviceObject, &parentDeviceObject);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    // 获取 PDO 设备所属的枚举器名称
    status = IoGetDeviceProperty(parentDeviceObject, &DEVPKEY_Device_EnumeratorName, sizeof(UNICODE_STRING), &enumeratorName, &ulPropertySize);
    if (!NT_SUCCESS(status)) {
        IoDeleteDevice(deviceObject);
        return status;
    }

    // 获取枚举器设备的设备描述符
    ulSize = 0;
    cr = CM_Get_Device_ID_Size(&ulSize, enumeratorName.Buffer, CM_GETIDLIST_FILTER_ENUMERATOR);
    if (cr != CR_SUCCESS || ulSize == 0) {
        IoDeleteDevice(deviceObject);
        return STATUS_UNSUCCESSFUL;
    }

    PWSTR pDeviceID = (PWSTR)ExAllocatePoolWithTag(PagedPool, ulSize, 'TDIM');
    if (pDeviceID == NULL) {
        IoDeleteDevice(deviceObject);
        return STATUS_NO_MEMORY;
    }

    cr = CM_Get_Device_ID_List(enumeratorName.Buffer, pDeviceID, ulSize, CM_GETIDLIST_FILTER_ENUMERATOR);
    if (cr != CR_SUCCESS) {
        ExFreePool(pDeviceID);
        IoDeleteDevice(deviceObject);
        return STATUS_UNSUCCESSFUL;
    }

    // 获取设备描述符中的 _UID 属性
    cr = CM_Locate_DevNode(&devinst, pDeviceID, CM_LOCATE_DEVNODE_NORMAL);
    if (cr != CR_SUCCESS) {
        ExFreePool(pDeviceID);
        IoDeleteDevice(deviceObject);
        return STATUS_UNSUCCESSFUL;
    }

    ulLength = 0;
    cr = CM_Get_DevNode_Property(devinst, &DEVPKEY_Device_UniqueId, &propertyType, NULL, &ulLength, 0);
    if (cr != CR_BUFFER_SMALL || ulLength == 0) {
        ExFreePool(pDeviceID);
        IoDeleteDevice(deviceObject);
        return STATUS_UNSUCCESSFUL;
    }

    uidBuffer = (PWSTR)ExAllocatePoolWithTag(PagedPool, ulLength, 'TDIM');
    if (uidBuffer == NULL) {
        ExFreePool(pDeviceID);
        IoDeleteDevice(deviceObject);
        return STATUS_NO_MEMORY;
    }

    cr = CM_Get_DevNode_Property(devinst, &请注意,上述代码示例中获取 _UID 属性的方式是使用 `DEVPKEY_Device_UniqueId` 属性,而不是 `DEVPKEY_Device_HardwareIds` 属性。这是因为 `_UID` 属性是一个设备特定的唯一标识符,而 `HardwareID` 属性是一个设备的硬件标识符列表。

如果您希望获取 `HardwareID` 属性,可以使用以下代码:

```c
// 获取设备的硬件 ID 列表
status = IoGetDeviceProperty(pdo, &DEVPKEY_Device_HardwareIds, 0, NULL, &ulSize);
if (status != STATUS_BUFFER_TOO_SMALL) {
    IoDeleteDevice(deviceObject);
    return status;
}

pPropertyBuffer = ExAllocatePoolWithTag(PagedPool, ulSize, 'TDIM');
if (pPropertyBuffer == NULL) {
    IoDeleteDevice(deviceObject);
    return STATUS_NO_MEMORY;
}

status = IoGetDeviceProperty(pdo, &DEVPKEY_Device_HardwareIds, ulSize, pPropertyBuffer, &ulSize);
if (!NT_SUCCESS(status)) {
    ExFreePool(pPropertyBuffer);
    IoDeleteDevice(deviceObject);
    return status;
}

PHardwareIDList pHardwareIDList = (PHardwareIDList)pPropertyBuffer;
PWCHAR pHardwareID = (PWCHAR)(pHardwareIDList + 1);

其中,PHardwareIDList 是一个结构体,定义如下:

typedef struct _HardwareIDList {
    ULONG NumberOfIds;
    WCHAR Ids[ANYSIZE_ARRAY];
} HardwareIDList, *PHardwareIDList;

HardwareIDList 结构体中,NumberOfIds 表示硬件 ID 的数量,Ids 数组包含了所有硬件 ID。


如果以上回答对您有所帮助,点击一下采纳该答案~谢谢

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 以下回答由chatgpt基于相关博客总结生成:

    解决方案

    要获取ACPI驱动创建的PDO设备的_UID属性,可以通过如下步骤进行操作:

    1. 在WDM驱动程序中,从ACPI驱动创建的PDO设备对象开始遍历,获取其父设备对象指针。

    2. 使用父设备对象指针调用IoGetDeviceProperty函数,指定属性参数为DevicePropertyDeviceID,获取设备的硬件ID(HardwareID)。

    3. 使用IO_STACK_LOCATION结构体中的Parameters.QueryDeviceRelations关联的DEVICE_RELATIONS结构体来获取ACPI设备的子设备对象。

    4. 循环遍历子设备对象,获取其对应的_UID属性值。

    下面是一个示例代码片段:

    #include <ntddk.h>
    #include <wdm.h>
    
    NTSTATUS GetPDODeviceInfo(PDEVICE_OBJECT pdo)
    {
        PDEVICE_OBJECT parentDeviceObject;
        UNICODE_STRING deviceID;
        NTSTATUS status;
    
        parentDeviceObject = pdo->AttachedDevice;
    
        // 获取设备的硬件ID(HardwareID)
        status = IoGetDeviceProperty(parentDeviceObject, DevicePropertyDeviceID,
            sizeof(UNICODE_STRING), &deviceID.Length, &deviceID);
        if (!NT_SUCCESS(status)) {
            // 错误处理
            return status;
        }
    
        // 打印设备的硬件ID(HardwareID)
        DbgPrint("Hardware ID: %wZ\n", &deviceID);
    
        // 释放deviceID
        RtlFreeUnicodeString(&deviceID);
    
        // 获取ACPI设备的子设备对象
        PDEVICE_RELATIONS deviceRelations = (PDEVICE_RELATIONS)Irp->IoStatus.Information;
    
        // 遍历子设备对象
        for (ULONG i = 0; i < deviceRelations->Count; i++) {
            // 获取设备对象的_UID属性值
            IO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
            PKEY_VALUE_PARTIAL_INFORMATION keyInformation = NULL;
            ULONG keyLength = 0;
            PVOID buffer = NULL;
    
            // 为查询注册表键值的长度
            status = ZwQueryValueKey(
                ParentDeviceObject,
                L"DeviceID",
                KeyValuePartialInformation,
                buffer,
                keyLength,
                &keyLength
            );
    
            if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW) {
                buffer = ExAllocatePoolWithTag(PagedPool, keyLength, 'DevI');
                if (buffer) {
                    status = ZwQueryValueKey(
                        ParentDeviceObject,
                        L"DeviceID",
                        KeyValuePartialInformation,
                        buffer,
                        keyLength,
                        &keyLength
                    );
                    if (NT_SUCCESS(status)) {
                        keyInformation = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
    
                        // 输出设备的_UID属性值
                        DbgPrint("UID: %ws\n", (PWCHAR)keyInformation->Data);
                    }
                }
            }
    
            // 释放buffer
            if (buffer != NULL) {
                ExFreePoolWithTag(buffer, 'DevI');
            }
        }
    
        return STATUS_SUCCESS;
    }
    
    // 在驱动程序中使用
    void DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
    {
        // ...
    
        NTSTATUS status;
        UNICODE_STRING pdoName;
    
        // 通过某种方式获取ACPI驱动创建的PDO设备对象
        // ...
    
        // 调用获取PDO设备信息函数
        status = GetPDODeviceInfo(pdo);
        if (!NT_SUCCESS(status)) {
            // 错误处理
        }
    
        // ...
    }
    

    以上是一个简单的示例代码,用于获取ACPI驱动创建的PDO设备的_UID属性。具体的实现可能因系统环境及驱动代码结构而有所差异,可以根据实际需求进行修改。

    希望对你有所帮助!如果需要进一步帮助,请随时提问。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

援引GPT回答:
如果您需要获取ACPI驱动创建的PDO设备的_UID,可以尝试使用以下方法:

  1. 获取ACPI设备对象的设备扩展属性:
    您可以使用IoGetDeviceProperty函数获取ACPI设备对象的设备扩展属性。具体步骤如下:

    • 首先,使用IoGetDeviceProperty函数获取设备对象的DevicePropertyPhysicalDeviceObjectName属性,以获取设备对象的物理设备对象名称。
    • 然后,使用IoGetDeviceObjectPointer函数将物理设备对象名称转换为物理设备对象的指针。
    • 最后,对物理设备对象调用IoGetDeviceProperty函数,获取其设备扩展属性,包括DevicePropertyDeviceDescriptionDevicePropertyHardwareID

    下面是一个示例代码片段,展示了如何使用上述方法获取ACPI设备对象的设备扩展属性:

    // 获取设备对象的物理设备对象名称
    UNICODE_STRING pdoObjectName;
    status = IoGetDeviceProperty(deviceObject, DevicePropertyPhysicalDeviceObjectName, sizeof(UNICODE_STRING), &pdoObjectName, &returnLength);
    if (NT_SUCCESS(status)) {
        // 将物理设备对象名称转换为物理设备对象的指针
        PDEVICE_OBJECT pdoDeviceObject;
        status = IoGetDeviceObjectPointer(&pdoObjectName, FILE_READ_ATTRIBUTES, &pdoDeviceObject, &pdoFileObject);
        if (NT_SUCCESS(status)) {
            // 获取物理设备对象的设备扩展属性
            UNICODE_STRING deviceDescription;
            status = IoGetDeviceProperty(pdoDeviceObject, DevicePropertyDeviceDescription, sizeof(UNICODE_STRING), &deviceDescription, &returnLength);
            if (NT_SUCCESS(status)) {
                // 获取设备描述信息成功
                // 处理设备描述信息
            }
    
            UNICODE_STRING hardwareID;
            status = IoGetDeviceProperty(pdoDeviceObject, DevicePropertyHardwareID, sizeof(UNICODE_STRING), &hardwareID, &returnLength);
            if (NT_SUCCESS(status)) {
                // 获取硬件ID成功
                // 处理硬件ID
            }
    
            // 释放物理设备对象
            ObDereferenceObject(pdoDeviceObject);
        }
    }
    
  2. 使用其他方法获取_UID:
    如果上述方法无法获取_UID,您可以尝试使用其他方法来获取_UID。例如,您可以尝试使用ACPI接口来查询设备的_UID,或者查看设备的注册表信息以获取_UID。具体方法可能因设备和驱动程序的不同而有所不同,请根据具体情况进行调整。

请注意,获取_UID可能需要对ACPI驱动进行特定的处理,因此具体实现可能会因驱动程序的实现方式而有所不同。建议您参考相关文档或与驱动程序开发者进行沟通,以获取准确的方法和信息。

结合GPT建议:
在 ACPI 驱动中,获取 PDO 设备的 UID(Unique Identifier)可以通过处理 IRP_MN_QUERY_ID 请求来实现。在 Windows 驱动开发中,IRP_MN_QUERY_ID 是一种 I/O 请求包 (IRP),它用于获取设备的标识符,包括设备的 UID。下面是一个获取 UID 的示例代码:

#include <ntddk.h>

NTSTATUS GetDeviceUID(PDEVICE_OBJECT pdo, PUNICODE_STRING pUID)
{
    PIRP irp;
    KEVENT event;
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status;

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
                                       pdo,
                                       NULL,
                                       0,
                                       NULL,
                                       &event,
                                       &ioStatus);

    if (irp == NULL)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // 设置 IRP 请求类型为 IRP_MN_QUERY_ID,并指定获取 UID
    UCHAR minorFunction = IRP_MN_QUERY_ID;
    irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
    irp->IoStatus.Information = 0;
    IoSetNextIrpStackLocation(irp);
    irp->Tail.Overlay.CurrentStackLocation->MinorFunction = minorFunction;

    // 设置请求 ID 类型为 BusQueryDeviceID,即获取设备的 UID
    irp->Tail.Overlay.CurrentStackLocation->Parameters.QueryId.IdType = BusQueryDeviceID;

    // 发送 IRP 请求
    status = IoCallDriver(pdo, irp);

    if (status == STATUS_PENDING)
    {
        // 如果请求挂起,则等待请求完成
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = ioStatus.Status;
    }

    if (NT_SUCCESS(status))
    {
        // 获取 UID 字符串
        *pUID = *((PUNICODE_STRING)ioStatus.Information);
    }

    return status;
}

在调用 GetDeviceUID 函数时,传递包含 ACPI PDO 的 PDEVICE_OBJECT 指针作为参数,并将 PUNICODE_STRING 类型的指针传递给函数,用于接收设备的 UID 字符串。

请注意,上述代码假定你已经创建了一个驱动,并且已经在驱动中实现了正确的 PnP(Plug and Play)处理程序。在实际的驱动中,你需要处理其他 IRP 类型和更多的错误情况。此外,此代码示例适用于 Windows 内核驱动程序开发。

根据你的具体环境和驱动实现,可能还需要进行其他适当的设置和检查,以确保正确获取设备的 UID。