[DEVMGR]
[reactos.git] / reactos / dll / win32 / devmgr_new / 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 {
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
120 // Get the description for the device
121 ulLength = DISPLAY_NAME_LEN * sizeof(WCHAR);
122 cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst,
123 CM_DRP_FRIENDLYNAME,
124 NULL,
125 m_DisplayName,
126 &ulLength,
127 0);
128 if (cr != CR_SUCCESS)
129 {
130 ulLength = DISPLAY_NAME_LEN * sizeof(WCHAR);
131 cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst,
132 CM_DRP_DEVICEDESC,
133 NULL,
134 m_DisplayName,
135 &ulLength,
136 0);
137
138 }
139
140 if (cr != CR_SUCCESS)
141 {
142 CAtlStringW str;
143 if (str.LoadStringW(g_hThisInstance, IDS_UNKNOWNDEVICE))
144 StringCchCopyW(m_DisplayName, MAX_PATH, str.GetBuffer());
145 }
146
147 return true;
148 }
149
150 bool
151 CDeviceNode::HasProblem()
152 {
153 CONFIGRET cr;
154 cr = CM_Get_DevNode_Status_Ex(&m_Status,
155 &m_ProblemNumber,
156 m_DevInst,
157 0,
158 NULL);
159 if (cr == CR_SUCCESS)
160 {
161 return ((m_Status & (DN_HAS_PROBLEM | DN_PRIVATE_PROBLEM)) != 0);
162 }
163
164 return false;
165 }
166
167 bool
168 CDeviceNode::IsHidden()
169 {
170 CONFIGRET cr;
171 cr = CM_Get_DevNode_Status_Ex(&m_Status,
172 &m_ProblemNumber,
173 m_DevInst,
174 0,
175 NULL);
176 if (cr == CR_SUCCESS)
177 {
178 return ((m_Status & DN_NO_SHOW_IN_DM) != 0);
179 }
180
181 return false;
182 }
183
184 bool
185 CDeviceNode::CanDisable()
186 {
187 CONFIGRET cr;
188 cr = CM_Get_DevNode_Status_Ex(&m_Status,
189 &m_ProblemNumber,
190 m_DevInst,
191 0,
192 NULL);
193 if (cr == CR_SUCCESS)
194 {
195 return ((m_Status & DN_DISABLEABLE) != 0);
196 }
197
198 return false;
199 }
200
201 bool
202 CDeviceNode::IsDisabled()
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_ProblemNumber & (CM_PROB_DISABLED | CM_PROB_HARDWARE_DISABLED)) != 0);
213 }
214
215 return false;
216 }
217
218 bool
219 CDeviceNode::IsStarted()
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_Status & DN_STARTED) != 0);
230 }
231
232 return false;
233 }
234
235 bool
236 CDeviceNode::IsInstalled()
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_HAS_PROBLEM) != 0 ||
247 (m_Status & (DN_DRIVER_LOADED | DN_STARTED)) != 0);
248 }
249
250 return false;
251 }
252
253 bool
254 CDeviceNode::CanUninstall()
255 {
256 CONFIGRET cr;
257 cr = CM_Get_DevNode_Status_Ex(&m_Status,
258 &m_ProblemNumber,
259 m_DevInst,
260 0,
261 NULL);
262 if (cr == CR_SUCCESS)
263 {
264 if ((m_Status & DN_ROOT_ENUMERATED) != 0 &&
265 (m_Status & DN_DISABLEABLE) == 0)
266 return false;
267 }
268
269 return true;
270 }
271
272 bool
273 CDeviceNode::EnableDevice(
274 _In_ bool Enable,
275 _Out_ bool &NeedsReboot
276 )
277 {
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 return true;
380
381 }
382
383 /* PRIVATE METHODS ******************************************************/
384
385 void
386 CDeviceNode::Cleanup()
387 {
388 if (m_DeviceId)
389 {
390 delete[] m_DeviceId;
391 m_DeviceId = NULL;
392 }
393 if (m_hDevInfo)
394 {
395 SetupDiDestroyDeviceInfoList(m_hDevInfo);
396 m_hDevInfo = NULL;
397 }
398 }
399
400 DWORD
401 CDeviceNode::GetFlags(
402 )
403 {
404 SP_DEVINSTALL_PARAMS DevInstallParams;
405 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
406 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo,
407 &m_DevinfoData,
408 &DevInstallParams))
409 {
410 return DevInstallParams.Flags;
411 }
412 return 0;
413 }
414
415 bool
416 CDeviceNode::SetFlags(
417 _In_ DWORD Flags,
418 _In_ DWORD FlagsEx
419 )
420 {
421 SP_DEVINSTALL_PARAMS DevInstallParams;
422 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
423 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo,
424 &m_DevinfoData,
425 &DevInstallParams))
426 {
427 DevInstallParams.Flags |= Flags;
428 DevInstallParams.FlagsEx |= FlagsEx;
429 return (SetupDiSetDeviceInstallParamsW(m_hDevInfo,
430 &m_DevinfoData,
431 &DevInstallParams) != 0);
432 }
433 return false;
434 }
435
436 bool
437 CDeviceNode::RemoveFlags(
438 _In_ DWORD Flags,
439 _In_ DWORD FlagsEx
440 )
441 {
442 SP_DEVINSTALL_PARAMS DevInstallParams;
443 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
444 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo,
445 &m_DevinfoData,
446 &DevInstallParams))
447 {
448 DevInstallParams.Flags &= ~Flags;
449 DevInstallParams.FlagsEx &= ~FlagsEx;
450 return (SetupDiSetDeviceInstallParamsW(m_hDevInfo,
451 &m_DevinfoData,
452 &DevInstallParams) != 0);
453 }
454 return false;
455 }
456
457