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