[DEVMGR]
[reactos.git] / reactos / base / applications / mscutils / devmgmt_new / Devices.cpp
1 /*
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>
7 *
8 */
9
10 #include "stdafx.h"
11 #include "devmgmt.h"
12 #include "Devices.h"
13
14
15 /* PUBLIC METHODS *****************************************/
16
17 CDevices::CDevices(void) :
18 m_bInitialized(FALSE),
19 m_RootImageIndex(-1)
20 {
21 ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA));
22
23 m_RootName[0] = UNICODE_NULL;
24 }
25
26 CDevices::~CDevices(void)
27 {
28 ATLASSERT(m_bInitialized == FALSE);
29 }
30
31 BOOL
32 CDevices::Initialize()
33 {
34 BOOL bSuccess;
35
36 ATLASSERT(m_bInitialized == FALSE);
37
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;
42
43 bSuccess = CreateRootDevice();
44 if (bSuccess) m_bInitialized = TRUE;
45
46 return m_bInitialized;
47 }
48
49 BOOL
50 CDevices::Uninitialize()
51 {
52 if (m_ImageListData.ImageList != NULL)
53 {
54 SetupDiDestroyClassImageList(&m_ImageListData);
55 ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA));
56 }
57
58 m_bInitialized = FALSE;
59
60 return TRUE;
61 }
62
63 BOOL
64 CDevices::GetDeviceTreeRoot(
65 _Out_ LPWSTR RootName,
66 _In_ DWORD RootNameSize,
67 _Out_ PINT RootImageIndex
68 )
69 {
70 wcscpy_s(RootName, RootNameSize, m_RootName);
71 *RootImageIndex = m_RootImageIndex;
72
73 return TRUE;
74 }
75
76 BOOL
77 CDevices::GetChildDevice(
78 _In_ DEVINST ParentDevInst,
79 _Out_ PDEVINST DevInst
80 )
81 {
82 CONFIGRET cr;
83
84 cr = CM_Get_Child(DevInst,
85 ParentDevInst,
86 0);
87 return (cr == CR_SUCCESS);
88 }
89
90 BOOL
91 CDevices::GetSiblingDevice(
92 _In_ DEVINST PrevDevice,
93 _Out_ PDEVINST DevInst
94 )
95 {
96 CONFIGRET cr;
97
98 cr = CM_Get_Sibling(DevInst,
99 PrevDevice,
100 0);
101 return (cr == CR_SUCCESS);
102 }
103
104 BOOL
105 CDevices::GetDeviceStatus(
106 _In_ LPWSTR DeviceId,
107 _Out_ PULONG Status,
108 _Out_ PULONG ProblemNumber
109 )
110 {
111 DEVINST DeviceInstance;
112 CONFIGRET cr;
113
114 *Status = 0;
115 *ProblemNumber = 0;
116
117 /* Use the device id string to lookup the instance */
118 cr = CM_Locate_DevNodeW(&DeviceInstance,
119 DeviceId,
120 CM_LOCATE_DEVNODE_NORMAL);
121 if (cr == CR_SUCCESS)
122 {
123 /* Get the status of this device */
124 cr = CM_Get_DevNode_Status_Ex(Status,
125 ProblemNumber,
126 DeviceInstance,
127 0,
128 NULL);
129 }
130
131 return (cr == CR_SUCCESS) ? TRUE : FALSE;
132 }
133
134 BOOL
135 CDevices::GetDevice(
136 _In_ DEVINST Device,
137 _Out_writes_(DeviceNameSize) LPWSTR DeviceName,
138 _In_ DWORD DeviceNameSize,
139 _Outptr_ LPWSTR *DeviceId,
140 _Out_ PINT ClassImage,
141 _Out_ PULONG Status,
142 _Out_ PULONG ProblemNumber
143 )
144 {
145 WCHAR ClassGuidString[MAX_GUID_STRING_LEN];
146 GUID ClassGuid;
147 ULONG ulLength;
148 CONFIGRET cr;
149 BOOL bSuccess;
150
151 *DeviceId = NULL;
152
153 /* Get the length of the device id string */
154 cr = CM_Get_Device_ID_Size(&ulLength, Device, 0);
155 if (cr == CR_SUCCESS)
156 {
157 /* We alloc heap here because this will be stored in the lParam of the TV */
158 *DeviceId = (LPWSTR)HeapAlloc(GetProcessHeap(),
159 0,
160 (ulLength + 1) * sizeof(WCHAR));
161 if (*DeviceId)
162 {
163 /* Now get the actual device id */
164 cr = CM_Get_Device_IDW(Device,
165 *DeviceId,
166 ulLength + 1,
167 0);
168 if (cr != CR_SUCCESS)
169 {
170 HeapFree(GetProcessHeap(), 0, *DeviceId);
171 *DeviceId = NULL;
172 }
173 }
174 }
175
176 /* Make sure we got the string */
177 if (*DeviceId == NULL)
178 return FALSE;
179
180
181 /* Get the current status of the device */
182 bSuccess = GetDeviceStatus(*DeviceId, Status, ProblemNumber);
183 if (bSuccess == FALSE)
184 {
185 HeapFree(GetProcessHeap(), 0, *DeviceId);
186 *DeviceId = NULL;
187 return FALSE;
188 }
189
190 /* Get the class guid for this device */
191 ulLength = MAX_GUID_STRING_LEN * sizeof(WCHAR);
192 cr = CM_Get_DevNode_Registry_PropertyW(Device,
193 CM_DRP_CLASSGUID,
194 NULL,
195 ClassGuidString,
196 &ulLength,
197 0);
198 if (cr == CR_SUCCESS)
199 {
200 /* Convert the string to a proper guid */
201 CLSIDFromString(ClassGuidString, &ClassGuid);
202 }
203 else
204 {
205 /* It's a device with no driver */
206 ClassGuid = GUID_DEVCLASS_UNKNOWN;
207 }
208
209
210 /* Get the image for the class this device is in */
211 SetupDiGetClassImageIndex(&m_ImageListData,
212 &ClassGuid,
213 ClassImage);
214
215 /* Get the description for the device */
216 ulLength = DeviceNameSize * sizeof(WCHAR);
217 cr = CM_Get_DevNode_Registry_PropertyW(Device,
218 CM_DRP_FRIENDLYNAME,
219 NULL,
220 DeviceName,
221 &ulLength,
222 0);
223 if (cr != CR_SUCCESS)
224 {
225 ulLength = DeviceNameSize * sizeof(WCHAR);
226 cr = CM_Get_DevNode_Registry_PropertyW(Device,
227 CM_DRP_DEVICEDESC,
228 NULL,
229 DeviceName,
230 &ulLength,
231 0);
232
233 }
234
235 /* Cleanup if something failed */
236 if (cr != CR_SUCCESS)
237 {
238 HeapFree(GetProcessHeap(), 0, *DeviceId);
239 *DeviceId = NULL;
240 }
241
242 return (cr == CR_SUCCESS ? TRUE : FALSE);
243 }
244
245 BOOL
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
254 )
255 {
256 DWORD RequiredSize, Type, Size;
257 CONFIGRET cr;
258 DWORD Success;
259 HKEY hKey;
260
261 ClassName[0] = UNICODE_NULL;
262 ClassDesc[0] = UNICODE_NULL;
263 *ClassImage = -1;
264
265 /* Get the next class in the list */
266 cr = CM_Enumerate_Classes(ClassIndex,
267 ClassGuid,
268 0);
269 if (cr != CR_SUCCESS) return FALSE;
270
271 /* Get the name of the device class */
272 RequiredSize = MAX_CLASS_NAME_LEN;
273 (VOID)SetupDiClassNameFromGuidW(ClassGuid,
274 ClassName,
275 RequiredSize,
276 &RequiredSize);
277
278 /* Open the registry key for this class */
279 hKey = SetupDiOpenClassRegKeyExW(ClassGuid,
280 MAXIMUM_ALLOWED,
281 DIOCR_INSTALLER,
282 NULL,
283 0);
284 if (hKey != INVALID_HANDLE_VALUE)
285 {
286 Size = ClassDescSize;
287 Type = REG_SZ;
288
289 /* Lookup the class description (win7+) */
290 Success = RegQueryValueExW(hKey,
291 L"ClassDesc",
292 NULL,
293 &Type,
294 (LPBYTE)ClassDesc,
295 &Size);
296 if (Success == ERROR_SUCCESS)
297 {
298 /* Check if the string starts with an @ */
299 if (ClassDesc[0] == L'@')
300 {
301 /* The description is located in a module resource */
302 Success = ConvertResourceDescriptorToString(ClassDesc, ClassDescSize);
303 }
304 }
305 else if (Success == ERROR_FILE_NOT_FOUND)
306 {
307 /* WinXP stores the description in the default value */
308 Success = RegQueryValueExW(hKey,
309 NULL,
310 NULL,
311 &Type,
312 (LPBYTE)ClassDesc,
313 &Size);
314 }
315
316 /* Close the registry key */
317 RegCloseKey(hKey);
318 }
319 else
320 {
321 Success = GetLastError();
322 }
323
324 /* Check if we failed to get the class description */
325 if (Success != ERROR_SUCCESS)
326 {
327 /* Use the class name as the description */
328 wcscpy_s(ClassDesc, ClassDescSize, ClassName);
329 }
330
331 /* Get the image index for this class */
332 (VOID)SetupDiGetClassImageIndex(&m_ImageListData,
333 ClassGuid,
334 ClassImage);
335
336 return TRUE;
337 }
338
339 BOOL
340 CDevices::EnumDevicesForClass(
341 _Inout_opt_ LPHANDLE DeviceHandle,
342 _In_ LPCGUID ClassGuid,
343 _In_ DWORD Index,
344 _Out_ LPBOOL MoreItems,
345 _Out_ LPTSTR DeviceName,
346 _In_ DWORD DeviceNameSize,
347 _Outptr_ LPTSTR *DeviceId,
348 _Out_ PULONG Status,
349 _Out_ PULONG ProblemNumber
350 )
351 {
352 SP_DEVINFO_DATA DeviceInfoData;
353 DWORD DevIdSize;
354 HDEVINFO hDevInfo;
355 BOOL bUnknown, bSuccess;
356
357 *MoreItems = FALSE;
358 *DeviceName = NULL;
359 *DeviceId = NULL;
360 bUnknown = FALSE;
361
362 /* The unknown class is a special case */
363 if (IsEqualGUID(*ClassGuid, GUID_DEVCLASS_UNKNOWN))
364 bUnknown = TRUE;
365
366
367 /* Check if this is the first call for this class */
368 if (*DeviceHandle == NULL)
369 {
370 ATLASSERT(Index == 0);
371
372 /* Check for the special case */
373 if (bUnknown == TRUE)
374 {
375 /* Get device info for all devices for all classes */
376 hDevInfo = SetupDiGetClassDevsW(NULL,
377 NULL,
378 NULL,
379 DIGCF_ALLCLASSES);
380 if (hDevInfo == INVALID_HANDLE_VALUE)
381 return FALSE;
382 }
383 else
384 {
385 /* We only want the devices for this class */
386 hDevInfo = SetupDiGetClassDevsW(ClassGuid,
387 NULL,
388 NULL,
389 DIGCF_PRESENT);
390 if (hDevInfo == INVALID_HANDLE_VALUE)
391 return FALSE;
392 }
393
394 /* Store the handle for the next call */
395 *DeviceHandle = (HANDLE)hDevInfo;
396 }
397 else
398 {
399 hDevInfo = (HDEVINFO)*DeviceHandle;
400 }
401
402
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,
407 Index,
408 &DeviceInfoData);
409 if (bSuccess == FALSE) goto Quit;
410
411
412
413 /* We found a device, let the caller know they might be more */
414 *MoreItems = TRUE;
415
416 /* Check if this is the unknown class */
417 if (bUnknown)
418 {
419 /* We're looking for devices without guids */
420 if (IsEqualGUID(DeviceInfoData.ClassGuid, GUID_NULL) == FALSE)
421 {
422 /* This is a known device, we aren't interested in it */
423 return FALSE;
424 }
425 }
426
427 /* Get the size required to hold the device id */
428 bSuccess = SetupDiGetDeviceInstanceIdW(hDevInfo,
429 &DeviceInfoData,
430 NULL,
431 0,
432 &DevIdSize);
433 if (bSuccess == FALSE && (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
434 goto Quit;
435
436 /* Allocate some heap to hold the device string */
437 *DeviceId = (LPTSTR)HeapAlloc(GetProcessHeap(),
438 0,
439 DevIdSize * sizeof(WCHAR));
440 if (*DeviceId == NULL) goto Quit;
441
442 /* Now get the device id string */
443 bSuccess = SetupDiGetDeviceInstanceIdW(hDevInfo,
444 &DeviceInfoData,
445 *DeviceId,
446 DevIdSize,
447 NULL);
448 if (bSuccess == FALSE) goto Quit;
449
450 /* Skip the root device */
451 if (*DeviceId != NULL &&
452 wcscmp(*DeviceId, L"HTREE\\ROOT\\0") == 0)
453 {
454 bSuccess = FALSE;
455 goto Quit;
456 }
457
458
459 /* Get the current status of the device */
460 bSuccess = GetDeviceStatus(*DeviceId, Status, ProblemNumber);
461 if (bSuccess == FALSE) goto Quit;
462
463
464 /* Get the device's friendly name */
465 bSuccess = SetupDiGetDeviceRegistryPropertyW(hDevInfo,
466 &DeviceInfoData,
467 SPDRP_FRIENDLYNAME,
468 0,
469 (BYTE*)DeviceName,
470 256,
471 NULL);
472 if (bSuccess == FALSE)
473 {
474 /* if the friendly name fails, try the description instead */
475 bSuccess = SetupDiGetDeviceRegistryPropertyW(hDevInfo,
476 &DeviceInfoData,
477 SPDRP_DEVICEDESC,
478 0,
479 (BYTE*)DeviceName,
480 256,
481 NULL);
482 }
483
484 /* If we didn't find a name, check if this is an unknown device */
485 if (bSuccess == FALSE && bUnknown == TRUE)
486 {
487 /* We add in our own text */
488 wcscpy_s(DeviceName, 256, L"Unknown device");
489 bSuccess = TRUE;
490 }
491
492 Quit:
493 if (MoreItems == FALSE)
494 SetupDiDestroyDeviceInfoList(hDevInfo);
495
496 if (bSuccess == FALSE)
497 {
498 if (*DeviceId)
499 {
500 HeapFree(GetProcessHeap(), 0, *DeviceId);
501 *DeviceId = NULL;
502 }
503 }
504
505 return bSuccess;
506 }
507
508
509 /* PRIVATE METHODS ******************************************/
510
511 BOOL
512 CDevices::CreateRootDevice()
513 {
514 HBITMAP hRootImage = NULL;
515 DWORD Size = ROOT_NAME_SIZE;
516 BOOL bSuccess = FALSE;
517 CONFIGRET cr;
518
519 /* The root name is the computer name */
520 (VOID)GetComputerNameW(m_RootName, &Size);
521
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;
526
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,
529 hRootImage,
530 NULL);
531 if (m_RootImageIndex == -1)
532 goto Cleanup;
533
534 /* Get the root instance */
535 cr = CM_Locate_DevNodeW(&m_RootDevInst,
536 NULL,
537 CM_LOCATE_DEVNODE_NORMAL);
538 if (cr == CR_SUCCESS)
539 bSuccess = TRUE;
540
541 Cleanup:
542 if (bSuccess == FALSE)
543 {
544 SetupDiDestroyClassImageList(&m_ImageListData);
545 ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA));
546 }
547
548 if (hRootImage) DeleteObject(hRootImage);
549
550 return bSuccess;
551 }
552
553 DWORD
554 CDevices::ConvertResourceDescriptorToString(
555 _Inout_z_ LPWSTR ResourceDescriptor,
556 _In_ DWORD ResourceDescriptorSize
557 )
558 {
559 WCHAR ModulePath[MAX_PATH];
560 WCHAR ResString[256];
561 INT ResourceId;
562 HMODULE hModule;
563 LPWSTR ptr;
564 DWORD Size;
565 DWORD dwError;
566
567
568 /* First check for a semi colon */
569 ptr = wcschr(ResourceDescriptor, L';');
570 if (ptr)
571 {
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;
575 }
576 else
577 {
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;
581
582 /* Terminate the string where the comma was */
583 *ptr = UNICODE_NULL;
584
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();
589
590 /* Put the comma back and move past it */
591 *ptr = L',';
592 ptr++;
593
594 /* Load the dll */
595 hModule = LoadLibraryExW(ModulePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
596 if (hModule == NULL) return GetLastError();
597
598 /* Convert the resource id to a number */
599 ResourceId = _wtoi(ptr);
600
601 /* If the number is negative, make it positive */
602 if (ResourceId < 0) ResourceId = -ResourceId;
603
604 /* Load the string from the dll */
605 if (LoadStringW(hModule, ResourceId, ResString, 256))
606 {
607 wcscpy_s(ResourceDescriptor, ResourceDescriptorSize, ResString);
608 dwError = ERROR_SUCCESS;
609 }
610 else
611 {
612 dwError = GetLastError();
613 }
614
615 /* Free the library */
616 FreeLibrary(hModule);
617 }
618
619 return dwError;
620 }
621
622