Search code examples
c#winapipinvoke

Get friendly name of a device from WM_DEVICECHANGE LParam


So i'm building an app for a school PC that tracks all inserted devices. I managed to use RegisterDeviceNotification() to get a notification in the main thread whenever i plug in or remove a device. All i can get though, is the LParam, a pointer that is device unique.

I can't find anything about how to get the friendly name of the device using that LParam. The only resource i can find is this CodeProject from 2006 (in C++).

I can't find anything on pinvoke.net and the only thing i found (i don't remember exactly where) is using a ManagementObjectSearcher to get this data, but it finds null data. (here's the code)

ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select Name from Win32_PnpEntity");

            foreach (ManagementObject devices in searcher.Get())
            {
                foreach (var v in devices.Properties)
                {
                    Console.WriteLine(v.Value);
                }
            }

            searcher.Dispose();

Can anyone please help me figure out how to get the friendly name of the device?


Solution

  • demo code:

    struct DeviceName : public LIST_ENTRY 
    {
        ULONG InterfaceHash;
        WCHAR Name[];
    
        void* operator new(size_t cb, size_t len)
        {
            return LocalAlloc(0, cb + len);
        }
    
        void operator delete(void* pv)
        {
            LocalFree(pv);
        }
    };
    
    volatile const UCHAR guz = 0;
    CONFIGRET GetFriendlyNameByDevNode(DeviceName** pp, DEVINST dnDevInst)
    {
        CONFIGRET status;
    
        ULONG cb = 32;
    
        DEVPROPTYPE PropertyType;
    
        do 
        {
            if (DeviceName* p = new(cb) DeviceName)
            {
                status = CM_Get_DevNode_PropertyW(
                    dnDevInst, &DEVPKEY_DeviceInterface_FriendlyName, &PropertyType, (PBYTE)p->Name, &cb, 0);
    
                if (status == CR_SUCCESS)
                {
                    if (PropertyType == DEVPROP_TYPE_STRING)
                    {
                        *pp = p;
                        return CR_SUCCESS;
                    }
                    else
                    {
                        status = CR_WRONG_TYPE;
                    }
                }
    
                delete p;
            }
            else
            {
                status = CR_OUT_OF_MEMORY;
            }
    
        } while (CR_BUFFER_SMALL == status);
    
        return status;
    }
    
    CONFIGRET GetFriendlyNameByInterface(DeviceName** pp, PCWSTR pszDeviceInterface)
    {
        // RTCu must be disabled !
        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);
            }
    
        } while (CR_BUFFER_SMALL == (status = CM_Get_Device_Interface_PropertyW(
            pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0)));
    
        if (status == CR_SUCCESS)
        {
            if (PropertyType == DEVPROP_TYPE_STRING)
            {
                DEVINST dnDevInst;
    
                status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL);
    
                return status == CR_SUCCESS ? GetFriendlyNameByDevNode(pp, dnDevInst) : status;
            }
            else
            {
                status = CR_WRONG_TYPE;
            }
        }
    
        return status;
    }
    
            case WM_DESTROY:
                if (_hDevNot)
                {
                    UnregisterDeviceNotification(_hDevNot);
    
                    PLIST_ENTRY head = &_DevListHead, entry = head->Flink;
    
                    while (entry != head)
                    {
                        DeviceName* p = static_cast<DeviceName*>(entry);
    
                        entry = entry->Flink;
    
                        delete p;
                    }
                }
                break;
    
            case WM_DEVICECHANGE:
                switch (wParam)
                {
                case DBT_DEVICEREMOVECOMPLETE:
                case DBT_DEVICEARRIVAL:
                    if (reinterpret_cast<PDEV_BROADCAST_DEVICEINTERFACE>(lParam)->dbcc_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
                    {
                        DeviceName* p;
                        ULONG InterfaceHash;
                        UNICODE_STRING dbcc_name;
                        RtlInitUnicodeString(&dbcc_name, reinterpret_cast<PDEV_BROADCAST_DEVICEINTERFACE>(lParam)->dbcc_name);
                        RtlHashUnicodeString(&dbcc_name, FALSE, HASH_STRING_ALGORITHM_DEFAULT, &InterfaceHash);
    
                        if (wParam == DBT_DEVICEARRIVAL)
                        {
                            if (CR_SUCCESS == GetFriendlyNameByInterface(&p, dbcc_name.Buffer))
                            {
                                p->InterfaceHash = InterfaceHash;
                                InsertHeadList(&_DevListHead, p);
                                DbgPrint("inserted %S ( %wZ )\n", p->Name, &dbcc_name);
                            }
                        }
                        else
                        {
    
                            PLIST_ENTRY head = &_DevListHead, entry = head;
    
                            while ((entry = entry->Flink) != head)
                            {
                                if (static_cast<DeviceName*>(entry)->InterfaceHash == InterfaceHash)
                                {
                                    DbgPrint("removed %S ( %wZ )\n", 
                                        static_cast<DeviceName*>(entry)->Name, &dbcc_name);
    
                                    RemoveEntryList(entry);
                                    delete static_cast<DeviceName*>(entry);
                                    break;
                                }
                            }
                        }
                    }
                    break;
                }
                return 0;
    
            case WM_CREATE:
                InitializeListHead(&_DevListHead);
                static DEV_BROADCAST_DEVICEINTERFACE dbd = { sizeof(dbd), DBT_DEVTYP_DEVICEINTERFACE };
                _hDevNot = RegisterDeviceNotificationW(hwnd, &dbd, DEVICE_NOTIFY_WINDOW_HANDLE|DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
                break;