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