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