1 /* 2 * PROJECT: ReactOS Device Manager 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: dll/win32/devmgr/devmgmt/DeviceView.cpp 5 * PURPOSE: Implements the tree view which contains the devices 6 * COPYRIGHT: Copyright 2015 Ged Murphy <gedmurphy@reactos.org> 7 */ 8 9 10 11 #include "precomp.h" 12 #include "devmgmt.h" 13 #include "DeviceView.h" 14 15 16 // DATA ********************************************/ 17 18 #define CLASS_NAME_LEN 256 19 #define CLASS_DESC_LEN 256 20 #define ROOT_NAME_SIZE MAX_COMPUTERNAME_LENGTH + 1 21 22 23 typedef VOID(WINAPI *PADDHARDWAREWIZARD)(HWND hwnd, LPWSTR lpName); 24 25 struct RefreshThreadData 26 { 27 CDeviceView *This; 28 BOOL ScanForChanges; 29 BOOL UpdateView; 30 }; 31 32 33 // PUBLIC METHODS ************************************/ 34 35 CDeviceView::CDeviceView( 36 HWND hMainWnd 37 ) : 38 m_hMainWnd(hMainWnd), 39 m_hTreeView(NULL), 40 m_hPropertyDialog(NULL), 41 m_hMenu(NULL), 42 m_ViewType(DevicesByType), 43 m_ShowHidden(false), 44 m_RootNode(NULL) 45 { 46 ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA)); 47 } 48 49 CDeviceView::~CDeviceView(void) 50 { 51 } 52 53 bool 54 CDeviceView::Initialize() 55 { 56 // Get the device image list 57 m_ImageListData.cbSize = sizeof(SP_CLASSIMAGELIST_DATA); 58 BOOL bSuccess = SetupDiGetClassImageList(&m_ImageListData); 59 if (bSuccess == FALSE) 60 return false; 61 62 // Create the main treeview 63 m_hTreeView = CreateWindowExW(WS_EX_CLIENTEDGE, 64 WC_TREEVIEW, 65 NULL, 66 WS_CHILD | WS_VISIBLE | WS_BORDER | TVS_HASLINES | 67 TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_LINESATROOT, 68 0, 0, 0, 0, 69 m_hMainWnd, 70 (HMENU)IDC_TREEVIEW, 71 g_hThisInstance, 72 NULL); 73 if (m_hTreeView) 74 { 75 // Set the image list against the treeview 76 (void)TreeView_SetImageList(m_hTreeView, 77 m_ImageListData.ImageList, 78 TVSIL_NORMAL); 79 80 // Give the treeview arrows instead of +/- boxes (on Win7) 81 SetWindowTheme(m_hTreeView, L"explorer", NULL); 82 83 // Create the root node 84 m_RootNode = new CRootNode(&m_ImageListData); 85 m_RootNode->SetupNode(); 86 } 87 88 89 90 return !!(m_hTreeView); 91 } 92 93 bool 94 CDeviceView::Uninitialize() 95 { 96 EmptyDeviceView(); 97 98 if (m_ImageListData.ImageList != NULL) 99 { 100 SetupDiDestroyClassImageList(&m_ImageListData); 101 ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA)); 102 } 103 104 return true; 105 } 106 107 LRESULT 108 CDeviceView::OnSize( 109 _In_ int x, 110 _In_ int y, 111 _In_ int cx, 112 _In_ int cy 113 ) 114 { 115 // Resize the treeview 116 SetWindowPos(m_hTreeView, 117 NULL, 118 x, 119 y, 120 cx, 121 cy, 122 SWP_NOZORDER); 123 124 return 0; 125 } 126 127 LRESULT 128 CDeviceView::OnRightClick( 129 _In_ LPNMHDR NmHdr 130 ) 131 { 132 TVHITTESTINFO hitInfo; 133 HTREEITEM hItem; 134 135 GetCursorPos(&hitInfo.pt); 136 ScreenToClient(m_hTreeView, &hitInfo.pt); 137 138 hItem = TreeView_HitTest(m_hTreeView, &hitInfo); 139 if (hItem != NULL && (hitInfo.flags & (TVHT_ONITEM | TVHT_ONITEMICON))) 140 { 141 TreeView_SelectItem(m_hTreeView, hItem); 142 } 143 144 return 0; 145 } 146 147 LRESULT 148 CDeviceView::OnContextMenu( 149 _In_ LPARAM lParam 150 ) 151 { 152 HTREEITEM hSelected = TreeView_GetSelection(m_hTreeView); 153 154 RECT rc; 155 if (TreeView_GetItemRect(m_hTreeView, 156 hSelected, 157 &rc, 158 TRUE)) 159 { 160 POINT pt; 161 if (GetCursorPos(&pt) && 162 ScreenToClient(m_hTreeView, &pt) && 163 PtInRect(&rc, pt)) 164 { 165 CNode *Node = GetSelectedNode(); 166 if (Node) 167 { 168 // Create the context menu 169 HMENU hContextMenu = CreatePopupMenu(); 170 171 // Add the actions for this node 172 BuildActionMenuForNode(hContextMenu, Node, false); 173 174 INT xPos = GET_X_LPARAM(lParam); 175 INT yPos = GET_Y_LPARAM(lParam); 176 177 // Display the menu 178 TrackPopupMenuEx(hContextMenu, 179 TPM_RIGHTBUTTON, 180 xPos, 181 yPos, 182 m_hMainWnd, 183 NULL); 184 185 DestroyMenu(hContextMenu); 186 } 187 } 188 } 189 190 return 0; 191 } 192 193 194 void 195 CDeviceView::Refresh( 196 _In_ ViewType Type, 197 _In_ bool ScanForChanges, 198 _In_ bool UpdateView 199 ) 200 { 201 // Enum devices on a separate thread to keep the gui responsive 202 203 m_ViewType = Type; 204 205 RefreshThreadData *ThreadData; 206 ThreadData = new RefreshThreadData; 207 ThreadData->This = this; 208 ThreadData->ScanForChanges = ScanForChanges; 209 ThreadData->UpdateView = UpdateView; 210 211 HANDLE hThread; 212 hThread = (HANDLE)_beginthreadex(NULL, 213 0, 214 RefreshThread, 215 ThreadData, 216 0, 217 NULL); 218 if (hThread) CloseHandle(hThread); 219 } 220 221 LRESULT 222 CDeviceView::OnAction( 223 _In_ UINT Action 224 ) 225 { 226 switch (Action) 227 { 228 case IDC_PROPERTIES: 229 { 230 DisplayPropertySheet(); 231 break; 232 } 233 234 case IDC_SCAN_HARDWARE: 235 { 236 Refresh(GetCurrentView(), 237 true, 238 true); 239 break; 240 } 241 242 case IDC_ENABLE_DRV: 243 { 244 bool NeedsReboot; 245 if (EnableSelectedDevice(true, NeedsReboot) && 246 NeedsReboot) 247 { 248 MessageBox(m_hMainWnd, L"Rebooting", L"Enable", MB_OK); 249 } 250 break; 251 } 252 253 case IDC_DISABLE_DRV: 254 { 255 bool NeedsReboot; 256 EnableSelectedDevice(false, NeedsReboot); 257 break; 258 } 259 260 case IDC_UPDATE_DRV: 261 { 262 bool NeedsReboot; 263 UpdateSelectedDevice(NeedsReboot); 264 break; 265 } 266 267 case IDC_UNINSTALL_DRV: 268 { 269 UninstallSelectedDevice(); 270 break; 271 } 272 273 case IDC_ADD_HARDWARE: 274 { 275 RunAddHardwareWizard(); 276 break; 277 } 278 } 279 280 return 0; 281 } 282 283 void 284 CDeviceView::DisplayPropertySheet() 285 { 286 CNode *Node = GetSelectedNode(); 287 if (Node && Node->HasProperties()) 288 { 289 DevicePropertiesExW(m_hTreeView, 290 NULL, 291 Node->GetDeviceId(), 292 1,//DPF_EXTENDED, 293 FALSE); 294 } 295 } 296 297 void 298 CDeviceView::SetFocus() 299 { 300 ::SetFocus(m_hTreeView); 301 } 302 303 bool 304 CDeviceView::CreateActionMenu( 305 _In_ HMENU OwnerMenu, 306 _In_ bool MainMenu 307 ) 308 { 309 CNode *Node = GetSelectedNode(); 310 if (Node) 311 { 312 BuildActionMenuForNode(OwnerMenu, Node, MainMenu); 313 return true; 314 } 315 316 return false; 317 } 318 319 CNode* 320 CDeviceView::GetSelectedNode() 321 { 322 TV_ITEM TvItem; 323 TvItem.hItem = TreeView_GetSelection(m_hTreeView); 324 return GetNode(&TvItem); 325 } 326 327 328 329 // PRIVATE METHODS *******************************************/ 330 331 bool 332 CDeviceView::AddRootDevice() 333 { 334 m_hTreeRoot = InsertIntoTreeView(NULL, m_RootNode); 335 return (m_hTreeRoot != NULL); 336 } 337 338 bool 339 CDeviceView::GetNextClass( 340 _In_ ULONG ClassIndex, 341 _Out_ LPGUID ClassGuid, 342 _Out_ HDEVINFO *hDevInfo 343 ) 344 { 345 CONFIGRET cr; 346 347 // Get the next class in the list 348 cr = CM_Enumerate_Classes(ClassIndex, 349 ClassGuid, 350 0); 351 if (cr != CR_SUCCESS) 352 return false; 353 354 // Check if this is the unknown class 355 if (IsEqualGUID(*ClassGuid, GUID_DEVCLASS_UNKNOWN)) 356 { 357 // Get device info for all devices 358 *hDevInfo = SetupDiGetClassDevsW(NULL, 359 NULL, 360 NULL, 361 DIGCF_ALLCLASSES); 362 } 363 else 364 { 365 // We only want the devices for this class 366 *hDevInfo = SetupDiGetClassDevsW(ClassGuid, 367 NULL, 368 NULL, 369 DIGCF_PRESENT); 370 } 371 372 return (hDevInfo != INVALID_HANDLE_VALUE); 373 } 374 375 unsigned int __stdcall CDeviceView::RefreshThread(void *Param) 376 { 377 RefreshThreadData *ThreadData = (RefreshThreadData *)Param; 378 CDeviceView *This = ThreadData->This; 379 380 // Get a copy of the currently selected node 381 CNode *LastSelectedNode = This->GetSelectedNode(); 382 if (LastSelectedNode == nullptr || (LastSelectedNode->GetNodeType() == RootNode)) 383 { 384 LastSelectedNode = new CRootNode(*This->m_RootNode); 385 } 386 else if (LastSelectedNode->GetNodeType() == ClassNode) 387 { 388 LastSelectedNode = new CClassNode(*dynamic_cast<CClassNode *>(LastSelectedNode)); 389 } 390 else if (LastSelectedNode->GetNodeType() == DeviceNode) 391 { 392 LastSelectedNode = new CDeviceNode(*dynamic_cast<CDeviceNode *>(LastSelectedNode)); 393 } 394 395 // Empty the treeview 396 This->EmptyDeviceView(); 397 398 // Re-add the root node to the tree 399 if (This->AddRootDevice() == false) 400 return 0; 401 402 // Refresh the devices only if requested 403 if (ThreadData->ScanForChanges) 404 { 405 This->RefreshDeviceList(); 406 } 407 408 // display the type of view the user wants 409 switch (This->m_ViewType) 410 { 411 case DevicesByType: 412 (void)This->ListDevicesByType(); 413 break; 414 415 case DevicesByConnection: 416 (VOID)This->ListDevicesByConnection(); 417 break; 418 419 case ResourcesByType: 420 break; 421 422 case ResourcesByConnection: 423 break; 424 } 425 426 This->SelectNode(LastSelectedNode); 427 428 delete ThreadData; 429 430 return 0; 431 } 432 433 434 bool 435 CDeviceView::ListDevicesByType() 436 { 437 CClassNode *ClassNode; 438 CDeviceNode *DeviceNode; 439 HDEVINFO hDevInfo; 440 HTREEITEM hTreeItem = NULL; 441 GUID ClassGuid; 442 INT ClassIndex; 443 BOOL bClassSuccess, bSuccess; 444 445 ClassIndex = 0; 446 do 447 { 448 // Loop through all the device classes 449 bClassSuccess = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo); 450 if (bClassSuccess) 451 { 452 bool bClassUnknown = false; 453 bool AddedParent = false; 454 INT DeviceIndex = 0; 455 bool MoreItems = false; 456 457 // Get the cached class node 458 ClassNode = GetClassNode(&ClassGuid); 459 if (ClassNode == nullptr) 460 { 461 ATLASSERT(FALSE); 462 ClassIndex++; 463 continue; 464 } 465 466 // Set a flag is this is the (special case) unknown class 467 if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_UNKNOWN)) 468 bClassUnknown = true; 469 470 // Check if this is a hidden class 471 if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_LEGACYDRIVER) || 472 IsEqualGUID(ClassGuid, GUID_DEVCLASS_VOLUME)) 473 { 474 // Ignore this device if we aren't displaying hidden devices 475 if (m_ShowHidden == FALSE) 476 { 477 ClassIndex++; 478 continue; 479 } 480 } 481 482 do 483 { 484 // Get a handle to all the devices in this class 485 SP_DEVINFO_DATA DeviceInfoData; 486 ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA)); 487 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); 488 bSuccess = SetupDiEnumDeviceInfo(hDevInfo, 489 DeviceIndex, 490 &DeviceInfoData); 491 if (bSuccess == FALSE && GetLastError() == ERROR_NO_MORE_ITEMS) 492 MoreItems = false; 493 494 if (bSuccess) 495 { 496 MoreItems = true; 497 498 // The unknown class handle contains all devices on the system, 499 // and we're just looking for the ones with a null GUID 500 if (bClassUnknown) 501 { 502 if (IsEqualGUID(DeviceInfoData.ClassGuid, GUID_NULL) == FALSE) 503 { 504 // This is a known device, we aren't interested in it 505 DeviceIndex++; 506 continue; 507 } 508 } 509 510 // Get the cached device node 511 DeviceNode = GetDeviceNode(DeviceInfoData.DevInst); 512 if (DeviceNode == nullptr) 513 { 514 ATLASSERT(bClassUnknown == true); 515 DeviceIndex++; 516 continue; 517 } 518 519 // Check if this is a hidden device 520 if (DeviceNode->IsHidden()) 521 { 522 // Ignore this device if we aren't displaying hidden devices 523 if (m_ShowHidden == FALSE) 524 { 525 DeviceIndex++; 526 continue; 527 } 528 } 529 530 // We have a device, we need to add the parent if it hasn't yet been added 531 if (AddedParent == false) 532 { 533 // Insert the new class under the root item 534 hTreeItem = InsertIntoTreeView(m_hTreeRoot, 535 ClassNode); 536 AddedParent = true; 537 } 538 539 // Add the device under the class item node 540 (void)InsertIntoTreeView(hTreeItem, DeviceNode); 541 542 // Expand the class if it has a problem device 543 if (DeviceNode->HasProblem()) 544 { 545 (void)TreeView_Expand(m_hTreeView, 546 hTreeItem, 547 TVE_EXPAND); 548 } 549 } 550 551 DeviceIndex++; 552 553 } while (MoreItems); 554 555 // If this class has devices, sort them alphabetically 556 if (AddedParent == true) 557 { 558 (void)TreeView_SortChildren(m_hTreeView, 559 hTreeItem, 560 0); 561 } 562 } 563 564 ClassIndex++; 565 566 } while (bClassSuccess); 567 568 // Sort the classes alphabetically 569 (void)TreeView_SortChildren(m_hTreeView, 570 m_hTreeRoot, 571 0); 572 573 // Expand the root item 574 (void)TreeView_Expand(m_hTreeView, 575 m_hTreeRoot, 576 TVE_EXPAND); 577 578 // Pre-select the root item 579 (VOID)TreeView_SelectItem(m_hTreeView, 580 m_hTreeRoot); 581 582 return 0; 583 } 584 585 bool 586 CDeviceView::ListDevicesByConnection() 587 { 588 // Walk the device tree and add all the devices 589 (void)RecurseChildDevices(m_RootNode->GetDeviceInst(), m_hTreeRoot); 590 591 // Expand the root item 592 (void)TreeView_Expand(m_hTreeView, 593 m_hTreeRoot, 594 TVE_EXPAND); 595 596 return true; 597 } 598 599 bool 600 CDeviceView::RecurseChildDevices( 601 _In_ DEVINST ParentDevice, 602 _In_ HTREEITEM hParentTreeItem 603 ) 604 { 605 HTREEITEM hDevItem = NULL; 606 DEVINST Device; 607 bool HasProblem = false; 608 bool bSuccess; 609 610 // Check if the parent has any child devices 611 if (GetChildDevice(ParentDevice, &Device) == FALSE) 612 return true; 613 614 // Get the cached device node 615 CDeviceNode *DeviceNode; 616 DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device)); 617 if (DeviceNode == nullptr) 618 { 619 ATLASSERT(FALSE); 620 return false; 621 } 622 623 // Don't show hidden devices if not requested 624 if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden()))) 625 { 626 // Add this device to the tree under its parent 627 hDevItem = InsertIntoTreeView(hParentTreeItem, 628 DeviceNode); 629 if (hDevItem) 630 { 631 // Check if this child has any children itself 632 if (!RecurseChildDevices(Device, hDevItem)) 633 HasProblem = true; 634 } 635 636 if (DeviceNode->HasProblem()) 637 { 638 HasProblem = true; 639 } 640 } 641 642 643 // Check for siblings 644 for (;;) 645 { 646 // Check if the parent device has anything at the same level 647 bSuccess = GetSiblingDevice(Device, &Device); 648 if (bSuccess == FALSE) 649 break; 650 651 DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device)); 652 if (DeviceNode == nullptr) 653 { 654 ATLASSERT(FALSE); 655 } 656 657 // Don't show hidden devices if not requested 658 if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden()))) 659 { 660 if (DeviceNode->HasProblem()) 661 { 662 HasProblem = true; 663 } 664 665 // Add this device to the tree under its parent 666 hDevItem = InsertIntoTreeView(hParentTreeItem, 667 DeviceNode); 668 if (hDevItem) 669 { 670 // Check if this child has any children itself 671 if (!RecurseChildDevices(Device, hDevItem)) 672 HasProblem = true; 673 } 674 } 675 } 676 677 (void)TreeView_SortChildren(m_hTreeView, 678 hParentTreeItem, 679 0); 680 681 // Expand the class if it has a problem device 682 if (HasProblem == true) 683 { 684 (void)TreeView_Expand(m_hTreeView, 685 hParentTreeItem, 686 TVE_EXPAND); 687 } 688 689 // If there was a problem, expand the ancestors 690 if (HasProblem) 691 return false; 692 693 return true; 694 } 695 696 bool 697 CDeviceView::EnableSelectedDevice( 698 _In_ bool Enable, 699 _Out_ bool &NeedsReboot 700 ) 701 { 702 CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode()); 703 if (Node == nullptr) 704 return false; 705 706 if (Enable == false) 707 { 708 CAtlStringW str; 709 if (str.LoadStringW(g_hThisInstance, IDS_CONFIRM_DISABLE)) 710 { 711 if (MessageBoxW(m_hMainWnd, 712 str, 713 Node->GetDisplayName(), 714 MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES) 715 { 716 return false; 717 } 718 } 719 } 720 721 return Node->EnableDevice(Enable, NeedsReboot); 722 } 723 724 bool 725 CDeviceView::UpdateSelectedDevice( 726 _Out_ bool &NeedsReboot 727 ) 728 { 729 CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode()); 730 if (Node == nullptr) 731 return false; 732 733 DWORD dwReboot; 734 if (InstallDevInst(m_hMainWnd, Node->GetDeviceId(), TRUE, &dwReboot)) 735 { 736 NeedsReboot = false; 737 return true; 738 } 739 740 return false; 741 } 742 743 bool 744 CDeviceView::UninstallSelectedDevice( 745 ) 746 { 747 CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode()); 748 if (Node == nullptr) 749 return false; 750 751 CAtlStringW str; 752 if (str.LoadStringW(g_hThisInstance, IDS_CONFIRM_UNINSTALL)) 753 { 754 if (MessageBoxW(m_hMainWnd, 755 str, 756 Node->GetDisplayName(), 757 MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES) 758 { 759 return false; 760 } 761 } 762 763 return Node->UninstallDevice(); 764 } 765 766 bool 767 CDeviceView::RunAddHardwareWizard() 768 { 769 PADDHARDWAREWIZARD pAddHardwareWizard; 770 HMODULE hModule; 771 772 hModule = LoadLibraryW(L"hdwwiz.cpl"); 773 if (hModule == NULL) 774 return false; 775 776 pAddHardwareWizard = (PADDHARDWAREWIZARD)GetProcAddress(hModule, 777 "AddHardwareWizard"); 778 if (pAddHardwareWizard == NULL) 779 { 780 FreeLibrary(hModule); 781 return false; 782 } 783 784 pAddHardwareWizard(m_hMainWnd, NULL); 785 786 FreeLibrary(hModule); 787 return true; 788 } 789 790 bool 791 CDeviceView::GetChildDevice( 792 _In_ DEVINST ParentDevInst, 793 _Out_ PDEVINST DevInst 794 ) 795 { 796 CONFIGRET cr; 797 cr = CM_Get_Child(DevInst, 798 ParentDevInst, 799 0); 800 return (cr == CR_SUCCESS); 801 } 802 803 bool 804 CDeviceView::GetSiblingDevice( 805 _In_ DEVINST PrevDevice, 806 _Out_ PDEVINST DevInst 807 ) 808 { 809 CONFIGRET cr; 810 cr = CM_Get_Sibling(DevInst, 811 PrevDevice, 812 0); 813 return (cr == CR_SUCCESS); 814 } 815 816 HTREEITEM 817 CDeviceView::InsertIntoTreeView( 818 _In_opt_ HTREEITEM hParent, 819 _In_ CNode *Node 820 ) 821 { 822 LPWSTR lpLabel; 823 lpLabel = Node->GetDisplayName(); 824 825 TV_ITEMW tvi; 826 TV_INSERTSTRUCT tvins; 827 ZeroMemory(&tvi, sizeof(tvi)); 828 ZeroMemory(&tvins, sizeof(tvins)); 829 830 tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE; 831 tvi.pszText = lpLabel; 832 tvi.cchTextMax = wcslen(lpLabel); 833 tvi.lParam = (LPARAM)Node; 834 tvi.iImage = Node->GetClassImage(); 835 tvi.iSelectedImage = Node->GetClassImage(); 836 837 // try to cast it to a device node. This will only succeed if it's the correct type 838 CDeviceNode *DeviceNode = dynamic_cast<CDeviceNode *>(Node); 839 if (DeviceNode && DeviceNode->GetOverlayImage()) 840 { 841 tvi.mask |= TVIF_STATE; 842 tvi.stateMask = TVIS_OVERLAYMASK; 843 tvi.state = INDEXTOOVERLAYMASK(DeviceNode->GetOverlayImage()); 844 } 845 846 tvins.item = tvi; 847 tvins.hParent = hParent; 848 849 return TreeView_InsertItem(m_hTreeView, &tvins); 850 } 851 852 void 853 CDeviceView::BuildActionMenuForNode( 854 _In_ HMENU OwnerMenu, 855 _In_ CNode *Node, 856 _In_ bool MainMenu 857 ) 858 { 859 // Create a separator structure 860 MENUITEMINFOW MenuSeparator = { 0 }; 861 MenuSeparator.cbSize = sizeof(MENUITEMINFOW); 862 MenuSeparator.fType = MFT_SEPARATOR; 863 864 // Setup the 865 MENUITEMINFOW MenuItemInfo = { 0 }; 866 MenuItemInfo.cbSize = sizeof(MENUITEMINFOW); 867 MenuItemInfo.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA | MIIM_SUBMENU; 868 MenuItemInfo.fType = MFT_STRING; 869 870 CAtlStringW String; 871 int i = 0; 872 873 // Device nodes have extra data 874 if (Node->GetNodeType() == DeviceNode) 875 { 876 CDeviceNode *DeviceNode = dynamic_cast<CDeviceNode *>(Node); 877 878 if (DeviceNode->CanUpdate()) 879 { 880 String.LoadStringW(g_hThisInstance, IDS_MENU_UPDATE); 881 MenuItemInfo.wID = IDC_UPDATE_DRV; 882 MenuItemInfo.dwTypeData = String.GetBuffer(); 883 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo); 884 i++; 885 } 886 887 if (DeviceNode->IsDisabled()) 888 { 889 String.LoadStringW(g_hThisInstance, IDS_MENU_ENABLE); 890 MenuItemInfo.wID = IDC_ENABLE_DRV; 891 MenuItemInfo.dwTypeData = String.GetBuffer(); 892 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo); 893 i++; 894 } 895 896 if (DeviceNode->CanDisable() && !DeviceNode->IsDisabled()) 897 { 898 String.LoadStringW(g_hThisInstance, IDS_MENU_DISABLE); 899 MenuItemInfo.wID = IDC_DISABLE_DRV; 900 MenuItemInfo.dwTypeData = String.GetBuffer(); 901 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo); 902 i++; 903 } 904 905 if (DeviceNode->CanUninstall()) 906 { 907 String.LoadStringW(g_hThisInstance, IDS_MENU_UNINSTALL); 908 MenuItemInfo.wID = IDC_UNINSTALL_DRV; 909 MenuItemInfo.dwTypeData = String.GetBuffer(); 910 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo); 911 i++; 912 } 913 914 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuSeparator); 915 i++; 916 } 917 918 // All nodes have the scan option 919 String.LoadStringW(g_hThisInstance, IDS_MENU_SCAN); 920 MenuItemInfo.wID = IDC_SCAN_HARDWARE; 921 MenuItemInfo.dwTypeData = String.GetBuffer(); 922 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo); 923 i++; 924 925 if ((Node->GetNodeType() == RootNode) || (MainMenu == true)) 926 { 927 String.LoadStringW(g_hThisInstance, IDS_MENU_ADD); 928 MenuItemInfo.wID = IDC_ADD_HARDWARE; 929 MenuItemInfo.dwTypeData = String.GetBuffer(); 930 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo); 931 i++; 932 } 933 934 if (Node->HasProperties()) 935 { 936 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuSeparator); 937 i++; 938 939 String.LoadStringW(g_hThisInstance, IDS_MENU_PROPERTIES); 940 MenuItemInfo.wID = IDC_PROPERTIES; 941 MenuItemInfo.dwTypeData = String.GetBuffer(); 942 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo); 943 i++; 944 945 SetMenuDefaultItem(OwnerMenu, IDC_PROPERTIES, FALSE); 946 } 947 } 948 949 HTREEITEM 950 CDeviceView::RecurseFindDevice( 951 _In_ HTREEITEM hParentItem, 952 _In_ CNode *Node 953 ) 954 { 955 HTREEITEM FoundItem; 956 HTREEITEM hItem; 957 TVITEMW tvItem; 958 CNode *FoundNode; 959 960 // Check if this node has any children 961 hItem = TreeView_GetChild(m_hTreeView, hParentItem); 962 if (hItem == NULL) 963 return NULL; 964 965 // The lParam contains the node pointer data 966 tvItem.hItem = hItem; 967 tvItem.mask = TVIF_PARAM; 968 if (TreeView_GetItem(m_hTreeView, &tvItem) && 969 tvItem.lParam != NULL) 970 { 971 // check for a matching node 972 FoundNode = reinterpret_cast<CNode *>(tvItem.lParam); 973 if ((FoundNode->GetNodeType() == Node->GetNodeType()) && 974 (IsEqualGUID(*FoundNode->GetClassGuid(), *Node->GetClassGuid()))) 975 { 976 // check if this is a class node, or a device with matching ID's 977 if ((FoundNode->GetNodeType() == ClassNode) || 978 (wcscmp(FoundNode->GetDeviceId(), Node->GetDeviceId()) == 0)) 979 { 980 return hItem; 981 } 982 } 983 } 984 985 // This node may have its own children 986 FoundItem = RecurseFindDevice(hItem, Node); 987 if (FoundItem) 988 return FoundItem; 989 990 // Loop all the siblings 991 for (;;) 992 { 993 // Get the next item at this level 994 hItem = TreeView_GetNextSibling(m_hTreeView, hItem); 995 if (hItem == NULL) 996 break; 997 998 // The lParam contains the node pointer data 999 tvItem.hItem = hItem; 1000 tvItem.mask = TVIF_PARAM; 1001 if (TreeView_GetItem(m_hTreeView, &tvItem)) 1002 { 1003 // check for a matching class 1004 FoundNode = reinterpret_cast<CNode *>(tvItem.lParam); 1005 if ((FoundNode->GetNodeType() == Node->GetNodeType()) && 1006 (IsEqualGUID(*FoundNode->GetClassGuid(), *Node->GetClassGuid()))) 1007 { 1008 // check if this is a class node, or a device with matching ID's 1009 if ((FoundNode->GetNodeType() == ClassNode) || 1010 (wcscmp(FoundNode->GetDeviceId(), Node->GetDeviceId()) == 0)) 1011 { 1012 return hItem; 1013 } 1014 } 1015 } 1016 1017 // This node may have its own children 1018 FoundItem = RecurseFindDevice(hItem, Node); 1019 if (FoundItem) 1020 return FoundItem; 1021 } 1022 1023 return hItem; 1024 } 1025 1026 void 1027 CDeviceView::SelectNode( 1028 _In_ CNode *Node 1029 ) 1030 { 1031 HTREEITEM hRoot, hItem; 1032 1033 // Check if there are any items in the tree 1034 hRoot = TreeView_GetRoot(m_hTreeView); 1035 if (hRoot == NULL) 1036 return; 1037 1038 // If we don't want to set select a node, just select root 1039 if (Node == nullptr || Node->GetNodeType() == RootNode) 1040 { 1041 TreeView_SelectItem(m_hTreeView, hRoot); 1042 return; 1043 } 1044 1045 // Scan the tree looking for the node we want 1046 hItem = RecurseFindDevice(hRoot, Node); 1047 if (hItem) 1048 { 1049 TreeView_SelectItem(m_hTreeView, hItem); 1050 } 1051 else 1052 { 1053 TreeView_SelectItem(m_hTreeView, hRoot); 1054 } 1055 } 1056 1057 1058 void 1059 CDeviceView::EmptyDeviceView() 1060 { 1061 (VOID)TreeView_DeleteAllItems(m_hTreeView); 1062 } 1063 1064 1065 CClassNode* 1066 CDeviceView::GetClassNode( 1067 _In_ LPGUID ClassGuid 1068 ) 1069 { 1070 POSITION Pos; 1071 CClassNode *Node = nullptr; 1072 1073 Pos = m_ClassNodeList.GetHeadPosition(); 1074 if (Pos == NULL) 1075 return nullptr; 1076 1077 do 1078 { 1079 Node = m_ClassNodeList.GetNext(Pos); 1080 if (IsEqualGUID(*Node->GetClassGuid(), *ClassGuid)) 1081 { 1082 ATLASSERT(Node->GetNodeType() == ClassNode); 1083 break; 1084 } 1085 1086 Node = nullptr; 1087 1088 } while (Pos != NULL); 1089 1090 return Node; 1091 } 1092 1093 CDeviceNode* 1094 CDeviceView::GetDeviceNode( 1095 _In_ DEVINST Device 1096 ) 1097 { 1098 POSITION Pos; 1099 CDeviceNode *Node = nullptr; 1100 1101 Pos = m_DeviceNodeList.GetHeadPosition(); 1102 if (Pos == NULL) 1103 return nullptr; 1104 1105 do 1106 { 1107 Node = m_DeviceNodeList.GetNext(Pos); 1108 if (Node->GetDeviceInst() == Device) 1109 { 1110 ATLASSERT(Node->GetNodeType() == DeviceNode); 1111 break; 1112 } 1113 1114 Node = nullptr; 1115 1116 } while (Pos != NULL); 1117 1118 return Node; 1119 } 1120 1121 CNode* CDeviceView::GetNode( 1122 _In_ LPTV_ITEMW TvItem 1123 ) 1124 { 1125 TvItem->mask = TVIF_PARAM; 1126 if (TreeView_GetItem(m_hTreeView, TvItem)) 1127 { 1128 return (CNode *)TvItem->lParam; 1129 } 1130 return nullptr; 1131 } 1132 1133 void 1134 CDeviceView::EmptyLists() 1135 { 1136 CNode *Node; 1137 1138 while (!m_ClassNodeList.IsEmpty()) 1139 { 1140 Node = m_ClassNodeList.RemoveTail(); 1141 delete Node; 1142 } 1143 1144 while (!m_DeviceNodeList.IsEmpty()) 1145 { 1146 Node = m_DeviceNodeList.RemoveTail(); 1147 delete Node; 1148 } 1149 } 1150 1151 bool 1152 CDeviceView::RefreshDeviceList() 1153 { 1154 GUID ClassGuid; 1155 CClassNode *ClassNode; 1156 CDeviceNode *DeviceNode; 1157 HDEVINFO hDevInfo; 1158 SP_DEVINFO_DATA DeviceInfoData; 1159 DWORD i; 1160 BOOL Success; 1161 1162 ULONG ClassIndex = 0; 1163 1164 EmptyLists(); 1165 1166 if (m_RootNode) delete m_RootNode; 1167 m_RootNode = new CRootNode(&m_ImageListData); 1168 m_RootNode->SetupNode(); 1169 1170 // Loop through all the classes 1171 do 1172 { 1173 Success = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo); 1174 if (Success) 1175 { 1176 // Create a new class node and add it to the list 1177 ClassNode = new CClassNode(&ClassGuid, &m_ImageListData); 1178 if (ClassNode->SetupNode()) 1179 { 1180 m_ClassNodeList.AddTail(ClassNode); 1181 } 1182 1183 SetupDiDestroyDeviceInfoList(hDevInfo); 1184 } 1185 ClassIndex++; 1186 } while (Success); 1187 1188 // Get all the devices on the local machine 1189 hDevInfo = SetupDiGetClassDevsW(NULL, 1190 0, 1191 0, 1192 DIGCF_PRESENT | DIGCF_ALLCLASSES); 1193 if (hDevInfo == INVALID_HANDLE_VALUE) 1194 { 1195 return false; 1196 } 1197 1198 // loop though all the devices 1199 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); 1200 for (i = 0;; i++) 1201 { 1202 // Get the devinst for this device 1203 Success = SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); 1204 if (Success == FALSE) 1205 break; 1206 1207 // create a new device node and add it to the list 1208 DeviceNode = new CDeviceNode(DeviceInfoData.DevInst, &m_ImageListData); 1209 /* FIXME: Start of Hack for CORE-5643 */ 1210 if (!DeviceNode->IsInstalled()) 1211 continue; 1212 /* FIXME: End of Hack for CORE-5643 */ 1213 1214 if (DeviceNode->SetupNode()) 1215 { 1216 m_DeviceNodeList.AddTail(DeviceNode); 1217 } 1218 else 1219 { 1220 ATLASSERT(FALSE); 1221 } 1222 } 1223 1224 SetupDiDestroyDeviceInfoList(hDevInfo); 1225 1226 return TRUE; 1227 }