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