Reactos
1/*
2* PROJECT: ReactOS Device Manager
3* LICENSE: GPL - See COPYING in the top level directory
4* FILE: dll/win32/devmgr/devmgmt/ClassNode.cpp
5* PURPOSE: Class object for
6* COPYRIGHT: Copyright 2015 Ged Murphy <gedmurphy@reactos.org>
7*
8*/
9
10#include "precomp.h"
11#include "devmgmt.h"
12#include "DeviceNode.h"
13
14
15CDeviceNode::CDeviceNode(
16 _In_opt_ DEVINST Device,
17 _In_ PSP_CLASSIMAGELIST_DATA ImageListData
18 ) :
19 CNode(DeviceNode, ImageListData),
20 m_DevInst(Device),
21 m_hDevInfo(NULL),
22 m_Status(0),
23 m_ProblemNumber(0),
24 m_OverlayImage(0)
25{
26 ZeroMemory(&m_DevinfoData, sizeof(SP_DEVINFO_DATA));
27}
28
29CDeviceNode::CDeviceNode(
30 _In_ const CDeviceNode &Node
31 ) :
32 CNode(Node)
33{
34 m_DevInst = Node.m_DevInst;
35 m_hDevInfo = Node.m_hDevInfo;
36 m_Status = Node.m_Status;
37 m_ProblemNumber = Node.m_ProblemNumber;
38 m_OverlayImage = Node.m_OverlayImage;
39 CopyMemory(&m_DevinfoData, &Node.m_DevinfoData, sizeof(SP_DEVINFO_DATA));
40
41 size_t size = wcslen(Node.m_DeviceId) + 1;
42 m_DeviceId = new WCHAR[size];
43 StringCbCopyW(m_DeviceId, size * sizeof(WCHAR), Node.m_DeviceId);
44}
45
46CDeviceNode::~CDeviceNode()
47{
48 Cleanup();
49}
50
51bool
52CDeviceNode::SetupNode()
53{
54 WCHAR ClassGuidString[MAX_GUID_STRING_LEN];
55 ULONG ulLength;
56 CONFIGRET cr;
57
58 // Get the length of the device id string
59 cr = CM_Get_Device_ID_Size(&ulLength, m_DevInst, 0);
60 if (cr == CR_SUCCESS)
61 {
62 // We alloc heap here because this will be stored in the lParam of the TV
63 m_DeviceId = new WCHAR[ulLength + 1];
64
65 // Now get the actual device id
66 cr = CM_Get_Device_IDW(m_DevInst,
67 m_DeviceId,
68 ulLength + 1,
69 0);
70 if (cr != CR_SUCCESS || wcscmp(m_DeviceId, L"HTREE\\ROOT\\0") == 0)
71 {
72 delete[] m_DeviceId;
73 m_DeviceId = NULL;
74 }
75 }
76
77 // Make sure we got the string
78 if (m_DeviceId == NULL)
79 return false;
80
81 // Build up a handle a and devinfodata struct
82 m_hDevInfo = SetupDiCreateDeviceInfoListExW(NULL,
83 NULL,
84 NULL,
85 NULL);
86 if (m_hDevInfo != INVALID_HANDLE_VALUE)
87 {
88 m_DevinfoData.cbSize = sizeof(SP_DEVINFO_DATA);
89 SetupDiOpenDeviceInfoW(m_hDevInfo,
90 m_DeviceId,
91 NULL,
92 0,
93 &m_DevinfoData);
94 }
95
96 // Check if the device has a problem
97 if (HasProblem())
98 {
99 if (IsDisabled())
100 {
101 m_OverlayImage = OverlayDisabled;
102 }
103 else
104 {
105 m_OverlayImage = OverlayProblem;
106 }
107 }
108
109 // Get the class guid for this device
110 ulLength = MAX_GUID_STRING_LEN * sizeof(WCHAR);
111 cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst,
112 CM_DRP_CLASSGUID,
113 NULL,
114 ClassGuidString,
115 &ulLength,
116 0);
117 if (cr == CR_SUCCESS)
118 {
119 // Convert the string to a proper guid
120 CLSIDFromString(ClassGuidString, &m_ClassGuid);
121 }
122 else
123 {
124 // It's a device with no driver
125 m_ClassGuid = GUID_DEVCLASS_UNKNOWN;
126 }
127
128 // Get the image for the class this device is in
129 SetupDiGetClassImageIndex(m_ImageListData,
130 &m_ClassGuid,
131 &m_ClassImage);
132
133 // Get the description for the device
134 ulLength = sizeof(m_DisplayName);
135 cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst,
136 CM_DRP_FRIENDLYNAME,
137 NULL,
138 m_DisplayName,
139 &ulLength,
140 0);
141 if (cr != CR_SUCCESS)
142 {
143 ulLength = sizeof(m_DisplayName);
144 cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst,
145 CM_DRP_DEVICEDESC,
146 NULL,
147 m_DisplayName,
148 &ulLength,
149 0);
150 }
151
152 if (cr != CR_SUCCESS)
153 {
154 CAtlStringW str;
155 if (str.LoadStringW(g_hThisInstance, IDS_UNKNOWNDEVICE))
156 StringCchCopyW(m_DisplayName, MAX_PATH, str.GetBuffer());
157 }
158
159 return true;
160}
161
162bool
163CDeviceNode::HasProblem()
164{
165 CONFIGRET cr;
166 cr = CM_Get_DevNode_Status_Ex(&m_Status,
167 &m_ProblemNumber,
168 m_DevInst,
169 0,
170 NULL);
171 if (cr == CR_SUCCESS)
172 {
173 return ((m_Status & (DN_HAS_PROBLEM | DN_PRIVATE_PROBLEM)) != 0);
174 }
175
176 return false;
177}
178
179bool
180CDeviceNode::IsHidden()
181{
182 CONFIGRET cr;
183 cr = CM_Get_DevNode_Status_Ex(&m_Status,
184 &m_ProblemNumber,
185 m_DevInst,
186 0,
187 NULL);
188 if (cr == CR_SUCCESS)
189 {
190 if (m_Status & DN_NO_SHOW_IN_DM)
191 return true;
192 }
193
194 if (IsEqualGUID(*GetClassGuid(), GUID_DEVCLASS_LEGACYDRIVER) ||
195 IsEqualGUID(*GetClassGuid(), GUID_DEVCLASS_VOLUME))
196 return true;
197
198 return false;
199}
200
201bool
202CDeviceNode::CanDisable()
203{
204 CONFIGRET cr;
205 cr = CM_Get_DevNode_Status_Ex(&m_Status,
206 &m_ProblemNumber,
207 m_DevInst,
208 0,
209 NULL);
210 if (cr == CR_SUCCESS)
211 {
212 return ((m_Status & DN_DISABLEABLE) != 0);
213 }
214
215 return false;
216}
217
218bool
219CDeviceNode::IsDisabled()
220{
221 CONFIGRET cr;
222 cr = CM_Get_DevNode_Status_Ex(&m_Status,
223 &m_ProblemNumber,
224 m_DevInst,
225 0,
226 NULL);
227 if (cr == CR_SUCCESS)
228 {
229 return ((m_ProblemNumber == CM_PROB_DISABLED) || (m_ProblemNumber == CM_PROB_HARDWARE_DISABLED));
230 }
231
232 return false;
233}
234
235bool
236CDeviceNode::IsStarted()
237{
238 CONFIGRET cr;
239 cr = CM_Get_DevNode_Status_Ex(&m_Status,
240 &m_ProblemNumber,
241 m_DevInst,
242 0,
243 NULL);
244 if (cr == CR_SUCCESS)
245 {
246 return ((m_Status & DN_STARTED) != 0);
247 }
248
249 return false;
250}
251
252bool
253CDeviceNode::IsInstalled()
254{
255 CONFIGRET cr;
256 cr = CM_Get_DevNode_Status_Ex(&m_Status,
257 &m_ProblemNumber,
258 m_DevInst,
259 0,
260 NULL);
261 if (cr == CR_SUCCESS)
262 {
263 return ((m_Status & DN_HAS_PROBLEM) != 0 ||
264 (m_Status & (DN_DRIVER_LOADED | DN_STARTED)) != 0);
265 }
266
267 return false;
268}
269
270bool
271CDeviceNode::CanUninstall()
272{
273 CONFIGRET cr;
274 cr = CM_Get_DevNode_Status_Ex(&m_Status,
275 &m_ProblemNumber,
276 m_DevInst,
277 0,
278 NULL);
279 if (cr == CR_SUCCESS)
280 {
281 if ((m_Status & DN_ROOT_ENUMERATED) != 0 &&
282 (m_Status & DN_DISABLEABLE) == 0)
283 return false;
284 }
285
286 return true;
287}
288
289bool
290CDeviceNode::EnableDevice(
291 _In_ bool Enable,
292 _Out_ bool &NeedsReboot
293 )
294{
295 bool Canceled = false;
296
297 SetFlags(DI_NODI_DEFAULTACTION, 0);
298
299 SP_PROPCHANGE_PARAMS pcp;
300 pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
301 pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
302 pcp.StateChange = (Enable ? DICS_ENABLE : DICS_DISABLE);
303 pcp.HwProfile = 0;
304
305 // check both scopes to make sure we can make the change
306 for (int i = 0; i < 2; i++)
307 {
308 // Check globally first, then check config specific
309 pcp.Scope = (i == 0) ? DICS_FLAG_CONFIGGENERAL : DICS_FLAG_CONFIGSPECIFIC;
310
311 if (SetupDiSetClassInstallParamsW(m_hDevInfo,
312 &m_DevinfoData,
313 &pcp.ClassInstallHeader,
314 sizeof(SP_PROPCHANGE_PARAMS)))
315 {
316 SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,
317 m_hDevInfo,
318 &m_DevinfoData);
319 }
320
321 if (GetLastError() == ERROR_CANCELLED)
322 {
323 Canceled = true;
324 break;
325 }
326 }
327
328 if (Canceled == false)
329 {
330 pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
331 if (SetupDiSetClassInstallParamsW(m_hDevInfo,
332 &m_DevinfoData,
333 &pcp.ClassInstallHeader,
334 sizeof(SP_PROPCHANGE_PARAMS)))
335 {
336 SetupDiChangeState(m_hDevInfo, &m_DevinfoData);
337 }
338
339
340 if (Enable)
341 {
342 // config specific enabling first, then global enabling.
343 // The global appears to be the one that starts the device
344 pcp.Scope = DICS_FLAG_GLOBAL;
345 if (SetupDiSetClassInstallParamsW(m_hDevInfo,
346 &m_DevinfoData,
347 &pcp.ClassInstallHeader,
348 sizeof(SP_PROPCHANGE_PARAMS)))
349 {
350 SetupDiChangeState(m_hDevInfo, &m_DevinfoData);
351 }
352 }
353
354 SetFlags(DI_PROPERTIES_CHANGE, 0);
355
356 NeedsReboot = ((GetFlags() & (DI_NEEDRESTART | DI_NEEDREBOOT)) != 0);
357 }
358
359 RemoveFlags(DI_NODI_DEFAULTACTION, 0);
360
361 return true;
362}
363
364bool
365CDeviceNode::UninstallDevice()
366{
367 if (CanUninstall() == false)
368 return false;
369
370 SP_REMOVEDEVICE_PARAMS RemoveDevParams;
371 RemoveDevParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
372 RemoveDevParams.ClassInstallHeader.InstallFunction = DIF_REMOVE;
373 RemoveDevParams.Scope = DI_REMOVEDEVICE_GLOBAL;
374 RemoveDevParams.HwProfile = 0;
375
376 //
377 // We probably need to walk all the siblings of this
378 // device and ask if they're happy with the uninstall
379 //
380
381 // Remove it
382 SetupDiSetClassInstallParamsW(m_hDevInfo,
383 &m_DevinfoData,
384 &RemoveDevParams.ClassInstallHeader,
385 sizeof(SP_REMOVEDEVICE_PARAMS));
386 SetupDiCallClassInstaller(DIF_REMOVE, m_hDevInfo, &m_DevinfoData);
387
388 // Clear the install params
389 SetupDiSetClassInstallParamsW(m_hDevInfo,
390 &m_DevinfoData,
391 NULL,
392 0);
393
394 return true;
395}
396
397bool
398CDeviceNode::HasResources()
399{
400 HKEY hKey = NULL;
401 DWORD dwError, dwSize = 0;
402
403 CStringW keyName = L"SYSTEM\\CurrentControlSet\\Enum\\";
404 keyName += m_DeviceId;
405 keyName += L"\\Control";
406
407 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &hKey);
408 if (dwError == ERROR_SUCCESS)
409 {
410 RegQueryValueExW(hKey, L"AllocConfig", NULL, NULL, NULL, &dwSize);
411 RegCloseKey(hKey);
412 }
413
414 return (dwSize != 0);
415}
416
417/* PRIVATE METHODS ******************************************************/
418
419void
420CDeviceNode::Cleanup()
421{
422 if (m_DeviceId)
423 {
424 delete[] m_DeviceId;
425 m_DeviceId = NULL;
426 }
427 if (m_hDevInfo)
428 {
429 SetupDiDestroyDeviceInfoList(m_hDevInfo);
430 m_hDevInfo = NULL;
431 }
432}
433
434DWORD
435CDeviceNode::GetFlags()
436{
437 SP_DEVINSTALL_PARAMS DevInstallParams;
438 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
439 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo,
440 &m_DevinfoData,
441 &DevInstallParams))
442 {
443 return DevInstallParams.Flags;
444 }
445 return 0;
446}
447
448bool
449CDeviceNode::SetFlags(
450 _In_ DWORD Flags,
451 _In_ DWORD FlagsEx
452 )
453{
454 SP_DEVINSTALL_PARAMS DevInstallParams;
455 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
456 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo,
457 &m_DevinfoData,
458 &DevInstallParams))
459 {
460 DevInstallParams.Flags |= Flags;
461 DevInstallParams.FlagsEx |= FlagsEx;
462 return (SetupDiSetDeviceInstallParamsW(m_hDevInfo,
463 &m_DevinfoData,
464 &DevInstallParams) != 0);
465 }
466 return false;
467}
468
469bool
470CDeviceNode::RemoveFlags(
471 _In_ DWORD Flags,
472 _In_ DWORD FlagsEx
473 )
474{
475 SP_DEVINSTALL_PARAMS DevInstallParams;
476 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
477 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo,
478 &m_DevinfoData,
479 &DevInstallParams))
480 {
481 DevInstallParams.Flags &= ~Flags;
482 DevInstallParams.FlagsEx &= ~FlagsEx;
483 return (SetupDiSetDeviceInstallParamsW(m_hDevInfo,
484 &m_DevinfoData,
485 &DevInstallParams) != 0);
486 }
487 return false;
488}