[DEVMGR]
[reactos.git] / reactos / dll / win32 / devmgr / devmgmt / DeviceNode.cpp
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
15 CDeviceNode::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
29 CDeviceNode::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
46 CDeviceNode::~CDeviceNode()
47 {
48 Cleanup();
49 }
50
51 bool
52 CDeviceNode::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 = DISPLAY_NAME_LEN * sizeof(WCHAR);
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 = DISPLAY_NAME_LEN * sizeof(WCHAR);
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
162 bool
163 CDeviceNode::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
179 bool
180 CDeviceNode::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
201 bool
202 CDeviceNode::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
218 bool
219 CDeviceNode::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
235 bool
236 CDeviceNode::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
252 bool
253 CDeviceNode::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
270 bool
271 CDeviceNode::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
289 bool
290 CDeviceNode::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
364 bool
365 CDeviceNode::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
397 /* PRIVATE METHODS ******************************************************/
398
399 void
400 CDeviceNode::Cleanup()
401 {
402 if (m_DeviceId)
403 {
404 delete[] m_DeviceId;
405 m_DeviceId = NULL;
406 }
407 if (m_hDevInfo)
408 {
409 SetupDiDestroyDeviceInfoList(m_hDevInfo);
410 m_hDevInfo = NULL;
411 }
412 }
413
414 DWORD
415 CDeviceNode::GetFlags()
416 {
417 SP_DEVINSTALL_PARAMS DevInstallParams;
418 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
419 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo,
420 &m_DevinfoData,
421 &DevInstallParams))
422 {
423 return DevInstallParams.Flags;
424 }
425 return 0;
426 }
427
428 bool
429 CDeviceNode::SetFlags(
430 _In_ DWORD Flags,
431 _In_ DWORD FlagsEx
432 )
433 {
434 SP_DEVINSTALL_PARAMS DevInstallParams;
435 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
436 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo,
437 &m_DevinfoData,
438 &DevInstallParams))
439 {
440 DevInstallParams.Flags |= Flags;
441 DevInstallParams.FlagsEx |= FlagsEx;
442 return (SetupDiSetDeviceInstallParamsW(m_hDevInfo,
443 &m_DevinfoData,
444 &DevInstallParams) != 0);
445 }
446 return false;
447 }
448
449 bool
450 CDeviceNode::RemoveFlags(
451 _In_ DWORD Flags,
452 _In_ DWORD FlagsEx
453 )
454 {
455 SP_DEVINSTALL_PARAMS DevInstallParams;
456 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
457 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo,
458 &m_DevinfoData,
459 &DevInstallParams))
460 {
461 DevInstallParams.Flags &= ~Flags;
462 DevInstallParams.FlagsEx &= ~FlagsEx;
463 return (SetupDiSetDeviceInstallParamsW(m_hDevInfo,
464 &m_DevinfoData,
465 &DevInstallParams) != 0);
466 }
467 return false;
468 }