2 * PROJECT: ReactOS Device Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/mscutils/devmgmt/devices.cpp
5 * PURPOSE: Wrapper around setupapi functions
6 * COPYRIGHT: Copyright 2014 Ged Murphy <gedmurphy@gmail.com>
15 /* PUBLIC METHODS *****************************************/
17 CDevices::CDevices(void) :
18 m_bInitialized(FALSE
),
21 ZeroMemory(&m_ImageListData
, sizeof(SP_CLASSIMAGELIST_DATA
));
23 m_RootName
[0] = UNICODE_NULL
;
26 CDevices::~CDevices(void)
28 ATLASSERT(m_bInitialized
== FALSE
);
32 CDevices::Initialize()
36 ATLASSERT(m_bInitialized
== FALSE
);
38 /* Get the device image list */
39 m_ImageListData
.cbSize
= sizeof(SP_CLASSIMAGELIST_DATA
);
40 bSuccess
= SetupDiGetClassImageList(&m_ImageListData
);
41 if (bSuccess
== FALSE
) return FALSE
;
43 bSuccess
= CreateRootDevice();
44 if (bSuccess
) m_bInitialized
= TRUE
;
46 return m_bInitialized
;
50 CDevices::Uninitialize()
52 if (m_ImageListData
.ImageList
!= NULL
)
54 SetupDiDestroyClassImageList(&m_ImageListData
);
55 ZeroMemory(&m_ImageListData
, sizeof(SP_CLASSIMAGELIST_DATA
));
58 m_bInitialized
= FALSE
;
64 CDevices::GetDeviceTreeRoot(
65 _Out_ LPWSTR RootName
,
66 _In_ DWORD RootNameSize
,
67 _Out_ PINT RootImageIndex
70 wcscpy_s(RootName
, RootNameSize
, m_RootName
);
71 *RootImageIndex
= m_RootImageIndex
;
77 CDevices::GetChildDevice(
78 _In_ DEVINST ParentDevInst
,
79 _Out_ PDEVINST DevInst
84 cr
= CM_Get_Child(DevInst
,
87 return (cr
== CR_SUCCESS
);
91 CDevices::GetSiblingDevice(
92 _In_ DEVINST PrevDevice
,
93 _Out_ PDEVINST DevInst
98 cr
= CM_Get_Sibling(DevInst
,
101 return (cr
== CR_SUCCESS
);
105 CDevices::GetDeviceStatus(
106 _In_ LPWSTR DeviceId
,
108 _Out_ PULONG ProblemNumber
111 DEVINST DeviceInstance
;
117 /* Use the device id string to lookup the instance */
118 cr
= CM_Locate_DevNodeW(&DeviceInstance
,
120 CM_LOCATE_DEVNODE_NORMAL
);
121 if (cr
== CR_SUCCESS
)
123 /* Get the status of this device */
124 cr
= CM_Get_DevNode_Status_Ex(Status
,
131 return (cr
== CR_SUCCESS
) ? TRUE
: FALSE
;
137 _Out_writes_(DeviceNameSize
) LPWSTR DeviceName
,
138 _In_ DWORD DeviceNameSize
,
139 _Outptr_ LPWSTR
*DeviceId
,
140 _Out_ PINT ClassImage
,
142 _Out_ PULONG ProblemNumber
145 WCHAR ClassGuidString
[MAX_GUID_STRING_LEN
];
153 /* Get the length of the device id string */
154 cr
= CM_Get_Device_ID_Size(&ulLength
, Device
, 0);
155 if (cr
== CR_SUCCESS
)
157 /* We alloc heap here because this will be stored in the lParam of the TV */
158 *DeviceId
= (LPWSTR
)HeapAlloc(GetProcessHeap(),
160 (ulLength
+ 1) * sizeof(WCHAR
));
163 /* Now get the actual device id */
164 cr
= CM_Get_Device_IDW(Device
,
168 if (cr
!= CR_SUCCESS
)
170 HeapFree(GetProcessHeap(), 0, *DeviceId
);
176 /* Make sure we got the string */
177 if (*DeviceId
== NULL
)
181 /* Get the current status of the device */
182 bSuccess
= GetDeviceStatus(*DeviceId
, Status
, ProblemNumber
);
183 if (bSuccess
== FALSE
)
185 HeapFree(GetProcessHeap(), 0, *DeviceId
);
190 /* Get the class guid for this device */
191 ulLength
= MAX_GUID_STRING_LEN
* sizeof(WCHAR
);
192 cr
= CM_Get_DevNode_Registry_PropertyW(Device
,
198 if (cr
== CR_SUCCESS
)
200 /* Convert the string to a proper guid */
201 CLSIDFromString(ClassGuidString
, &ClassGuid
);
205 /* It's a device with no driver */
206 ClassGuid
= GUID_DEVCLASS_UNKNOWN
;
210 /* Get the image for the class this device is in */
211 SetupDiGetClassImageIndex(&m_ImageListData
,
215 /* Get the description for the device */
216 ulLength
= DeviceNameSize
* sizeof(WCHAR
);
217 cr
= CM_Get_DevNode_Registry_PropertyW(Device
,
223 if (cr
!= CR_SUCCESS
)
225 ulLength
= DeviceNameSize
* sizeof(WCHAR
);
226 cr
= CM_Get_DevNode_Registry_PropertyW(Device
,
235 /* Cleanup if something failed */
236 if (cr
!= CR_SUCCESS
)
238 HeapFree(GetProcessHeap(), 0, *DeviceId
);
242 return (cr
== CR_SUCCESS
? TRUE
: FALSE
);
246 CDevices::EnumClasses(
247 _In_ ULONG ClassIndex
,
248 _Out_writes_bytes_(sizeof(GUID
)) LPGUID ClassGuid
,
249 _Out_writes_(ClassNameSize
) LPWSTR ClassName
,
250 _In_ DWORD ClassNameSize
,
251 _Out_writes_(ClassDescSize
) LPWSTR ClassDesc
,
252 _In_ DWORD ClassDescSize
,
253 _Out_ PINT ClassImage
256 DWORD RequiredSize
, Type
, Size
;
261 ClassName
[0] = UNICODE_NULL
;
262 ClassDesc
[0] = UNICODE_NULL
;
265 /* Get the next class in the list */
266 cr
= CM_Enumerate_Classes(ClassIndex
,
269 if (cr
!= CR_SUCCESS
) return FALSE
;
271 /* Get the name of the device class */
272 RequiredSize
= MAX_CLASS_NAME_LEN
;
273 (VOID
)SetupDiClassNameFromGuidW(ClassGuid
,
278 /* Open the registry key for this class */
279 hKey
= SetupDiOpenClassRegKeyExW(ClassGuid
,
284 if (hKey
!= INVALID_HANDLE_VALUE
)
286 Size
= ClassDescSize
;
289 /* Lookup the class description (win7+) */
290 Success
= RegQueryValueExW(hKey
,
296 if (Success
== ERROR_SUCCESS
)
298 /* Check if the string starts with an @ */
299 if (ClassDesc
[0] == L
'@')
301 /* The description is located in a module resource */
302 Success
= ConvertResourceDescriptorToString(ClassDesc
, ClassDescSize
);
305 else if (Success
== ERROR_FILE_NOT_FOUND
)
307 /* WinXP stores the description in the default value */
308 Success
= RegQueryValueExW(hKey
,
316 /* Close the registry key */
321 Success
= GetLastError();
324 /* Check if we failed to get the class description */
325 if (Success
!= ERROR_SUCCESS
)
327 /* Use the class name as the description */
328 wcscpy_s(ClassDesc
, ClassDescSize
, ClassName
);
331 /* Get the image index for this class */
332 (VOID
)SetupDiGetClassImageIndex(&m_ImageListData
,
340 CDevices::EnumDevicesForClass(
341 _Inout_opt_ LPHANDLE DeviceHandle
,
342 _In_ LPCGUID ClassGuid
,
344 _Out_ LPBOOL MoreItems
,
345 _Out_ LPTSTR DeviceName
,
346 _In_ DWORD DeviceNameSize
,
347 _Outptr_ LPTSTR
*DeviceId
,
349 _Out_ PULONG ProblemNumber
352 SP_DEVINFO_DATA DeviceInfoData
;
355 BOOL bUnknown
, bSuccess
;
362 /* The unknown class is a special case */
363 if (IsEqualGUID(*ClassGuid
, GUID_DEVCLASS_UNKNOWN
))
367 /* Check if this is the first call for this class */
368 if (*DeviceHandle
== NULL
)
370 ATLASSERT(Index
== 0);
372 /* Check for the special case */
373 if (bUnknown
== TRUE
)
375 /* Get device info for all devices for all classes */
376 hDevInfo
= SetupDiGetClassDevsW(NULL
,
380 if (hDevInfo
== INVALID_HANDLE_VALUE
)
385 /* We only want the devices for this class */
386 hDevInfo
= SetupDiGetClassDevsW(ClassGuid
,
390 if (hDevInfo
== INVALID_HANDLE_VALUE
)
394 /* Store the handle for the next call */
395 *DeviceHandle
= (HANDLE
)hDevInfo
;
399 hDevInfo
= (HDEVINFO
)*DeviceHandle
;
403 /* Get the device info for this device in the class */
404 ZeroMemory(&DeviceInfoData
, sizeof(SP_DEVINFO_DATA
));
405 DeviceInfoData
.cbSize
= sizeof(SP_DEVINFO_DATA
);
406 bSuccess
= SetupDiEnumDeviceInfo(hDevInfo
,
409 if (bSuccess
== FALSE
) goto Quit
;
413 /* We found a device, let the caller know they might be more */
416 /* Check if this is the unknown class */
419 /* We're looking for devices without guids */
420 if (IsEqualGUID(DeviceInfoData
.ClassGuid
, GUID_NULL
) == FALSE
)
422 /* This is a known device, we aren't interested in it */
427 /* Get the size required to hold the device id */
428 bSuccess
= SetupDiGetDeviceInstanceIdW(hDevInfo
,
433 if (bSuccess
== FALSE
&& (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
436 /* Allocate some heap to hold the device string */
437 *DeviceId
= (LPTSTR
)HeapAlloc(GetProcessHeap(),
439 DevIdSize
* sizeof(WCHAR
));
440 if (*DeviceId
== NULL
) goto Quit
;
442 /* Now get the device id string */
443 bSuccess
= SetupDiGetDeviceInstanceIdW(hDevInfo
,
448 if (bSuccess
== FALSE
) goto Quit
;
450 /* Skip the root device */
451 if (*DeviceId
!= NULL
&&
452 wcscmp(*DeviceId
, L
"HTREE\\ROOT\\0") == 0)
459 /* Get the current status of the device */
460 bSuccess
= GetDeviceStatus(*DeviceId
, Status
, ProblemNumber
);
461 if (bSuccess
== FALSE
) goto Quit
;
464 /* Get the device's friendly name */
465 bSuccess
= SetupDiGetDeviceRegistryPropertyW(hDevInfo
,
472 if (bSuccess
== FALSE
)
474 /* if the friendly name fails, try the description instead */
475 bSuccess
= SetupDiGetDeviceRegistryPropertyW(hDevInfo
,
484 /* If we didn't find a name, check if this is an unknown device */
485 if (bSuccess
== FALSE
&& bUnknown
== TRUE
)
487 /* We add in our own text */
488 wcscpy_s(DeviceName
, 256, L
"Unknown device");
493 if (MoreItems
== FALSE
)
494 SetupDiDestroyDeviceInfoList(hDevInfo
);
496 if (bSuccess
== FALSE
)
500 HeapFree(GetProcessHeap(), 0, *DeviceId
);
509 /* PRIVATE METHODS ******************************************/
512 CDevices::CreateRootDevice()
514 HBITMAP hRootImage
= NULL
;
515 DWORD Size
= ROOT_NAME_SIZE
;
516 BOOL bSuccess
= FALSE
;
519 /* The root name is the computer name */
520 (VOID
)GetComputerNameW(m_RootName
, &Size
);
522 /* Load the bitmap we'll be using as the root image */
523 hRootImage
= LoadBitmapW(g_hInstance
,
524 MAKEINTRESOURCEW(IDB_ROOT_IMAGE
));
525 if (hRootImage
== NULL
) goto Cleanup
;
527 /* Add this bitmap to the device image list. This is a bit hacky, but it's safe */
528 m_RootImageIndex
= ImageList_Add(m_ImageListData
.ImageList
,
531 if (m_RootImageIndex
== -1)
534 /* Get the root instance */
535 cr
= CM_Locate_DevNodeW(&m_RootDevInst
,
537 CM_LOCATE_DEVNODE_NORMAL
);
538 if (cr
== CR_SUCCESS
)
542 if (bSuccess
== FALSE
)
544 SetupDiDestroyClassImageList(&m_ImageListData
);
545 ZeroMemory(&m_ImageListData
, sizeof(SP_CLASSIMAGELIST_DATA
));
548 if (hRootImage
) DeleteObject(hRootImage
);
554 CDevices::ConvertResourceDescriptorToString(
555 _Inout_z_ LPWSTR ResourceDescriptor
,
556 _In_ DWORD ResourceDescriptorSize
559 WCHAR ModulePath
[MAX_PATH
];
560 WCHAR ResString
[256];
568 /* First check for a semi colon */
569 ptr
= wcschr(ResourceDescriptor
, L
';');
572 /* This must be an inf based descriptor, the desc is after the semi colon */
573 wcscpy_s(ResourceDescriptor
, ResourceDescriptorSize
, ++ptr
);
574 dwError
= ERROR_SUCCESS
;
578 /* This must be a dll resource based descriptor. Find the comma */
579 ptr
= wcschr(ResourceDescriptor
, L
',');
580 if (ptr
== NULL
) return ERROR_INVALID_DATA
;
582 /* Terminate the string where the comma was */
585 /* Expand any environment strings */
586 Size
= ExpandEnvironmentStringsW(&ResourceDescriptor
[1], ModulePath
, MAX_PATH
);
587 if (Size
> MAX_PATH
) return ERROR_BUFFER_OVERFLOW
;
588 if (Size
== 0) return GetLastError();
590 /* Put the comma back and move past it */
595 hModule
= LoadLibraryExW(ModulePath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
596 if (hModule
== NULL
) return GetLastError();
598 /* Convert the resource id to a number */
599 ResourceId
= _wtoi(ptr
);
601 /* If the number is negative, make it positive */
602 if (ResourceId
< 0) ResourceId
= -ResourceId
;
604 /* Load the string from the dll */
605 if (LoadStringW(hModule
, ResourceId
, ResString
, 256))
607 wcscpy_s(ResourceDescriptor
, ResourceDescriptorSize
, ResString
);
608 dwError
= ERROR_SUCCESS
;
612 dwError
= GetLastError();
615 /* Free the library */
616 FreeLibrary(hModule
);