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 = 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 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 } 469