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 (void)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
596 HTREEITEM hDevItem
= NULL
;
598 bool HasProblem
= false;
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
)
614 // Don't show hidden devices if not requested
615 if ((m_ShowHidden
== TRUE
) || (!(DeviceNode
->IsHidden())))
617 // Add this device to the tree under its parent
618 hDevItem
= InsertIntoTreeView(hParentTreeItem
,
622 // Check if this child has any children itself
623 if (!RecurseChildDevices(Device
, hDevItem
))
627 if (DeviceNode
->HasProblem())
634 // Check for siblings
637 // Check if the parent device has anything at the same level
638 bSuccess
= GetSiblingDevice(Device
, &Device
);
639 if (bSuccess
== FALSE
) break;
641 DeviceNode
= dynamic_cast<CDeviceNode
*>(GetDeviceNode(Device
));
642 if (DeviceNode
== NULL
)
647 // Check if this is a hidden device
648 if (DeviceNode
->IsHidden())
650 if (m_ShowHidden
== FALSE
)
654 if (DeviceNode
->HasProblem())
659 // Add this device to the tree under its parent
660 hDevItem
= InsertIntoTreeView(hParentTreeItem
,
664 // Check if this child has any children itself
665 if (!RecurseChildDevices(Device
, hDevItem
))
671 (void)TreeView_SortChildren(m_hTreeView
,
675 // Expand the class if it has a problem device
676 if (HasProblem
== true)
678 (void)TreeView_Expand(m_hTreeView
,
683 // If there was a problem, expand the ancestors
684 if (HasProblem
) return false;
690 CDeviceView::GetChildDevice(
691 _In_ DEVINST ParentDevInst
,
692 _Out_ PDEVINST DevInst
696 cr
= CM_Get_Child(DevInst
,
699 return (cr
== CR_SUCCESS
);
703 CDeviceView::GetSiblingDevice(
704 _In_ DEVINST PrevDevice
,
705 _Out_ PDEVINST DevInst
709 cr
= CM_Get_Sibling(DevInst
,
712 return (cr
== CR_SUCCESS
);
716 CDeviceView::InsertIntoTreeView(
717 _In_ HTREEITEM hParent
,
723 lpLabel
= Node
->GetDisplayName();
726 TV_INSERTSTRUCT tvins
;
727 ZeroMemory(&tvi
, sizeof(tvi
));
728 ZeroMemory(&tvins
, sizeof(tvins
));
730 tvi
.mask
= TVIF_TEXT
| TVIF_PARAM
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
731 tvi
.pszText
= lpLabel
;
732 tvi
.cchTextMax
= wcslen(lpLabel
);
733 tvi
.lParam
= (LPARAM
)Node
;
734 tvi
.iImage
= Node
->GetClassImage();
735 tvi
.iSelectedImage
= Node
->GetClassImage();
737 // try to cast it to a device node. This will only suceed if it's the correct type
738 CDeviceNode
*DeviceNode
= dynamic_cast<CDeviceNode
*>(Node
);
739 if (DeviceNode
&& DeviceNode
->GetOverlayImage())
741 tvi
.mask
|= TVIF_STATE
;
742 tvi
.stateMask
= TVIS_OVERLAYMASK
;
743 tvi
.state
= INDEXTOOVERLAYMASK(DeviceNode
->GetOverlayImage());
747 tvins
.hParent
= hParent
;
749 return TreeView_InsertItem(m_hTreeView
, &tvins
);
753 CDeviceView::RecurseDeviceView(
754 _In_ HTREEITEM hParentItem
760 // Check if this node has any children
761 hItem
= TreeView_GetChild(m_hTreeView
, hParentItem
);
762 if (hItem
== NULL
) return;
764 // The lParam contains the node pointer data
765 tvItem
.hItem
= hItem
;
766 tvItem
.mask
= TVIF_PARAM
;
767 if (TreeView_GetItem(m_hTreeView
, &tvItem
) &&
768 tvItem
.lParam
!= NULL
)
770 // Delete the node class
771 //delete reinterpret_cast<CNode *>(tvItem.lParam);
774 // This node may have its own children
775 RecurseDeviceView(hItem
);
777 // Delete all the siblings
780 // Get the next item at this level
781 hItem
= TreeView_GetNextSibling(m_hTreeView
, hItem
);
782 if (hItem
== NULL
) break;
784 // The lParam contains the node pointer data
785 tvItem
.hItem
= hItem
;
786 tvItem
.mask
= TVIF_PARAM
;
787 if (TreeView_GetItem(m_hTreeView
, &tvItem
))
789 //if (tvItem.lParam != NULL)
790 // delete reinterpret_cast<CNode *>(tvItem.lParam);
793 // This node may have its own children
794 RecurseDeviceView(hItem
);
800 CDeviceView::EmptyDeviceView()
804 // Check if there are any items in the tree
805 hItem
= TreeView_GetRoot(m_hTreeView
);
806 if (hItem
== NULL
) return;
808 // Free all the class nodes
809 //RecurseDeviceView(hItem);
811 // Delete all the items
812 (VOID
)TreeView_DeleteAllItems(m_hTreeView
);
819 CDeviceView::GetClassNode(_In_ LPGUID ClassGuid
)
824 Pos
= m_ClassNodeList
.GetHeadPosition();
828 Node
= m_ClassNodeList
.GetNext(Pos
);
829 if (IsEqualGUID(*Node
->GetClassGuid(), *ClassGuid
))
831 //ATLASSERT(Node->GetType() == NodeClass);
837 } while (Pos
!= NULL
);
843 CDeviceView::GetDeviceNode(_In_ DEVINST Device
)
848 Pos
= m_DeviceNodeList
.GetHeadPosition();
852 Node
= m_DeviceNodeList
.GetNext(Pos
);
853 if (Node
->GetDeviceInst() == Device
)
855 //ATLASSERT(Node->GetType() == NodeDevice);
861 } while (Pos
!= NULL
);
866 CNode
* CDeviceView::GetNode(LPTV_ITEMW TvItem
)
868 TvItem
->mask
= TVIF_PARAM
;
869 if (TreeView_GetItem(m_hTreeView
, TvItem
))
871 return (CNode
*)TvItem
->lParam
;
876 CNode
* CDeviceView::GetSelectedNode()
879 TvItem
.hItem
= TreeView_GetSelection(m_hTreeView
);
880 return GetNode(&TvItem
);
884 CDeviceView::EmptyLists()
886 CClassNode
*ClassNode
;
887 CDeviceNode
*DeviceNode
;
889 while (!m_ClassNodeList
.IsEmpty())
891 ClassNode
= m_ClassNodeList
.RemoveTail();
895 while (!m_DeviceNodeList
.IsEmpty())
897 DeviceNode
= m_DeviceNodeList
.RemoveTail();
903 CDeviceView::RefreshDeviceList()
906 CClassNode
*ClassNode
;
907 CDeviceNode
*DeviceNode
;
909 SP_DEVINFO_DATA DeviceInfoData
;
913 ULONG ClassIndex
= 0;
917 // Loop through all the classes
920 Success
= GetNextClass(ClassIndex
, &ClassGuid
, &hDevInfo
);
923 // Create a new class node and add it to the list
924 ClassNode
= new CClassNode(&ClassGuid
, &m_ImageListData
);
925 if (ClassNode
->SetupNode())
927 m_ClassNodeList
.AddTail(ClassNode
);
934 // Get all the devices on the local machine
935 hDevInfo
= SetupDiGetClassDevsW(NULL
,
938 DIGCF_PRESENT
| DIGCF_ALLCLASSES
);
939 if (hDevInfo
== INVALID_HANDLE_VALUE
)
944 // loop though all the devices
945 DeviceInfoData
.cbSize
= sizeof(SP_DEVINFO_DATA
);
948 // Get the devinst for this device
949 Success
= SetupDiEnumDeviceInfo(hDevInfo
, i
, &DeviceInfoData
);
950 if (Success
== FALSE
) break;
952 // create a new device node and add it to the list
953 DeviceNode
= new CDeviceNode(DeviceInfoData
.DevInst
, &m_ImageListData
);
954 if (DeviceNode
->SetupNode())
956 m_DeviceNodeList
.AddTail(DeviceNode
);
960 SetupDiDestroyDeviceInfoList(hDevInfo
);