This library implements mechanisms to enumerate over the devices that are
present in your Windows System. It uses Win32 API from CfgMgr32.dll which is
available since Windows 2000. This code is highly compatible and is tested with
Windows XP to Windows 11.
The current implementation provides readonly information. It is not designed that you can eject or modify the devices in the system.
It is called RJCP.IO.DeviceMgr, as it provides information very similar to
when you manage your devices from the Windows Desktop and view the details of
individual devices.
- 1. Testing
- 2. Using in Your Own Software
- 3. Implementation Details
- 4. Known Issues
- 5. Release History
To quickly test the usage of the library, run the executable DeviceInfoDump.
This gets the root tree node and dumps all information about the device tree to
the console. This gives you an idea of the information that can be obtained.
Import the library into your project.
To set up logging, to see any errors or warnings, see
Logging.md, and the example program DeviceInfoDump which is
a minimal program for dumping information.
This implementation uses CfgMgr32.dll as described in the introduction. The
APIs used are expected to be available for Windows XP and later.
Getting the list of all devices in the system uses the CM_Get_Device_ID_List API, which returns a list of all devices in the system, including those that are "phantom". No filter is applied when getting the list.
From the list of devices, CM_Locate_DevNode is used to get information about each device from the list returned, either "Normal" or "Phantom" devices. A tree of all devices is built after querying each device and getting information about the parent device in the tree.
The technique is described at MSDN
Using configuration manager functions:
Use
CM_Get_Device_ID_Listto retrieve a list of unique device instance identifier (ID) strings. To retrieve information only for devices that are present in the system, setCM_GETIDLIST_FILTER_PRESENTin theulFlagsparameter.You can use the unique device instance ID with
CM_Locate_DevNodeto retrieve aDEVINSTthat represents the device to use with other configuration manager APIs.
When refreshing devices, a different set of APIs are used. The root node is obtained with CM_Locate_DevNode, and then the tree is directly queried with CM_Get_Child and CM_Get_Sibling. These APIs only return devices that are active in the device tree.
On some specific systems, the devices returned by DeviceInstance.GetList() is
incomplete. Debugging shows that the list of strings returned by
CM_Get_Device_ID_List
is incomplete.
Calling a subsequent DeviceInstance.GetRoot().Refresh() removes any phantom
devices, and can include extra devices that were previously not present, despite
devices not being physically added by the user.
A workaround is to use DeviceInstance.GetRoot() instead of
DeviceInstance.GetList() if no phantom devices are needed.
It is not intended to try and workaround this problem in software (by calling
CM_Get_Device_ID_List and then enumerating the devices with CM_Get_Child and
CM_Get_Sibling, trying to remember the phantom devices) due to potential race
conditions.
This problem was observed in the case that an upgrade from Windows 10 to Windows
11 with SWD\PRINTENUM\{0C67E0C9-90A0-40EB-B1A6-470E41AB1CB7} and
SWD\PRINTENUM\{C9730CC1-28FB-4572-A864-D9546A92F674} which have the friendly
names OneNote and OneNote for Windows 10.
This was confirmed independently in a test program in C. See in
docs/SampleCode.
When using the API
CM_Locate_DevNode
to get the root node (even specifying CM_LOCATE_DEVNODE_PHANTOM on the root
node), subsequent calls to
CM_Get_Child
and
CM_Get_Sibling
will not return phantom devices.
Bug Fix:
- Don't refresh multiple times (DOTNET-1031)
- Fix refresh on device insertion (DOTNET-1033)
Quality:
- Clear cache on getting the list (DOTNET-1032)
- Don't use
SafeDevInstand useIntPtras there's nothing to free (DOTNET-1036)
Do not use. Refresh doesn't work properly.
Quality:
- Reduce the amount of allocations on the heap (DOTNET-1021, #1)
Quality:
- Add README.md to NuGet Package (DOTNET-813)
- Update from .NET 4.5 to .NET 4.6.2 (DOTNET-827)
- Update from .NET Standard 2.1 to .NET 6.0 (DOTNET-936, DOTNET-937, DOTNET-938, DOTNET-942, DOTNET-945)
- Update to .NET 8.0 (DOTNET-982, DOTNET-983, DOTNET-989, DOTNET-990)
- Initial Release