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>
13 #include "DeviceView.h"
16 // DATA ********************************************/
18 #define CLASS_NAME_LEN 256
19 #define CLASS_DESC_LEN 256
20 #define ROOT_NAME_SIZE MAX_COMPUTERNAME_LENGTH + 1
25 IN HWND hWndParent OPTIONAL
,
26 IN LPCWSTR lpMachineName OPTIONAL
,
27 IN LPCWSTR lpDeviceID OPTIONAL
,
28 IN DWORD dwFlags OPTIONAL
,
32 typedef INT_PTR(WINAPI
*pDevicePropertiesExW
)(HWND
,LPCWSTR
,LPCWSTR
,DWORD
,BOOL
);
34 struct RefreshThreadData
42 // PUBLIC METHODS ************************************/
44 CDeviceView::CDeviceView(
49 m_hPropertyDialog(NULL
),
52 m_ViewType(DevicesByType
),
57 ZeroMemory(&m_ImageListData
, sizeof(SP_CLASSIMAGELIST_DATA
));
60 CDeviceView::~CDeviceView(void)
65 CDeviceView::Initialize()
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;
72 // Create the main treeview
73 m_hTreeView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
76 WS_CHILD
| WS_VISIBLE
| WS_BORDER
| TVS_HASLINES
|
77 TVS_HASBUTTONS
| TVS_SHOWSELALWAYS
| TVS_LINESATROOT
,
85 // Set the image list against the treeview
86 (void)TreeView_SetImageList(m_hTreeView
,
87 m_ImageListData
.ImageList
,
90 // Give the treeview arrows instead of +/- boxes (on Win7)
91 SetWindowTheme(m_hTreeView
, L
"explorer", NULL
);
94 // Create the context menu and make properties the default item
95 m_hMenu
= LoadMenuW(g_hInstance
, MAKEINTRESOURCEW(IDR_POPUP
));
96 m_hContextMenu
= GetSubMenu(m_hMenu
, 0);
97 SetMenuDefaultItem(m_hContextMenu
, IDC_PROPERTIES
, FALSE
);
99 return !!(m_hTreeView
);
103 CDeviceView::Uninitialize()
107 if (m_ImageListData
.ImageList
!= NULL
)
109 SetupDiDestroyClassImageList(&m_ImageListData
);
110 ZeroMemory(&m_ImageListData
, sizeof(SP_CLASSIMAGELIST_DATA
));
113 DestroyMenu(m_hMenu
);
126 // Resize the treeview
127 SetWindowPos(m_hTreeView
,
139 CDeviceView::OnRightClick(
143 HTREEITEM hItem
= TreeView_GetNextItem(NmHdr
->hwndFrom
, 0, TVGN_DROPHILITE
);
146 TreeView_SelectItem(NmHdr
->hwndFrom
, hItem
);
153 CDeviceView::OnContextMenu(
157 HTREEITEM hSelected
= TreeView_GetSelection(m_hTreeView
);
160 if (TreeView_GetItemRect(m_hTreeView
,
166 if (GetCursorPos(&pt
) &&
167 ScreenToClient(m_hTreeView
, &pt
) &&
170 INT xPos
= GET_X_LPARAM(lParam
);
171 INT yPos
= GET_Y_LPARAM(lParam
);
173 TrackPopupMenuEx(m_hContextMenu
,
187 CDeviceView::Refresh(
189 _In_
bool ScanForChanges
,
193 // Enum devices on a seperate thread to keep the gui responsive
197 RefreshThreadData
*ThreadData
;
198 ThreadData
= new RefreshThreadData();
199 ThreadData
->This
= this;
200 ThreadData
->ScanForChanges
= ScanForChanges
;
201 ThreadData
->UpdateView
= UpdateView
;
204 hThread
= (HANDLE
)_beginthreadex(NULL
,
211 if (hThread
) CloseHandle(hThread
);
215 CDeviceView::DisplayPropertySheet()
218 // In ReactOS we can link to DevicePropertiesEx but
219 // not in windows as it's not part of the SDK
222 HMODULE hModule
= LoadLibraryW(L
"devmgr.dll");
223 if (hModule
== NULL
) return;
225 pDevicePropertiesExW DevicePropertiesExW
;
226 DevicePropertiesExW
= (pDevicePropertiesExW
)GetProcAddress(hModule
,
227 "DevicePropertiesExW");
228 if (DevicePropertiesExW
== NULL
)
230 FreeLibrary(hModule
);
235 CNode
*Node
= GetSelectedNode();
236 if (Node
&& Node
->HasProperties())
238 DevicePropertiesExW(m_hTreeView
,
246 FreeLibrary(hModule
);
251 CDeviceView::SetFocus()
256 CDeviceView::HasProperties(
257 _In_ LPTV_ITEMW TvItem
260 CNode
*Node
= GetNode(TvItem
);
263 return Node
->HasProperties();
269 CDeviceView::IsDisabled(
270 _In_ LPTV_ITEMW TvItem
273 CDeviceNode
*Node
= dynamic_cast<CDeviceNode
*>(GetNode(TvItem
));
276 return Node
->IsDisabled();
282 CDeviceView::CanDisable(
283 _In_ LPTV_ITEMW TvItem
286 CDeviceNode
*Node
= dynamic_cast<CDeviceNode
*>(GetNode(TvItem
));
295 // PRIVATE METHODS *******************************************/
298 CDeviceView::AddRootDevice()
300 // Check whether we've loaded the root bitmap into the imagelist (done on first run)
301 if (m_RootClassImage
== -1)
303 // Load the bitmap we'll be using as the root image
305 hRootImage
= LoadBitmapW(g_hInstance
,
306 MAKEINTRESOURCEW(IDB_ROOT_IMAGE
));
307 if (hRootImage
== NULL
) return FALSE
;
309 // Add this bitmap to the device image list. This is a bit hacky, but it's safe
310 m_RootClassImage
= ImageList_Add(m_ImageListData
.ImageList
,
313 DeleteObject(hRootImage
);
316 // Get the root instance
318 cr
= CM_Locate_DevNodeW(&m_RootDevInst
,
320 CM_LOCATE_DEVNODE_NORMAL
);
321 if (cr
!= CR_SUCCESS
)
326 // The root name is the computer name
327 WCHAR RootDeviceName
[ROOT_NAME_SIZE
];
328 DWORD Size
= ROOT_NAME_SIZE
;
329 if (GetComputerNameW(RootDeviceName
, &Size
))
330 _wcslwr_s(RootDeviceName
);
333 TV_INSERTSTRUCT tvins
;
334 ZeroMemory(&tvi
, sizeof(tvi
));
335 ZeroMemory(&tvins
, sizeof(tvins
));
337 // Insert the root / parent item into our treeview
338 tvi
.mask
= TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
339 tvi
.pszText
= RootDeviceName
;
340 tvi
.cchTextMax
= wcslen(RootDeviceName
);
341 tvi
.iImage
= m_RootClassImage
;
342 tvi
.iSelectedImage
= m_RootClassImage
;
344 m_hTreeRoot
= TreeView_InsertItem(m_hTreeView
, &tvins
);
346 return (m_hTreeRoot
!= NULL
);
351 CDeviceView::GetNextClass(_In_ ULONG ClassIndex
,
352 _Out_ LPGUID ClassGuid
,
353 _Out_ HDEVINFO
*hDevInfo
)
357 // Get the next class in the list
358 cr
= CM_Enumerate_Classes(ClassIndex
,
361 if (cr
!= CR_SUCCESS
) return false;
363 // Check for devices without a class
364 if (IsEqualGUID(*ClassGuid
, GUID_DEVCLASS_UNKNOWN
))
366 // Get device info for all devices for all classes
367 *hDevInfo
= SetupDiGetClassDevsW(NULL
,
374 // We only want the devices for this class
375 *hDevInfo
= SetupDiGetClassDevsW(ClassGuid
,
382 return (hDevInfo
!= INVALID_HANDLE_VALUE
);
385 unsigned int __stdcall
CDeviceView::RefreshThread(void *Param
)
387 RefreshThreadData
*ThreadData
= (RefreshThreadData
*)Param
;
388 CDeviceView
*This
= ThreadData
->This
;
391 // Empty the treeview
392 This
->EmptyDeviceView();
393 This
->m_hTreeRoot
= NULL
;
395 // Refresh the devices only if requested. This means
396 // switching views uses the cache and remains fast
397 if (ThreadData
->ScanForChanges
)
399 This
->RefreshDeviceList();
402 // display the type of view the user wants
403 switch (This
->m_ViewType
)
406 (void)This
->ListDevicesByType();
409 case DevicesByConnection
:
410 (VOID
)This
->ListDevicesByConnection();
413 case ResourcesByType
:
416 case ResourcesByConnection
:
427 CDeviceView::ListDevicesByType()
429 CClassNode
*ClassNode
;
430 CDeviceNode
*DeviceNode
;
432 HTREEITEM hTreeItem
= NULL
;
435 LPTSTR DeviceId
= NULL
;
436 BOOL bClassSuccess
, bSuccess
;
438 // Start by adding the root node to the tree
439 bSuccess
= AddRootDevice();
440 if (bSuccess
== false) return false;
445 // Loop through all the device classes
446 bClassSuccess
= GetNextClass(ClassIndex
, &ClassGuid
, &hDevInfo
);
449 bool bClassUnknown
= false;
450 bool AddedParent
= false;
454 // Get the cached class node
455 ClassNode
= GetClassNode(&ClassGuid
);
456 if (ClassNode
== NULL
)
463 // Set a flag is this is the (special case) unknown class
464 if (IsEqualGUID(ClassGuid
, GUID_DEVCLASS_UNKNOWN
))
465 bClassUnknown
= true;
469 // Get a handle to all the devices in this class
470 SP_DEVINFO_DATA DeviceInfoData
;
471 ZeroMemory(&DeviceInfoData
, sizeof(SP_DEVINFO_DATA
));
472 DeviceInfoData
.cbSize
= sizeof(SP_DEVINFO_DATA
);
473 bSuccess
= SetupDiEnumDeviceInfo(hDevInfo
,
476 if (bSuccess
== FALSE
&& GetLastError() == ERROR_NO_MORE_ITEMS
)
483 // The unknown class handle contains all devices on the system,
484 // and we're just looking for the ones with a null GUID
487 if (IsEqualGUID(DeviceInfoData
.ClassGuid
, GUID_NULL
) == FALSE
)
489 // This is a known device, we aren't interested in it
495 // Get the cached device node
496 DeviceNode
= GetDeviceNode(DeviceInfoData
.DevInst
);
497 if (DeviceNode
== NULL
)
499 ATLASSERT(bClassUnknown
== true);
504 // Check if this is a hidden device
505 if (DeviceNode
->IsHidden())
507 // Ignore this device if we aren't displaying hidden devices
508 if (m_ShowHidden
== FALSE
)
515 // We have a device, we need to add the parent if it hasn't yet been added
516 if (AddedParent
== false)
518 // Insert the new class under the root item
519 hTreeItem
= InsertIntoTreeView(m_hTreeRoot
,
524 // Add the device under the class item node
525 (void)InsertIntoTreeView(hTreeItem
, DeviceNode
);
527 // Expand the class if it has a problem device
528 if (DeviceNode
->HasProblem())
530 (void)TreeView_Expand(m_hTreeView
,
540 // If this class has devices, sort them alphabetically
541 if (AddedParent
== true)
543 (void)TreeView_SortChildren(m_hTreeView
,
551 } while (bClassSuccess
);
553 // Sort the classes alphabetically
554 (void)TreeView_SortChildren(m_hTreeView
,
558 // Expand the root item
559 (void)TreeView_Expand(m_hTreeView
,
563 // Pre-select the root item
564 (VOID
)TreeView_SelectItem(m_hTreeView
,
571 CDeviceView::ListDevicesByConnection()
575 // Start by adding the root node to the tree
576 bSuccess
= AddRootDevice();
577 if (bSuccess
== false) return false;
579 // Walk the device tree and add all the devices
580 RecurseChildDevices(m_RootDevInst
, m_hTreeRoot
);
582 // Expand the root item
583 (VOID
)TreeView_Expand(m_hTreeView
,
591 CDeviceView::RecurseChildDevices(
592 _In_ DEVINST ParentDevice
,
593 _In_ HTREEITEM hParentTreeItem
597 HTREEITEM hDevItem
= NULL
;
601 // Check if the parent has any child devices
602 if (GetChildDevice(ParentDevice
, &Device
) == FALSE
)
605 // Get the cached device node
606 CDeviceNode
*DeviceNode
;
607 DeviceNode
= dynamic_cast<CDeviceNode
*>(GetDeviceNode(Device
));
608 if (DeviceNode
== NULL
)
615 // Check if this is a hidden device
616 if ((m_ShowHidden
== TRUE
) || (!(DeviceNode
->IsHidden())))
618 // Add this device to the tree under its parent
619 hDevItem
= InsertIntoTreeView(hParentTreeItem
,
625 // Check if this child has any children itself
626 RecurseChildDevices(Device
, hDevItem
);
633 // Check if the parent device has anything at the same level
634 bSuccess
= GetSiblingDevice(Device
, &Device
);
635 if (bSuccess
== FALSE
) break;
637 DeviceNode
= dynamic_cast<CDeviceNode
*>(GetDeviceNode(Device
));
638 if (DeviceNode
== NULL
)
643 // Check if this is a hidden device
644 if (DeviceNode
->IsHidden())
646 if (m_ShowHidden
== FALSE
)
650 // Add this device to the tree under its parent
651 hDevItem
= InsertIntoTreeView(hParentTreeItem
,
655 // Check if this child has any children itself
656 RecurseChildDevices(Device
, hDevItem
);
660 (void)TreeView_SortChildren(m_hTreeView
,
667 CDeviceView::GetChildDevice(
668 _In_ DEVINST ParentDevInst
,
669 _Out_ PDEVINST DevInst
673 cr
= CM_Get_Child(DevInst
,
676 return (cr
== CR_SUCCESS
);
680 CDeviceView::GetSiblingDevice(
681 _In_ DEVINST PrevDevice
,
682 _Out_ PDEVINST DevInst
686 cr
= CM_Get_Sibling(DevInst
,
689 return (cr
== CR_SUCCESS
);
693 CDeviceView::InsertIntoTreeView(
694 _In_ HTREEITEM hParent
,
700 lpLabel
= Node
->GetDisplayName();
703 TV_INSERTSTRUCT tvins
;
704 ZeroMemory(&tvi
, sizeof(tvi
));
705 ZeroMemory(&tvins
, sizeof(tvins
));
707 tvi
.mask
= TVIF_TEXT
| TVIF_PARAM
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
708 tvi
.pszText
= lpLabel
;
709 tvi
.cchTextMax
= wcslen(lpLabel
);
710 tvi
.lParam
= (LPARAM
)Node
;
711 tvi
.iImage
= Node
->GetClassImage();
712 tvi
.iSelectedImage
= Node
->GetClassImage();
714 // try to cast it to a device node. This will only suceed if it's the correct type
715 CDeviceNode
*DeviceNode
= dynamic_cast<CDeviceNode
*>(Node
);
716 if (DeviceNode
&& DeviceNode
->GetOverlayImage())
718 tvi
.mask
|= TVIF_STATE
;
719 tvi
.stateMask
= TVIS_OVERLAYMASK
;
720 tvi
.state
= INDEXTOOVERLAYMASK(DeviceNode
->GetOverlayImage());
724 tvins
.hParent
= hParent
;
726 return TreeView_InsertItem(m_hTreeView
, &tvins
);
730 CDeviceView::RecurseDeviceView(
731 _In_ HTREEITEM hParentItem
737 // Check if this node has any children
738 hItem
= TreeView_GetChild(m_hTreeView
, hParentItem
);
739 if (hItem
== NULL
) return;
741 // The lParam contains the node pointer data
742 tvItem
.hItem
= hItem
;
743 tvItem
.mask
= TVIF_PARAM
;
744 if (TreeView_GetItem(m_hTreeView
, &tvItem
) &&
745 tvItem
.lParam
!= NULL
)
747 // Delete the node class
748 //delete reinterpret_cast<CNode *>(tvItem.lParam);
751 // This node may have its own children
752 RecurseDeviceView(hItem
);
754 // Delete all the siblings
757 // Get the next item at this level
758 hItem
= TreeView_GetNextSibling(m_hTreeView
, hItem
);
759 if (hItem
== NULL
) break;
761 // The lParam contains the node pointer data
762 tvItem
.hItem
= hItem
;
763 tvItem
.mask
= TVIF_PARAM
;
764 if (TreeView_GetItem(m_hTreeView
, &tvItem
))
766 //if (tvItem.lParam != NULL)
767 // delete reinterpret_cast<CNode *>(tvItem.lParam);
770 // This node may have its own children
771 RecurseDeviceView(hItem
);
777 CDeviceView::EmptyDeviceView()
781 // Check if there are any items in the tree
782 hItem
= TreeView_GetRoot(m_hTreeView
);
783 if (hItem
== NULL
) return;
785 // Free all the class nodes
786 //RecurseDeviceView(hItem);
788 // Delete all the items
789 (VOID
)TreeView_DeleteAllItems(m_hTreeView
);
796 CDeviceView::GetClassNode(_In_ LPGUID ClassGuid
)
801 Pos
= m_ClassNodeList
.GetHeadPosition();
805 Node
= m_ClassNodeList
.GetNext(Pos
);
806 if (IsEqualGUID(*Node
->GetClassGuid(), *ClassGuid
))
808 //ATLASSERT(Node->GetType() == NodeClass);
814 } while (Pos
!= NULL
);
820 CDeviceView::GetDeviceNode(_In_ DEVINST Device
)
825 Pos
= m_DeviceNodeList
.GetHeadPosition();
829 Node
= m_DeviceNodeList
.GetNext(Pos
);
830 if (Node
->GetDeviceInst() == Device
)
832 //ATLASSERT(Node->GetType() == NodeDevice);
838 } while (Pos
!= NULL
);
843 CNode
* CDeviceView::GetNode(LPTV_ITEMW TvItem
)
845 TvItem
->mask
= TVIF_PARAM
;
846 if (TreeView_GetItem(m_hTreeView
, TvItem
))
848 return (CNode
*)TvItem
->lParam
;
853 CNode
* CDeviceView::GetSelectedNode()
856 TvItem
.hItem
= TreeView_GetSelection(m_hTreeView
);
857 return GetNode(&TvItem
);
861 CDeviceView::EmptyLists()
866 if (!m_ClassNodeList
.IsEmpty())
868 Pos
= m_ClassNodeList
.GetHeadPosition();
871 Node
= m_ClassNodeList
.GetNext(Pos
);
874 } while (Pos
!= NULL
);
877 if (!m_DeviceNodeList
.IsEmpty())
879 Pos
= m_DeviceNodeList
.GetHeadPosition();
882 Node
= m_DeviceNodeList
.GetNext(Pos
);
885 } while (Pos
!= NULL
);
890 CDeviceView::RefreshDeviceList()
893 CClassNode
*ClassNode
;
894 CDeviceNode
*DeviceNode
;
896 SP_DEVINFO_DATA DeviceInfoData
;
900 ULONG ClassIndex
= 0;
904 // Loop through all the classes
907 Success
= GetNextClass(ClassIndex
, &ClassGuid
, &hDevInfo
);
910 // Create a new class node and add it to the list
911 ClassNode
= new CClassNode(&ClassGuid
, &m_ImageListData
);
912 if (ClassNode
->SetupNode())
914 m_ClassNodeList
.AddTail(ClassNode
);
921 // Get all the devices on the local machine
922 hDevInfo
= SetupDiGetClassDevsW(NULL
,
925 DIGCF_PRESENT
| DIGCF_ALLCLASSES
);
926 if (hDevInfo
== INVALID_HANDLE_VALUE
)
931 // loop though all the devices
932 DeviceInfoData
.cbSize
= sizeof(SP_DEVINFO_DATA
);
935 // Get the devinst for this device
936 Success
= SetupDiEnumDeviceInfo(hDevInfo
, i
, &DeviceInfoData
);
937 if (Success
== FALSE
) break;
939 // create a new device node and add it to the list
940 DeviceNode
= new CDeviceNode(DeviceInfoData
.DevInst
, &m_ImageListData
);
941 if (DeviceNode
->SetupNode())
943 m_DeviceNodeList
.AddTail(DeviceNode
);
947 SetupDiDestroyDeviceInfoList(hDevInfo
);