94f7732f22e88cd1d489b3c02541d20f3ce2c92e
[reactos.git] / reactos / dll / win32 / devmgr / devmgmt / Node.cpp
1 /*
2 * PROJECT: ReactOS Device Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/devmgr/devmgr/node.cpp
5 * PURPOSE: Object for each device in the tree
6 * COPYRIGHT: Copyright 2015 Ged Murphy <gedmurphy@reactos.org>
7 *
8 */
9
10 #include "stdafx.h"
11 #include "devmgmt.h"
12 #include "Node.h"
13
14
15 /* PUBLIC METHODS *******************************************/
16
17 CNode::CNode(_In_ LPGUID ClassGuid,
18 _In_ PSP_CLASSIMAGELIST_DATA ImageListData) :
19 m_ImageListData(ImageListData),
20 m_NodeType(NodeClass),
21 m_DevInst(0),
22 m_DeviceId(NULL),
23 m_ClassImage(0),
24 m_Status(0),
25 m_ProblemNumber(0),
26 m_OverlayImage(0)
27 {
28 m_DisplayName[0] = UNICODE_NULL;
29 CopyMemory(&m_ClassGuid, ClassGuid, sizeof(GUID));
30 }
31
32 CNode::CNode(_In_opt_ DEVINST Device,
33 _In_ PSP_CLASSIMAGELIST_DATA ImageListData) :
34 m_ImageListData(ImageListData),
35 m_NodeType(NodeDevice),
36 m_DevInst(Device),
37 m_DeviceId(NULL),
38 m_ClassImage(0),
39 m_Status(0),
40 m_ProblemNumber(0),
41 m_OverlayImage(0)
42 {
43 m_DisplayName[0] = UNICODE_NULL;
44 CopyMemory(&m_ClassGuid, &GUID_NULL, sizeof(GUID));
45 }
46
47 CNode::~CNode()
48 {
49 Cleanup();
50 }
51
52 bool
53 CNode::Setup()
54 {
55 // TODO: Make this polymorphic
56
57 if (m_NodeType == NodeClass)
58 {
59 return SetupClassNode();
60 }
61 else if (m_NodeType == NodeDevice)
62 {
63 return SetupDeviceNode();
64 }
65
66 return FALSE;
67 }
68
69 bool
70 CNode::HasProperties()
71 {
72 return (m_DeviceId != NULL);
73 }
74
75 bool
76 CNode::IsHidden()
77 {
78 CONFIGRET cr;
79 cr = CM_Get_DevNode_Status_Ex(&m_Status,
80 &m_ProblemNumber,
81 m_DevInst,
82 0,
83 NULL);
84 if (cr == CR_SUCCESS)
85 {
86 return ((m_Status & DN_NO_SHOW_IN_DM) != 0);
87 }
88
89 return false;
90 }
91
92 bool
93 CNode::CanDisable()
94 {
95 CONFIGRET cr;
96 cr = CM_Get_DevNode_Status_Ex(&m_Status,
97 &m_ProblemNumber,
98 m_DevInst,
99 0,
100 NULL);
101 if (cr == CR_SUCCESS)
102 {
103 return (m_NodeType == NodeDevice && ((m_Status & DN_DISABLEABLE) != 0));
104 }
105
106 return false;
107 }
108
109 bool
110 CNode::IsDisabled()
111 {
112 CONFIGRET cr;
113 cr = CM_Get_DevNode_Status_Ex(&m_Status,
114 &m_ProblemNumber,
115 m_DevInst,
116 0,
117 NULL);
118 if (cr == CR_SUCCESS)
119 {
120 return ((m_ProblemNumber & (CM_PROB_DISABLED | CM_PROB_HARDWARE_DISABLED)) != 0);
121 }
122
123 return false;
124 }
125
126 bool
127 CNode::IsStarted()
128 {
129 CONFIGRET cr;
130 cr = CM_Get_DevNode_Status_Ex(&m_Status,
131 &m_ProblemNumber,
132 m_DevInst,
133 0,
134 NULL);
135 if (cr == CR_SUCCESS)
136 {
137 return ((m_Status & DN_STARTED) != 0);
138 }
139
140 return false;
141 }
142
143 bool
144 CNode::IsInstalled()
145 {
146 CONFIGRET cr;
147 cr = CM_Get_DevNode_Status_Ex(&m_Status,
148 &m_ProblemNumber,
149 m_DevInst,
150 0,
151 NULL);
152 if (cr == CR_SUCCESS)
153 {
154 return ((m_Status & DN_HAS_PROBLEM) != 0 ||
155 (m_Status & (DN_DRIVER_LOADED | DN_STARTED)) != 0);
156 }
157
158 return false;
159 }
160
161
162 /* PRIVATE METHODS ******************************************/
163
164 bool
165 CNode::SetupClassNode()
166 {
167 DWORD RequiredSize, Type, Size;
168 DWORD Success;
169 HKEY hKey;
170
171 // Open the registry key for this class
172 hKey = SetupDiOpenClassRegKeyExW(&m_ClassGuid,
173 MAXIMUM_ALLOWED,
174 DIOCR_INSTALLER,
175 NULL,
176 0);
177 if (hKey != INVALID_HANDLE_VALUE)
178 {
179 Size = DISPLAY_NAME_LEN;
180 Type = REG_SZ;
181
182 // Lookup the class description (win7+)
183 Success = RegQueryValueExW(hKey,
184 L"ClassDesc",
185 NULL,
186 &Type,
187 (LPBYTE)m_DisplayName,
188 &Size);
189 if (Success == ERROR_SUCCESS)
190 {
191 // Check if the string starts with an @
192 if (m_DisplayName[0] == L'@')
193 {
194 // The description is located in a module resource
195 Success = ConvertResourceDescriptorToString(m_DisplayName, DISPLAY_NAME_LEN);
196 }
197 }
198 else if (Success == ERROR_FILE_NOT_FOUND)
199 {
200 // WinXP stores the description in the default value
201 Success = RegQueryValueExW(hKey,
202 NULL,
203 NULL,
204 &Type,
205 (LPBYTE)m_DisplayName,
206 &Size);
207 }
208
209 // Close the registry key
210 RegCloseKey(hKey);
211 }
212 else
213 {
214 Success = GetLastError();
215 }
216
217 // Check if we failed to get the class description
218 if (Success != ERROR_SUCCESS)
219 {
220 // Use the class name as the description
221 RequiredSize = DISPLAY_NAME_LEN;
222 (VOID)SetupDiClassNameFromGuidW(&m_ClassGuid,
223 m_DisplayName,
224 RequiredSize,
225 &RequiredSize);
226 }
227
228 // Get the image index for this class
229 (VOID)SetupDiGetClassImageIndex(m_ImageListData,
230 &m_ClassGuid,
231 &m_ClassImage);
232
233 return true;
234 }
235
236 bool
237 CNode::SetupDeviceNode()
238 {
239 WCHAR ClassGuidString[MAX_GUID_STRING_LEN];
240 ULONG ulLength;
241 CONFIGRET cr;
242
243 // ATLASSERT(m_DeviceId == NULL);
244
245 // Get the length of the device id string
246 cr = CM_Get_Device_ID_Size(&ulLength, m_DevInst, 0);
247 if (cr == CR_SUCCESS)
248 {
249 // We alloc heap here because this will be stored in the lParam of the TV
250 m_DeviceId = (LPWSTR)HeapAlloc(GetProcessHeap(),
251 0,
252 (ulLength + 1) * sizeof(WCHAR));
253 if (m_DeviceId)
254 {
255 // Now get the actual device id
256 cr = CM_Get_Device_IDW(m_DevInst,
257 m_DeviceId,
258 ulLength + 1,
259 0);
260 if (cr != CR_SUCCESS)
261 {
262 HeapFree(GetProcessHeap(), 0, m_DeviceId);
263 m_DeviceId = NULL;
264 }
265 }
266 }
267
268 // Make sure we got the string
269 if (m_DeviceId == NULL)
270 return false;
271
272 // Get the current status of the device
273 cr = CM_Get_DevNode_Status_Ex(&m_Status,
274 &m_ProblemNumber,
275 m_DevInst,
276 0,
277 NULL);
278 if (cr != CR_SUCCESS)
279 {
280 HeapFree(GetProcessHeap(), 0, m_DeviceId);
281 m_DeviceId = NULL;
282 return false;
283 }
284
285 // Check if the device has a problem
286 if (m_Status & DN_HAS_PROBLEM)
287 {
288 m_OverlayImage = 1;
289 }
290
291 // The disabled overlay takes precidence over the problem overlay
292 if (m_ProblemNumber & (CM_PROB_DISABLED | CM_PROB_HARDWARE_DISABLED))
293 {
294 m_OverlayImage = 2;
295 }
296
297
298 // Get the class guid for this device
299 ulLength = MAX_GUID_STRING_LEN * sizeof(WCHAR);
300 cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst,
301 CM_DRP_CLASSGUID,
302 NULL,
303 ClassGuidString,
304 &ulLength,
305 0);
306 if (cr == CR_SUCCESS)
307 {
308 // Convert the string to a proper guid
309 CLSIDFromString(ClassGuidString, &m_ClassGuid);
310 }
311 else
312 {
313 // It's a device with no driver
314 m_ClassGuid = GUID_DEVCLASS_UNKNOWN;
315 }
316
317
318 // Get the image for the class this device is in
319 SetupDiGetClassImageIndex(m_ImageListData,
320 &m_ClassGuid,
321 &m_ClassImage);
322
323 // Get the description for the device
324 ulLength = DISPLAY_NAME_LEN * sizeof(WCHAR);
325 cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst,
326 CM_DRP_FRIENDLYNAME,
327 NULL,
328 m_DisplayName,
329 &ulLength,
330 0);
331 if (cr != CR_SUCCESS)
332 {
333 ulLength = DISPLAY_NAME_LEN * sizeof(WCHAR);
334 cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst,
335 CM_DRP_DEVICEDESC,
336 NULL,
337 m_DisplayName,
338 &ulLength,
339 0);
340
341 }
342
343 // Cleanup if something failed
344 if (cr != CR_SUCCESS)
345 {
346 HeapFree(GetProcessHeap(), 0, m_DeviceId);
347 m_DeviceId = NULL;
348 }
349
350 return (cr == CR_SUCCESS ? true : false);
351 }
352
353 void
354 CNode::Cleanup()
355 {
356 if (m_DeviceId)
357 {
358 HeapFree(GetProcessHeap(), 0, m_DeviceId);
359 m_DeviceId = NULL;
360 }
361 }
362
363 DWORD
364 CNode::ConvertResourceDescriptorToString(
365 _Inout_z_ LPWSTR ResourceDescriptor,
366 _In_ DWORD ResourceDescriptorSize
367 )
368 {
369 WCHAR ModulePath[MAX_PATH];
370 WCHAR ResString[256];
371 INT ResourceId;
372 HMODULE hModule;
373 LPWSTR ptr;
374 DWORD Size;
375 DWORD dwError;
376
377
378 // First check for a semi colon */
379 ptr = wcschr(ResourceDescriptor, L';');
380 if (ptr)
381 {
382 // This must be an inf based descriptor, the desc is after the semi colon
383 wcscpy_s(ResourceDescriptor, ResourceDescriptorSize, ++ptr);
384 dwError = ERROR_SUCCESS;
385 }
386 else
387 {
388 // This must be a dll resource based descriptor. Find the comma
389 ptr = wcschr(ResourceDescriptor, L',');
390 if (ptr == NULL) return ERROR_INVALID_DATA;
391
392 // Terminate the string where the comma was
393 *ptr = UNICODE_NULL;
394
395 // Expand any environment strings
396 Size = ExpandEnvironmentStringsW(&ResourceDescriptor[1], ModulePath, MAX_PATH);
397 if (Size > MAX_PATH) return ERROR_BUFFER_OVERFLOW;
398 if (Size == 0) return GetLastError();
399
400 // Put the comma back and move past it
401 *ptr = L',';
402 ptr++;
403
404 // Load the dll
405 hModule = LoadLibraryExW(ModulePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
406 if (hModule == NULL) return GetLastError();
407
408 // Convert the resource id to a number
409 ResourceId = _wtoi(ptr);
410
411 // If the number is negative, make it positive
412 if (ResourceId < 0) ResourceId = -ResourceId;
413
414 // Load the string from the dll
415 if (LoadStringW(hModule, ResourceId, ResString, 256))
416 {
417 wcscpy_s(ResourceDescriptor, ResourceDescriptorSize, ResString);
418 dwError = ERROR_SUCCESS;
419 }
420 else
421 {
422 dwError = GetLastError();
423 }
424
425 // Free the library
426 FreeLibrary(hModule);
427 }
428
429 return dwError;
430 }