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