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 
CDeviceNode(_In_opt_ DEVINST Device,_In_ PSP_CLASSIMAGELIST_DATA ImageListData)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 
CDeviceNode(_In_ const CDeviceNode & Node)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 
~CDeviceNode()46 CDeviceNode::~CDeviceNode()
47 {
48     Cleanup();
49 }
50 
51 bool
SetupNode()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 = 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 
162 bool
HasProblem()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
IsHidden()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
CanDisable()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
IsDisabled()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
IsStarted()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
IsInstalled()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
CanUninstall()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
EnableDevice(_In_ bool Enable,_Out_ bool & NeedsReboot)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
UninstallDevice()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
Cleanup()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
GetFlags()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
SetFlags(_In_ DWORD Flags,_In_ DWORD FlagsEx)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
RemoveFlags(_In_ DWORD Flags,_In_ DWORD FlagsEx)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 }
469