Reactos
at master 488 lines 14 kB view raw
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 15CDeviceNode::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 29CDeviceNode::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 46CDeviceNode::~CDeviceNode() 47{ 48 Cleanup(); 49} 50 51bool 52CDeviceNode::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 162bool 163CDeviceNode::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 179bool 180CDeviceNode::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 201bool 202CDeviceNode::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 218bool 219CDeviceNode::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 235bool 236CDeviceNode::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 252bool 253CDeviceNode::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 270bool 271CDeviceNode::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 289bool 290CDeviceNode::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 364bool 365CDeviceNode::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 397bool 398CDeviceNode::HasResources() 399{ 400 HKEY hKey = NULL; 401 DWORD dwError, dwSize = 0; 402 403 CStringW keyName = L"SYSTEM\\CurrentControlSet\\Enum\\"; 404 keyName += m_DeviceId; 405 keyName += L"\\Control"; 406 407 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &hKey); 408 if (dwError == ERROR_SUCCESS) 409 { 410 RegQueryValueExW(hKey, L"AllocConfig", NULL, NULL, NULL, &dwSize); 411 RegCloseKey(hKey); 412 } 413 414 return (dwSize != 0); 415} 416 417/* PRIVATE METHODS ******************************************************/ 418 419void 420CDeviceNode::Cleanup() 421{ 422 if (m_DeviceId) 423 { 424 delete[] m_DeviceId; 425 m_DeviceId = NULL; 426 } 427 if (m_hDevInfo) 428 { 429 SetupDiDestroyDeviceInfoList(m_hDevInfo); 430 m_hDevInfo = NULL; 431 } 432} 433 434DWORD 435CDeviceNode::GetFlags() 436{ 437 SP_DEVINSTALL_PARAMS DevInstallParams; 438 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); 439 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo, 440 &m_DevinfoData, 441 &DevInstallParams)) 442 { 443 return DevInstallParams.Flags; 444 } 445 return 0; 446} 447 448bool 449CDeviceNode::SetFlags( 450 _In_ DWORD Flags, 451 _In_ DWORD FlagsEx 452 ) 453{ 454 SP_DEVINSTALL_PARAMS DevInstallParams; 455 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); 456 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo, 457 &m_DevinfoData, 458 &DevInstallParams)) 459 { 460 DevInstallParams.Flags |= Flags; 461 DevInstallParams.FlagsEx |= FlagsEx; 462 return (SetupDiSetDeviceInstallParamsW(m_hDevInfo, 463 &m_DevinfoData, 464 &DevInstallParams) != 0); 465 } 466 return false; 467} 468 469bool 470CDeviceNode::RemoveFlags( 471 _In_ DWORD Flags, 472 _In_ DWORD FlagsEx 473 ) 474{ 475 SP_DEVINSTALL_PARAMS DevInstallParams; 476 DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); 477 if (SetupDiGetDeviceInstallParamsW(m_hDevInfo, 478 &m_DevinfoData, 479 &DevInstallParams)) 480 { 481 DevInstallParams.Flags &= ~Flags; 482 DevInstallParams.FlagsEx &= ~FlagsEx; 483 return (SetupDiSetDeviceInstallParamsW(m_hDevInfo, 484 &m_DevinfoData, 485 &DevInstallParams) != 0); 486 } 487 return false; 488}