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