From: Ged Murphy Date: Tue, 16 Jun 2015 21:13:28 +0000 (+0000) Subject: [DEVMGR] X-Git-Tag: backups/colins-printing-for-freedom@73041~15^2~62 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=a676d524c9f2f358d87fcdf6247d50d68fb3158f [DEVMGR] - Move the devmgmt code to devmgr where it belongs and start to rewrite it (devmgmt_new wasn't a good design). It's not part of devmgr yet, I'll merge it and add it to the build when it's more complete. - Add support for caching devices to speed up switching device views - start to add dynamic context support so we can enable/disable, update and uninstall devices depending on its state. - WIP svn path=/trunk/; revision=68165 --- diff --git a/reactos/dll/win32/devmgr/devmgmt/DeviceView.cpp b/reactos/dll/win32/devmgr/devmgmt/DeviceView.cpp new file mode 100644 index 00000000000..d844cc93dce --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/DeviceView.cpp @@ -0,0 +1,873 @@ +/* +* PROJECT: ReactOS Device Manager +* LICENSE: GPL - See COPYING in the top level directory +* FILE: dll/win32/devmgr/devmgr/DeviceView.cpp +* PURPOSE: Implements the tree view which contains the devices +* COPYRIGHT: Copyright 2015 Ged Murphy +* +*/ + + +#include "stdafx.h" +#include "devmgmt.h" +#include "DeviceView.h" + + +/* DATA *********************************************/ + +#define CLASS_NAME_LEN 256 +#define CLASS_DESC_LEN 256 + +INT_PTR +WINAPI +DevicePropertiesExW( + IN HWND hWndParent OPTIONAL, + IN LPCWSTR lpMachineName OPTIONAL, + IN LPCWSTR lpDeviceID OPTIONAL, + IN DWORD dwFlags OPTIONAL, + IN BOOL bShowDevMgr +); + +typedef INT_PTR(WINAPI *pDevicePropertiesExW)(HWND,LPCWSTR,LPCWSTR,DWORD,BOOL); + +struct RefreshThreadData +{ + CDeviceView *This; + BOOL ScanForChanges; + BOOL UpdateView; +}; + + +/* PUBLIC METHODS *************************************/ + +CDeviceView::CDeviceView( + HWND hMainWnd + ) : + m_hMainWnd(hMainWnd), + m_hTreeView(NULL), + m_hPropertyDialog(NULL), + m_hShortcutMenu(NULL), + m_ViewType(DevicesByType), + m_ShowHidden(FALSE), + m_RootClassImage(-1), + m_RootDevInst(0) +{ + ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA)); +} + +CDeviceView::~CDeviceView(void) +{ +} + +bool +CDeviceView::Initialize() +{ + // Get the device image list + m_ImageListData.cbSize = sizeof(SP_CLASSIMAGELIST_DATA); + BOOL bSuccess = SetupDiGetClassImageList(&m_ImageListData); + if (bSuccess == FALSE) return false; + + // Create the main treeview + m_hTreeView = CreateWindowExW(WS_EX_CLIENTEDGE, + WC_TREEVIEW, + NULL, + WS_CHILD | WS_VISIBLE | WS_BORDER | TVS_HASLINES | + TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_LINESATROOT, + 0, 0, 0, 0, + m_hMainWnd, + (HMENU)IDC_TREEVIEW, + g_hInstance, + NULL); + if (m_hTreeView) + { + // Set the image list against the treeview + (void)TreeView_SetImageList(m_hTreeView, + m_ImageListData.ImageList, + TVSIL_NORMAL); + + // Give the treeview arrows instead of +/- boxes (on Win7) + SetWindowTheme(m_hTreeView, L"explorer", NULL); + } + + return !!(m_hTreeView); +} + +bool +CDeviceView::Uninitialize() +{ + EmptyDeviceView(); + + if (m_ImageListData.ImageList != NULL) + { + SetupDiDestroyClassImageList(&m_ImageListData); + ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA)); + } + + return true; +} + +void +CDeviceView::Size( + _In_ int x, + _In_ int y, + _In_ int cx, + _In_ int cy + ) +{ + // Resize the treeview + SetWindowPos(m_hTreeView, + NULL, + x, + y, + cx, + cy, + SWP_NOZORDER); +} + +void +CDeviceView::Refresh(_In_ ViewType Type, + _In_ bool ScanForChanges, + _In_ bool UpdateView) +{ + // Enum devices on a seperate thread to keep the gui responsive + + m_ViewType = Type; + + RefreshThreadData *ThreadData; + ThreadData = new RefreshThreadData(); + ThreadData->This = this; + ThreadData->ScanForChanges = ScanForChanges; + ThreadData->UpdateView = UpdateView; + + HANDLE hThread; + hThread = (HANDLE)_beginthreadex(NULL, + 0, + &RefreshThread, + ThreadData, + 0, + NULL); + + if (hThread) CloseHandle(hThread); +} + +void +CDeviceView::DisplayPropertySheet() +{ + // + // In ReactOS we can link to DevicePropertiesEx but + // not in windows as it's not part of the SDK + +#ifndef __REACTOS__ + HMODULE hModule = LoadLibraryW(L"devmgr.dll"); + if (hModule == NULL) return; + + pDevicePropertiesExW DevicePropertiesExW; + DevicePropertiesExW = (pDevicePropertiesExW)GetProcAddress(hModule, + "DevicePropertiesExW"); + if (DevicePropertiesExW == NULL) + { + FreeLibrary(hModule); + return; + } +#endif + + CNode *Node = GetSelectedNode(); + if (Node && Node->HasProperties()) + { + DevicePropertiesExW(m_hTreeView, + NULL, + Node->GetDeviceId(), + 1,//DPF_EXTENDED, + FALSE); + } + +#ifndef __REACTOS__ + FreeLibrary(hModule); +#endif +} + +void +CDeviceView::SetFocus() +{ +} + +bool +CDeviceView::HasProperties(_In_ LPTV_ITEMW TvItem) +{ + CNode *Node = GetNode(TvItem); + if (Node) + { + return Node->HasProperties(); + } + return false; +} + +bool +CDeviceView::IsDisabled(_In_ LPTV_ITEMW TvItem) +{ + CNode *Node = GetNode(TvItem); + if (Node) + { + return Node->IsDisabled(); + } + return false; +} + +bool +CDeviceView::CanDisable(_In_ LPTV_ITEMW TvItem) +{ + CNode *Node = GetNode(TvItem); + if (Node) + { + return Node->CanDisable(); + } + return false; +} + + +/* PRIVATE METHODS ********************************************/ + +bool +CDeviceView::AddRootDevice() +{ + // Check whether we've loaded the root bitmap into the imagelist (done on first run) + if (m_RootClassImage == -1) + { + // Load the bitmap we'll be using as the root image + HBITMAP hRootImage; + hRootImage = LoadBitmapW(g_hInstance, + MAKEINTRESOURCEW(IDB_ROOT_IMAGE)); + if (hRootImage == NULL) return FALSE; + + // Add this bitmap to the device image list. This is a bit hacky, but it's safe + m_RootClassImage = ImageList_Add(m_ImageListData.ImageList, + hRootImage, + NULL); + DeleteObject(hRootImage); + } + + /* Get the root instance */ + CONFIGRET cr; + cr = CM_Locate_DevNodeW(&m_RootDevInst, + NULL, + CM_LOCATE_DEVNODE_NORMAL); + if (cr != CR_SUCCESS) + { + return false; + } + + /* The root name is the computer name */ + WCHAR RootDeviceName[ROOT_NAME_SIZE]; + DWORD Size = ROOT_NAME_SIZE; + if (GetComputerNameW(RootDeviceName, &Size)) + _wcslwr_s(RootDeviceName); + + TV_ITEMW tvi; + TV_INSERTSTRUCT tvins; + ZeroMemory(&tvi, sizeof(tvi)); + ZeroMemory(&tvins, sizeof(tvins)); + + // Insert the root / parent item into our treeview + tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvi.pszText = RootDeviceName; + tvi.cchTextMax = wcslen(RootDeviceName); + tvi.iImage = m_RootClassImage; + tvi.iSelectedImage = m_RootClassImage; + tvins.item = tvi; + m_hTreeRoot = TreeView_InsertItem(m_hTreeView, &tvins); + + return (m_hTreeRoot != NULL); + +} + +bool +CDeviceView::GetNextClass(_In_ ULONG ClassIndex, + _Out_ LPGUID ClassGuid, + _Out_ HDEVINFO *hDevInfo) +{ + CONFIGRET cr; + + // Get the next class in the list + cr = CM_Enumerate_Classes(ClassIndex, + ClassGuid, + 0); + if (cr != CR_SUCCESS) return false; + + // Check for devices without a class + if (IsEqualGUID(*ClassGuid, GUID_DEVCLASS_UNKNOWN)) + { + // Get device info for all devices for all classes + *hDevInfo = SetupDiGetClassDevsW(NULL, + NULL, + NULL, + DIGCF_ALLCLASSES); + } + else + { + // We only want the devices for this class + *hDevInfo = SetupDiGetClassDevsW(ClassGuid, + NULL, + NULL, + DIGCF_PRESENT); + + } + + return (hDevInfo != INVALID_HANDLE_VALUE); +} + +unsigned int __stdcall CDeviceView::RefreshThread(void *Param) +{ + RefreshThreadData *ThreadData = (RefreshThreadData *)Param; + CDeviceView *This = ThreadData->This; + + + // Empty the treeview + This->EmptyDeviceView(); + This->m_hTreeRoot = NULL; + + // Refresh the devices only if requested. This means + // switching views uses the cache and remains fast + if (ThreadData->ScanForChanges) + { + This->RefreshDeviceList(); + } + + // display the type of view the user wants + switch (This->m_ViewType) + { + case DevicesByType: + (void)This->ListDevicesByType(); + break; + + case DevicesByConnection: + (VOID)This->ListDevicesByConnection(); + break; + + case ResourcesByType: + break; + + case ResourcesByConnection: + break; + } + + delete ThreadData; + + return 0; +} + + +bool +CDeviceView::ListDevicesByType() +{ + CNode *ClassNode, *DeviceNode; + HDEVINFO hDevInfo; + HTREEITEM hTreeItem = NULL; + GUID ClassGuid; + INT ClassIndex; + LPTSTR DeviceId = NULL; + BOOL bClassSuccess, bSuccess; + + // Start by adding the root node to the tree + bSuccess = AddRootDevice(); + if (bSuccess == false) return false; + + ClassIndex = 0; + do + { + // Loop through all the device classes + bClassSuccess = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo); + if (bClassSuccess) + { + bool bClassUnknown = false; + bool AddedParent = false; + INT DeviceIndex = 0; + BOOL MoreItems; + + // Get the cached class node + ClassNode = GetClassNode(&ClassGuid); + if (ClassNode == NULL) + { + ATLASSERT(FALSE); + ClassIndex++; + continue; + } + + // Set a flag is this is the (special case) unknown class + if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_UNKNOWN)) + bClassUnknown = true; + + do + { + // Get a handle to all the devices in this class + SP_DEVINFO_DATA DeviceInfoData; + ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA)); + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + bSuccess = SetupDiEnumDeviceInfo(hDevInfo, + DeviceIndex, + &DeviceInfoData); + if (bSuccess == FALSE && GetLastError() == ERROR_NO_MORE_ITEMS) + MoreItems = FALSE; + + if (bSuccess) + { + MoreItems = TRUE; + + // The unknown class handle contains all devices on the system, + // and we're just looking for the ones with a null GUID + if (bClassUnknown) + { + if (IsEqualGUID(DeviceInfoData.ClassGuid, GUID_NULL) == FALSE) + { + // This is a known device, we aren't interested in it + DeviceIndex++; + continue; + } + } + + // Get the cached device node + DeviceNode = GetDeviceNode(DeviceInfoData.DevInst); + if (DeviceNode == NULL) + { + ATLASSERT(bClassUnknown == true); + DeviceIndex++; + continue; + } + + // Check if this is a hidden device + if (DeviceNode->IsHidden()) + { + // Ignore this device if we aren't displaying hidden devices + if (m_ShowHidden == FALSE) + { + DeviceIndex++; + continue; + } + } + + // We have a device, we need to add the parent if it hasn't yet been added + if (AddedParent == false) + { + // Insert the new class under the root item + hTreeItem = InsertIntoTreeView(m_hTreeRoot, + ClassNode); + AddedParent = true; + } + + // Add the device under the class item node + (void)InsertIntoTreeView(hTreeItem, DeviceNode); + + // Expand the class if it has a problem device + if (DeviceNode->HasProblem()) + { + (void)TreeView_Expand(m_hTreeView, + hTreeItem, + TVE_EXPAND); + } + } + + DeviceIndex++; + + } while (MoreItems); + + // If this class has devices, sort them alphabetically + if (AddedParent == true) + { + (void)TreeView_SortChildren(m_hTreeView, + hTreeItem, + 0); + } + } + + ClassIndex++; + + } while (bClassSuccess); + + // Sort the classes alphabetically + (void)TreeView_SortChildren(m_hTreeView, + m_hTreeRoot, + 0); + + // Expand the root item + (void)TreeView_Expand(m_hTreeView, + m_hTreeRoot, + TVE_EXPAND); + + // Pre-select the root item + (VOID)TreeView_SelectItem(m_hTreeView, + m_hTreeRoot); + + return 0; +} + +bool +CDeviceView::ListDevicesByConnection() +{ + BOOL bSuccess; + + // Start by adding the root node to the tree + bSuccess = AddRootDevice(); + if (bSuccess == false) return false; + + /* Walk the device tree and add all the devices */ + RecurseChildDevices(m_RootDevInst, m_hTreeRoot); + + /* Expand the root item */ + (VOID)TreeView_Expand(m_hTreeView, + m_hTreeRoot, + TVE_EXPAND); + + return true; +} + +VOID +CDeviceView::RecurseChildDevices( + _In_ DEVINST ParentDevice, + _In_ HTREEITEM hParentTreeItem + ) +{ + + HTREEITEM hDevItem = NULL; + DEVINST Device; + BOOL bSuccess; + + /* Check if the parent has any child devices */ + if (GetChildDevice(ParentDevice, &Device) == FALSE) + return; + + // Get the cached device node + CNode *DeviceNode; + DeviceNode = GetDeviceNode(Device); + if (DeviceNode == NULL) + { + ATLASSERT(FALSE); + return; + } + + + /* Check if this is a hidden device */ + if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden()))) + { + /* Add this device to the tree under its parent */ + hDevItem = InsertIntoTreeView(hParentTreeItem, + DeviceNode); + + + if (hDevItem) + { + /* Check if this child has any children itself */ + RecurseChildDevices(Device, hDevItem); + } + } + + + for (;;) + { + /* Check if the parent device has anything at the same level */ + bSuccess = GetSiblingDevice(Device, &Device); + if (bSuccess == FALSE) break; + + DeviceNode = GetDeviceNode(Device); + if (DeviceNode == NULL) + { + ATLASSERT(FALSE); + } + + /* Check if this is a hidden device */ + if (DeviceNode->IsHidden()) + { + if (m_ShowHidden == FALSE) + continue; + } + + /* Add this device to the tree under its parent */ + hDevItem = InsertIntoTreeView(hParentTreeItem, + DeviceNode); + if (hDevItem) + { + /* Check if this child has any children itself */ + RecurseChildDevices(Device, hDevItem); + } + } + + (void)TreeView_SortChildren(m_hTreeView, + hParentTreeItem, + 0); + +} + +bool +CDeviceView::GetChildDevice( + _In_ DEVINST ParentDevInst, + _Out_ PDEVINST DevInst +) +{ + CONFIGRET cr; + cr = CM_Get_Child(DevInst, + ParentDevInst, + 0); + return (cr == CR_SUCCESS); +} + +bool +CDeviceView::GetSiblingDevice( + _In_ DEVINST PrevDevice, + _Out_ PDEVINST DevInst +) +{ + CONFIGRET cr; + cr = CM_Get_Sibling(DevInst, + PrevDevice, + 0); + return (cr == CR_SUCCESS); +} + +HTREEITEM +CDeviceView::InsertIntoTreeView( + _In_ HTREEITEM hParent, + _In_ CNode *Node + ) +{ + + LPWSTR lpLabel; + lpLabel = Node->GetDisplayName(); + + TV_ITEMW tvi; + TV_INSERTSTRUCT tvins; + ZeroMemory(&tvi, sizeof(tvi)); + ZeroMemory(&tvins, sizeof(tvins)); + + tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvi.pszText = lpLabel; + tvi.cchTextMax = wcslen(lpLabel); + tvi.lParam = (LPARAM)Node; + tvi.iImage = Node->GetClassImage(); + tvi.iSelectedImage = Node->GetClassImage(); + + if (Node->GetOverlayImage()) + { + tvi.mask |= TVIF_STATE; + tvi.stateMask = TVIS_OVERLAYMASK; + tvi.state = INDEXTOOVERLAYMASK(Node->GetOverlayImage()); + } + + tvins.item = tvi; + tvins.hParent = hParent; + + return TreeView_InsertItem(m_hTreeView, &tvins); +} + +void +CDeviceView::RecurseDeviceView( + _In_ HTREEITEM hParentItem + ) +{ + HTREEITEM hItem; + TVITEMW tvItem; + + // Check if this node has any children + hItem = TreeView_GetChild(m_hTreeView, hParentItem); + if (hItem == NULL) return; + + // The lParam contains the node pointer data + tvItem.hItem = hItem; + tvItem.mask = TVIF_PARAM; + if (TreeView_GetItem(m_hTreeView, &tvItem) && + tvItem.lParam != NULL) + { + // Delete the node class + //delete reinterpret_cast(tvItem.lParam); + } + + // This node may have its own children + RecurseDeviceView(hItem); + + // Delete all the siblings + for (;;) + { + // Get the next item at this level + hItem = TreeView_GetNextSibling(m_hTreeView, hItem); + if (hItem == NULL) break; + + // The lParam contains the node pointer data + tvItem.hItem = hItem; + tvItem.mask = TVIF_PARAM; + if (TreeView_GetItem(m_hTreeView, &tvItem)) + { + //if (tvItem.lParam != NULL) + // delete reinterpret_cast(tvItem.lParam); + } + + /* This node may have its own children */ + RecurseDeviceView(hItem); + } +} + + +void +CDeviceView::EmptyDeviceView() +{ + HTREEITEM hItem; + + // Check if there are any items in the tree + hItem = TreeView_GetRoot(m_hTreeView); + if (hItem == NULL) return; + + // Free all the class nodes + //RecurseDeviceView(hItem); + + // Delete all the items + (VOID)TreeView_DeleteAllItems(m_hTreeView); +} + + + + +CNode* +CDeviceView::GetClassNode(_In_ LPGUID ClassGuid) +{ + POSITION Pos; + CNode *Node; + + Pos = m_ClassNodeList.GetHeadPosition(); + + do + { + Node = m_ClassNodeList.GetNext(Pos); + if (IsEqualGUID(*Node->GetClassGuid(), *ClassGuid)) + { + //ATLASSERT(Node->GetType() == NodeClass); + break; + } + + Node = NULL; + + } while (Pos != NULL); + + return Node; +} + +CNode* +CDeviceView::GetDeviceNode(_In_ DEVINST Device) +{ + POSITION Pos; + CNode *Node; + + Pos = m_DeviceNodeList.GetHeadPosition(); + + do + { + Node = m_DeviceNodeList.GetNext(Pos); + if (Node->GetDeviceInst() == Device) + { + //ATLASSERT(Node->GetType() == NodeDevice); + break; + } + + Node = NULL; + + } while (Pos != NULL); + + return Node; +} + +CNode* CDeviceView::GetNode(LPTV_ITEMW TvItem) +{ + TvItem->mask = TVIF_PARAM; + if (TreeView_GetItem(m_hTreeView, TvItem)) + { + return (CNode *)TvItem->lParam; + } +} + +CNode* CDeviceView::GetSelectedNode() +{ + TV_ITEM TvItem; + TvItem.hItem = TreeView_GetSelection(m_hTreeView); + return GetNode(&TvItem); +} + +void +CDeviceView::EmptyLists() +{ + POSITION Pos; + CNode *Node; + + if (!m_ClassNodeList.IsEmpty()) + { + Pos = m_ClassNodeList.GetHeadPosition(); + do + { + Node = m_ClassNodeList.GetNext(Pos); + delete Node; + + } while (Pos != NULL); + } + + if (!m_DeviceNodeList.IsEmpty()) + { + Pos = m_DeviceNodeList.GetHeadPosition(); + do + { + Node = m_DeviceNodeList.GetNext(Pos); + delete Node; + + } while (Pos != NULL); + } +} + +bool +CDeviceView::RefreshDeviceList() +{ + GUID ClassGuid; + CNode *Node; + HDEVINFO hDevInfo; + SP_DEVINFO_DATA DeviceInfoData; + DWORD i; + BOOL Success; + + ULONG ClassIndex = 0; + + EmptyLists(); + + do + { + Success = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo); + if (Success) + { + /* Create a new class node */ + Node = new CNode(&ClassGuid, &m_ImageListData); + if (Node->Setup()) + { + m_ClassNodeList.AddTail(Node); + } + } + ClassIndex++; + } while (Success); + + + hDevInfo = SetupDiGetClassDevsW(NULL, + 0, + 0, + DIGCF_PRESENT | DIGCF_ALLCLASSES); + if (hDevInfo == INVALID_HANDLE_VALUE) + { + return false; + } + + + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + for (i = 0;; i++) + { + Success = SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); + if (Success == FALSE) break; + + + Node = new CNode(DeviceInfoData.DevInst, &m_ImageListData); + Node->Setup(); + m_DeviceNodeList.AddTail(Node); + } + + SetupDiDestroyDeviceInfoList(hDevInfo); + + return TRUE; +} \ No newline at end of file diff --git a/reactos/dll/win32/devmgr/devmgmt/DeviceView.h b/reactos/dll/win32/devmgr/devmgmt/DeviceView.h new file mode 100644 index 00000000000..26cbecf5bc6 --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/DeviceView.h @@ -0,0 +1,131 @@ +#pragma once +#include "Node.h" + +enum ViewType +{ + DevicesByType, + DevicesByConnection, + ResourcesByType, + ResourcesByConnection +}; + + +class CDeviceView +{ + CAtlList m_ClassNodeList; + CAtlList m_DeviceNodeList; + + SP_CLASSIMAGELIST_DATA m_ImageListData; + + HWND m_hMainWnd; + HWND m_hTreeView; + HWND m_hPropertyDialog; + HWND m_hShortcutMenu; + ViewType m_ViewType; + + HTREEITEM m_hTreeRoot; + DEVINST m_RootDevInst; + + bool m_ShowHidden; + int m_RootClassImage; + +public: + CDeviceView( + HWND hMainWnd + ); + + ~CDeviceView(void); + + bool Initialize(); + bool Uninitialize(); + + VOID Size( + _In_ int x, + _In_ int y, + _In_ int cx, + _In_ int cy + ); + + VOID Refresh( + _In_ ViewType Type, + _In_ bool ScanForChanges, + _In_ bool UpdateView + ); + + VOID DisplayPropertySheet(); + VOID SetFocus(); + + //VOID SetDeviceListType(ListDevices List) + //{ + // m_ListDevices = List; + //} + + VOID ShowHiddenDevices(_In_ bool ShowHidden) + { + m_ShowHidden = ShowHidden; + } + + ViewType GetCurrentView() { return m_ViewType; } + + bool HasProperties(_In_ LPTV_ITEMW TvItem); + //bool SelDeviceIsHidden(); + bool CanDisable(_In_ LPTV_ITEMW TvItem); + bool IsDisabled(_In_ LPTV_ITEMW TvItem); + bool SelDeviceIsStarted(); + bool SelDeviceIsInstalled(); + +private: + bool AddRootDevice(); + + bool RefreshDeviceList(); + + static unsigned int __stdcall RefreshThread( + void *Param + ); + + bool ListDevicesByConnection( + ); + bool ListDevicesByType( + ); + + bool GetNextClass( + _In_ ULONG ClassIndex, + _Out_ LPGUID ClassGuid, + _Out_ HDEVINFO *hDevInfo + ); + + VOID RecurseChildDevices( + _In_ DEVINST ParentDevice, + _In_ HTREEITEM hParentTreeItem + ); + + bool GetChildDevice( + _In_ DEVINST ParentDevInst, + _Out_ PDEVINST DevInst + ); + + bool GetSiblingDevice( + _In_ DEVINST PrevDevice, + _Out_ PDEVINST DevInst + ); + + HTREEITEM InsertIntoTreeView( + _In_ HTREEITEM hParent, + _In_ CNode *Node + ); + + VOID RecurseDeviceView( + _In_ HTREEITEM hParentItem + ); + + VOID EmptyDeviceView( + ); + + CNode* GetNode(_In_ LPTV_ITEMW TvItem); + CNode* GetSelectedNode(); + + CNode* GetClassNode(_In_ LPGUID ClassGuid); + CNode* GetDeviceNode(_In_ DEVINST Device); + void EmptyLists(); +}; + diff --git a/reactos/dll/win32/devmgr/devmgmt/MainWindow.cpp b/reactos/dll/win32/devmgr/devmgmt/MainWindow.cpp new file mode 100644 index 00000000000..b708b8dd9b2 --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/MainWindow.cpp @@ -0,0 +1,764 @@ +/* + * PROJECT: ReactOS Device Manager + * LICENSE: GPL - See COPYING in the top level directory + * FILE: dll/win32/devmgr/devmgr/MainWindow.cpp + * PURPOSE: Implements the main container window for the device view + * COPYRIGHT: Copyright 2014 - 2015 Ged Murphy + */ + + +#include "stdafx.h" +#include "devmgmt.h" +#include "MainWindow.h" + + +/* DATA *****************************************************/ + +#define BTN_PROPERTIES 0 +#define BTN_SCAN_HARDWARE 1 +#define BTN_SEPERATOR -1 +#define BTN_ENABLE_DRV 2 +#define BTN_DISABLE_DRV 3 +#define BTN_UPDATE_DRV 4 +#define BTN_UNINSTALL_DRV 5 + + +// menu hints +static const MENU_HINT MainMenuHintTable[] = +{ + // File Menu + { IDC_EXIT, IDS_HINT_EXIT }, + + // Action Menu + { IDC_UPDATE_DRV, NULL }, + { IDC_DISABLE_DRV, NULL }, + { IDC_UNINSTALL_DRV, NULL }, + { IDC_SCAN_HARDWARE, IDS_HINT_REFRESH }, + { IDC_ADD_HARDWARE, NULL }, + { IDC_PROPERTIES, IDS_HINT_PROP}, + + // View Menu + { IDC_DEVBYTYPE, IDS_HINT_DEV_BY_TYPE}, + { IDC_DEVBYCONN, IDS_HINT_DEV_BY_CONN}, + { IDC_RESBYTYPE, IDS_HINT_RES_BY_TYPE}, + { IDC_RESBYCONN, IDS_HINT_RES_BY_TYPE}, + + { IDC_ABOUT, IDS_HINT_ABOUT } + +}; + +// system menu hints +static const MENU_HINT SystemMenuHintTable[] = +{ + {SC_RESTORE, IDS_HINT_SYS_RESTORE}, + {SC_MOVE, IDS_HINT_SYS_MOVE}, + {SC_SIZE, IDS_HINT_SYS_SIZE}, + {SC_MINIMIZE, IDS_HINT_SYS_MINIMIZE}, + {SC_MAXIMIZE, IDS_HINT_SYS_MAXIMIZE}, + {SC_CLOSE, IDS_HINT_SYS_CLOSE}, +}; + +static TBBUTTON TbButtons[] = +{ + { BTN_PROPERTIES, IDC_PROPERTIES, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 }, + { BTN_SCAN_HARDWARE, IDC_SCAN_HARDWARE, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 }, + { 2, IDC_STATIC, TBSTATE_ENABLED, BTNS_SEP, 0, 0 }, + { BTN_ENABLE_DRV, IDC_ENABLE_DRV, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 }, + { BTN_DISABLE_DRV, IDC_DISABLE_DRV, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 }, + { BTN_UPDATE_DRV, IDC_UPDATE_DRV, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 }, + { BTN_UNINSTALL_DRV, IDC_UNINSTALL_DRV, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 } +}; + + +/* PUBLIC METHODS **********************************************/ + +CMainWindow::CMainWindow(void) : + m_ToolbarhImageList(NULL), + m_hMainWnd(NULL), + m_hStatusBar(NULL), + m_hToolBar(NULL), + m_CmdShow(0) +{ + m_szMainWndClass = L"DevMgmtWndClass"; +} + +CMainWindow::~CMainWindow(void) +{ + // Destroy any previous list + if (m_ToolbarhImageList) ImageList_Destroy(m_ToolbarhImageList); +} + +bool +CMainWindow::Initialize(LPCTSTR lpCaption, + int nCmdShow) +{ + CAtlStringW szCaption; + WNDCLASSEXW wc = {0}; + + // Store the show window value + m_CmdShow = nCmdShow; + + // Setup the window class struct + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpfnWndProc = MainWndProc; + wc.hInstance = g_hInstance; + wc.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCEW(IDI_MAIN_ICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wc.lpszMenuName = MAKEINTRESOURCEW(IDR_MAINMENU); + wc.lpszClassName = m_szMainWndClass; + wc.hIconSm = (HICON)LoadImage(g_hInstance, + MAKEINTRESOURCE(IDI_MAIN_ICON), + IMAGE_ICON, + 16, + 16, + LR_SHARED); + + // Register the window + if (RegisterClassExW(&wc)) + { + // Create the main window and store the object pointer + m_hMainWnd = CreateWindowExW(WS_EX_WINDOWEDGE, + m_szMainWndClass, + lpCaption, + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + CW_USEDEFAULT, + CW_USEDEFAULT, + 500, + 500, + NULL, + NULL, + g_hInstance, + this); + } + + // Return creation result + return !!(m_hMainWnd); +} + +void +CMainWindow::Uninitialize() +{ + // Unregister the window class + UnregisterClassW(m_szMainWndClass, g_hInstance); +} + +int +CMainWindow::Run() +{ + MSG Msg; + + // Pump the message queue + while (GetMessageW(&Msg, NULL, 0, 0 ) != 0) + { + TranslateMessage(&Msg); + DispatchMessageW(&Msg); + } + + return 0; +} + + +/* PRIVATE METHODS **********************************************/ + +bool +CMainWindow::MainWndMenuHint(WORD CmdId, + const MENU_HINT *HintArray, + DWORD HintsCount, + UINT DefHintId) +{ + bool Found = false; + const MENU_HINT *LastHint; + UINT HintId = DefHintId; + + LastHint = HintArray + HintsCount; + while (HintArray != LastHint) + { + if (HintArray->CmdId == CmdId) + { + HintId = HintArray->HintId; + Found = true; + break; + } + HintArray++; + } + + StatusBarLoadString(m_hStatusBar, + SB_SIMPLEID, + g_hInstance, + HintId); + + return Found; +} + +bool +CMainWindow::RefreshView(ViewType Type) +{ + UINT CheckId; + BOOL bSuccess; + + // Refreshed the cached view + m_DeviceView->Refresh(Type, FALSE, TRUE); + + // Get the menu item id + switch (Type) + { + case DevicesByType: CheckId = IDC_DEVBYTYPE; break; + case DevicesByConnection: CheckId = IDC_DEVBYCONN; break; + case ResourcesByType: CheckId = IDC_RESBYTYPE; break; + case ResourcesByConnection: CheckId = IDC_RESBYCONN; break; + default: ATLASSERT(FALSE); break; + } + + // Set the new check item + bSuccess = CheckMenuRadioItem(m_hMenu, + IDC_DEVBYTYPE, + IDC_RESBYCONN, + CheckId, + MF_BYCOMMAND); + + return TRUE; +} + +bool +CMainWindow::ScanForHardwareChanges() +{ + // Refresh the cache and and display + m_DeviceView->Refresh(m_DeviceView->GetCurrentView(), + true, + true); + return true; +} + + + + +bool +CMainWindow::CreateToolBar() +{ + TBADDBITMAP TbAddBitmap; + INT Index; + + DWORD dwStyles = WS_CHILDWINDOW | TBSTYLE_FLAT | TBSTYLE_WRAPABLE | TBSTYLE_TOOLTIPS | CCS_NODIVIDER; + DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR; + + // Create the toolbar window + m_hToolBar = CreateWindowExW(dwExStyles, + TOOLBARCLASSNAME, + NULL, + dwStyles, + 0, 0, 0, 0, + m_hMainWnd, + (HMENU)IDC_TOOLBAR, + g_hInstance, + NULL); + if (m_hToolBar == NULL) return FALSE; + + // Don't show clipped buttons + SendMessageW(m_hToolBar, + TB_SETEXTENDEDSTYLE, + 0, + TBSTYLE_EX_HIDECLIPPEDBUTTONS); + + SendMessageW(m_hToolBar, TB_SETBITMAPSIZE, 0, MAKELONG(16, 16)); + + // Set the struct size, the toobar needs this... + SendMessageW(m_hToolBar, + TB_BUTTONSTRUCTSIZE, + sizeof(TBBUTTON), + 0); + + TbAddBitmap.hInst = g_hInstance; + TbAddBitmap.nID = IDB_TOOLBAR; + Index = SendMessageW(m_hToolBar, TB_ADDBITMAP, _countof(TbButtons), (LPARAM)&TbAddBitmap); + + SendMessageW(m_hToolBar, TB_ADDBUTTONSW, _countof(TbButtons), (LPARAM)TbButtons); + SendMessageW(m_hToolBar, TB_AUTOSIZE, 0, 0); + + if (TRUE) + { + ShowWindow(m_hToolBar, SW_SHOW); + } + + return TRUE; +} + +bool +CMainWindow::CreateStatusBar() +{ + int StatWidths[] = {110, -1}; // widths of status bar + bool bRet = FALSE; + + // Create the status bar + m_hStatusBar = CreateWindowExW(0, + STATUSCLASSNAME, + NULL, + WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, + 0, 0, 0, 0, + m_hMainWnd, + (HMENU)IDC_STATUSBAR, + g_hInstance, + NULL); + if (m_hStatusBar) + { + // Set the width + bRet = (SendMessageW(m_hStatusBar, + SB_SETPARTS, + sizeof(StatWidths) / sizeof(int), + (LPARAM)StatWidths) != 0); + } + + return bRet; +} + +void CMainWindow::UpdateContext(_In_ LPTV_ITEMW TvItem) +{ + WORD State; + + // properties button + if (m_DeviceView->HasProperties(TvItem)) + { + State = TBSTATE_ENABLED; + } + else + { + State = TBSTATE_HIDDEN; + } + SendMessageW(m_hToolBar, TB_SETSTATE, IDC_PROPERTIES, MAKELPARAM(State, 0)); + SendMessageW(m_hToolBar, TB_SETSTATE, IDC_UPDATE_DRV, MAKELPARAM(State, 0)); //hack + SendMessageW(m_hToolBar, TB_SETSTATE, IDC_UNINSTALL_DRV, MAKELPARAM(State, 0)); // hack + + // enable driver button + if (m_DeviceView->IsDisabled(TvItem)) + { + State = TBSTATE_ENABLED; + } + else + { + State = TBSTATE_HIDDEN; + } + SendMessageW(m_hToolBar, TB_SETSTATE, IDC_ENABLE_DRV, MAKELPARAM(State, 0)); + + // disable driver button + if (m_DeviceView->CanDisable(TvItem) && !m_DeviceView->IsDisabled(TvItem)) + { + State = TBSTATE_ENABLED; + } + else + { + State = TBSTATE_HIDDEN; + } + SendMessageW(m_hToolBar, TB_SETSTATE, IDC_DISABLE_DRV, MAKELPARAM(State, 0)); + + + + + +} + + + +bool +CMainWindow::StatusBarLoadString(IN HWND hStatusBar, + IN INT PartId, + IN HINSTANCE hInstance, + IN UINT uID) +{ + CAtlStringW szMessage; + bool bRet = false; + + // Load the string + if (szMessage.LoadStringW(hInstance, uID)) + { + // Show the string on the status bar + bRet = (SendMessageW(hStatusBar, + SB_SETTEXT, + (WPARAM)PartId, + (LPARAM)szMessage.GetBuffer()) != 0); + } + + return bRet; +} + +LRESULT +CMainWindow::OnCreate(HWND hwnd) +{ + LRESULT RetCode; + + RetCode = -1; + m_hMainWnd = hwnd; + + // Store a handle to the main menu + m_hMenu = GetMenu(m_hMainWnd); + + // Create the toolbar and statusbar + if (CreateToolBar() && CreateStatusBar()) + { + // Create the device view object + m_DeviceView = new CDeviceView(m_hMainWnd); + if (m_DeviceView->Initialize()) + { + // Do the initial scan + ScanForHardwareChanges(); + + // Display the window according to the user request + ShowWindow(hwnd, m_CmdShow); + RetCode = 0; + } + } + + return RetCode; +} + +LRESULT +CMainWindow::OnSize() +{ + RECT rcClient, rcTool, rcStatus; + INT lvHeight, iToolHeight, iStatusHeight; + + // Autosize the toolbar + SendMessage(m_hToolBar, TB_AUTOSIZE, 0, 0); + + // Get the toolbar rect and save the height + GetWindowRect(m_hToolBar, &rcTool); + iToolHeight = rcTool.bottom - rcTool.top; + + // Resize the status bar + SendMessage(m_hStatusBar, WM_SIZE, 0, 0); + + // Get the statusbar rect and save the height + GetWindowRect(m_hStatusBar, &rcStatus); + iStatusHeight = rcStatus.bottom - rcStatus.top; + + // Get the full client rect + GetClientRect(m_hMainWnd, &rcClient); + + // Calculate the remaining height for the treeview + lvHeight = rcClient.bottom - iToolHeight - iStatusHeight; + + // Resize the device view + m_DeviceView->Size(0, + iToolHeight, + rcClient.right, + lvHeight); + + return 0; +} + +LRESULT +CMainWindow::OnNotify(LPARAM lParam) +{ + LPNMHDR NmHdr = (LPNMHDR)lParam; + + switch (NmHdr->code) + { + case TVN_SELCHANGED: + { + LPNMTREEVIEW NmTreeView = (LPNMTREEVIEW)lParam; + UpdateContext(&NmTreeView->itemNew); + break; + } + + case TVN_DELETEITEMW: + { + LPNMTREEVIEW NmTreeView = (LPNMTREEVIEW)lParam; + NmTreeView->action = NmTreeView->action; + + break; + } + + case NM_DBLCLK: + { + m_DeviceView->DisplayPropertySheet(); + break; + } + + case NM_RETURN: + { + m_DeviceView->DisplayPropertySheet(); + break; + } + } + + return 0; +} + +LRESULT +CMainWindow::OnContext(LPARAM lParam) +{ + return 0; +} + +LRESULT +CMainWindow::OnCommand(WPARAM wParam, + LPARAM lParam) +{ + LRESULT RetCode = 0; + WORD Msg; + + // Get the message + Msg = LOWORD(wParam); + + switch (Msg) + { + case IDC_PROPERTIES: + { + m_DeviceView->DisplayPropertySheet(); + break; + } + + case IDC_SCAN_HARDWARE: + { + ScanForHardwareChanges(); + break; + } + + case IDC_DEVBYTYPE: + { + RefreshView(DevicesByType); + break; + } + + case IDC_DEVBYCONN: + { + RefreshView(DevicesByConnection); + break; + } + + case IDC_SHOWHIDDEN: + { + UINT CurCheckState, NewCheckState; + + // Get the current state + CurCheckState = GetMenuState(m_hMenu, IDC_SHOWHIDDEN, MF_BYCOMMAND); + + if (CurCheckState == MF_CHECKED) + { + // Inform the device view of the change + m_DeviceView->ShowHiddenDevices(false); + NewCheckState = MF_UNCHECKED; + } + else if (CurCheckState == MF_UNCHECKED) + { + m_DeviceView->ShowHiddenDevices(true); + NewCheckState = MF_CHECKED; + } + else + { + ATLASSERT(FALSE); + break; + } + + // Set the new check state + CheckMenuItem(m_hMenu, IDC_SHOWHIDDEN, MF_BYCOMMAND | NewCheckState); + + // Refresh the device view + m_DeviceView->Refresh(m_DeviceView->GetCurrentView(), + false, + true); + break; + } + + case IDC_ABOUT: + { + // Apportion blame + MessageBoxW(m_hMainWnd, + L"ReactOS Device Manager\r\nCopyright Ged Murphy 2015", + L"About", + MB_OK | MB_APPLMODAL); + + // Set focus back to the treeview + m_DeviceView->SetFocus(); + break; + } + + case IDC_EXIT: + { + // Post a close message to the window + PostMessageW(m_hMainWnd, + WM_CLOSE, + 0, + 0); + break; + } + + default: + // We didn't handle it + RetCode = -1; + break; + } + + return RetCode; +} + +LRESULT +CMainWindow::OnDestroy() +{ + // Uninitialize the device view + m_DeviceView->Uninitialize(); + + // Kill the object + delete m_DeviceView; + m_DeviceView = NULL; + + // Clear the user data pointer + SetWindowLongPtr(m_hMainWnd, GWLP_USERDATA, 0); + + // Break the message loop + PostQuitMessage(0); + + return 0; +} + +LRESULT CALLBACK +CMainWindow::MainWndProc(HWND hwnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) +{ + CMainWindow *pThis; + LRESULT RetCode = 0; + + // Get the object pointer from window context + pThis = (CMainWindow *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (pThis == NULL) + { + // Check that this isn't a create message + if (msg != WM_CREATE) + { + // Don't handle null info pointer + goto HandleDefaultMessage; + } + } + + switch(msg) + { + case WM_CREATE: + { + // Get the object pointer from the create param + pThis = (CMainWindow *)((LPCREATESTRUCT)lParam)->lpCreateParams; + + // Store the pointer in the window's global user data + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis); + + // Call the create handler + RetCode = pThis->OnCreate(hwnd); + break; + } + + case WM_SIZE: + { + RetCode = pThis->OnSize(); + break; + } + + case WM_NOTIFY: + { + RetCode = pThis->OnNotify(lParam); + break; + } + + case WM_CONTEXTMENU: + { + RetCode = pThis->OnContext(lParam); + break; + } + + case WM_MENUSELECT: + { + if (pThis->m_hStatusBar != NULL) + { + if (!pThis->MainWndMenuHint(LOWORD(wParam), + MainMenuHintTable, + sizeof(MainMenuHintTable) / sizeof(MainMenuHintTable[0]), + IDS_HINT_BLANK)) + { + pThis->MainWndMenuHint(LOWORD(wParam), + SystemMenuHintTable, + sizeof(SystemMenuHintTable) / sizeof(SystemMenuHintTable[0]), + IDS_HINT_BLANK); + } + } + + break; + } + + case WM_COMMAND: + { + // Handle the command message + RetCode = pThis->OnCommand(wParam, lParam); + if (RetCode == -1) + { + // Hand it off to the default message handler + goto HandleDefaultMessage; + } + } + + case WM_CLOSE: + { + // Destroy the main window + DestroyWindow(hwnd); + } + break; + + case WM_DESTROY: + { + // Call the destroy handler + RetCode = pThis->OnDestroy(); + break; + } + + default: + { +HandleDefaultMessage: + RetCode = DefWindowProc(hwnd, msg, wParam, lParam); + break; + } + } + + return RetCode; +} + + +//////// MOVE ME //////////////// + +HINSTANCE g_hInstance = NULL; +HANDLE ProcessHeap = NULL; + +BOOL +WINAPI +DeviceManager_ExecuteW(HWND hWndParent, + HINSTANCE hInst, + LPCWSTR lpMachineName, + int nCmdShow) +{ + CMainWindow MainWindow; + INITCOMMONCONTROLSEX icex; + CAtlStringW szAppName; + int Ret = 1; + + // Store the global values + g_hInstance = hInst; + ProcessHeap = GetProcessHeap(); + + // Initialize common controls + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_BAR_CLASSES | ICC_COOL_CLASSES; + InitCommonControlsEx(&icex); + + // Load the application name + if (szAppName.LoadStringW(g_hInstance, IDS_APPNAME)) + { + // Initialize the main window + if (MainWindow.Initialize(szAppName, nCmdShow)) + { + // Run the application + Ret = MainWindow.Run(); + + // Uninitialize the main window + MainWindow.Uninitialize(); + } + } + + return Ret; +} diff --git a/reactos/dll/win32/devmgr/devmgmt/MainWindow.h b/reactos/dll/win32/devmgr/devmgmt/MainWindow.h new file mode 100644 index 00000000000..ebb0600e78a --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/MainWindow.h @@ -0,0 +1,70 @@ +#pragma once +#include "DeviceView.h" + +typedef struct _MENU_HINT +{ + WORD CmdId; + UINT HintId; +} MENU_HINT, *PMENU_HINT; + +class CMainWindow +{ + CAtlStringW m_szMainWndClass; + CDeviceView *m_DeviceView; + HWND m_hMainWnd; + HWND m_hStatusBar; + HWND m_hToolBar; + HIMAGELIST m_ToolbarhImageList; + HMENU m_hMenu; + int m_CmdShow; + +public: + CMainWindow(void); + ~CMainWindow(void); + + bool Initialize(LPCTSTR lpCaption, int nCmdShow); + int Run(); + void Uninitialize(); + +private: + static LRESULT CALLBACK MainWndProc( + HWND hwnd, + UINT msg, + WPARAM wParam, + LPARAM lParam + ); + + LRESULT OnCreate(HWND hwnd); + LRESULT OnDestroy(); + LRESULT OnSize(); + LRESULT OnNotify(LPARAM lParam); + LRESULT OnContext(LPARAM lParam); + LRESULT OnCommand(WPARAM wParam, LPARAM lParam); + + bool CreateToolBar(); + bool CreateStatusBar(); + + void UpdateContext(_In_ LPTV_ITEMW TvItem); + + bool StatusBarLoadString( + HWND hStatusBar, + INT PartId, + HINSTANCE hInstance, + UINT uID + ); + + bool MainWndMenuHint( + WORD CmdId, + const MENU_HINT *HintArray, + DWORD HintsCount, + UINT DefHintId + ); + + bool RefreshView( + ViewType Type + ); + + bool ScanForHardwareChanges( + ); +}; + diff --git a/reactos/dll/win32/devmgr/devmgmt/Node.cpp b/reactos/dll/win32/devmgr/devmgmt/Node.cpp new file mode 100644 index 00000000000..2167f83e8ec --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/Node.cpp @@ -0,0 +1,375 @@ +/* +* PROJECT: ReactOS Device Manager +* LICENSE: GPL - See COPYING in the top level directory +* FILE: dll/win32/devmgr/devmgr/node.cpp +* PURPOSE: Object for each device in the tree +* COPYRIGHT: Copyright 2015 Ged Murphy +* +*/ + +#include "stdafx.h" +#include "devmgmt.h" +#include "Node.h" + + +/* PUBLIC METHODS *******************************************/ + +CNode::CNode(_In_ LPGUID ClassGuid, + _In_ PSP_CLASSIMAGELIST_DATA ImageListData) : + m_ImageListData(ImageListData), + m_NodeType(NodeClass), + m_DevInst(0), + m_DeviceId(NULL), + m_ClassImage(0), + m_Status(0), + m_ProblemNumber(0), + m_OverlayImage(0) +{ + m_DisplayName[0] = UNICODE_NULL; + CopyMemory(&m_ClassGuid, ClassGuid, sizeof(GUID)); +} + +CNode::CNode(_In_opt_ DEVINST Device, + _In_ PSP_CLASSIMAGELIST_DATA ImageListData) : + m_ImageListData(ImageListData), + m_NodeType(NodeDevice), + m_DevInst(Device), + m_DeviceId(NULL), + m_ClassImage(0), + m_Status(0), + m_ProblemNumber(0), + m_OverlayImage(0) +{ + m_DisplayName[0] = UNICODE_NULL; + CopyMemory(&m_ClassGuid, &GUID_NULL, sizeof(GUID)); +} + +CNode::~CNode() +{ + Cleanup(); +} + +bool +CNode::Setup() +{ + // TODO: Make this polymorphic + + if (m_NodeType == NodeClass) + { + return SetupClassNode(); + } + else if (m_NodeType == NodeDevice) + { + return SetupDeviceNode(); + } + + return FALSE; +} + +bool +CNode::HasProperties() +{ + return (m_DeviceId != NULL); +} + +bool +CNode::IsHidden() +{ + return ((m_Status & DN_NO_SHOW_IN_DM) != 0); +} + +bool +CNode::CanDisable() +{ + return (m_NodeType == NodeDevice && ((m_Status & DN_DISABLEABLE) != 0)); +} + +bool +CNode::IsDisabled() +{ + return ((m_ProblemNumber & (CM_PROB_DISABLED | CM_PROB_HARDWARE_DISABLED)) != 0); +} + +bool +CNode::IsStarted() +{ + return ((m_Status & DN_STARTED) != 0); +} + +bool +CNode::IsInstalled() +{ + return ((m_Status & DN_HAS_PROBLEM) != 0 || + (m_Status & (DN_DRIVER_LOADED | DN_STARTED)) != 0); +} + + +/* PRIVATE METHODS ******************************************/ + +bool +CNode::SetupClassNode() +{ + DWORD RequiredSize, Type, Size; + DWORD Success; + HKEY hKey; + + // Open the registry key for this class + hKey = SetupDiOpenClassRegKeyExW(&m_ClassGuid, + MAXIMUM_ALLOWED, + DIOCR_INSTALLER, + NULL, + 0); + if (hKey != INVALID_HANDLE_VALUE) + { + Size = DISPLAY_NAME_LEN; + Type = REG_SZ; + + // Lookup the class description (win7+) + Success = RegQueryValueExW(hKey, + L"ClassDesc", + NULL, + &Type, + (LPBYTE)m_DisplayName, + &Size); + if (Success == ERROR_SUCCESS) + { + // Check if the string starts with an @ + if (m_DisplayName[0] == L'@') + { + // The description is located in a module resource + Success = ConvertResourceDescriptorToString(m_DisplayName, DISPLAY_NAME_LEN); + } + } + else if (Success == ERROR_FILE_NOT_FOUND) + { + // WinXP stores the description in the default value + Success = RegQueryValueExW(hKey, + NULL, + NULL, + &Type, + (LPBYTE)m_DisplayName, + &Size); + } + + // Close the registry key + RegCloseKey(hKey); + } + else + { + Success = GetLastError(); + } + + // Check if we failed to get the class description + if (Success != ERROR_SUCCESS) + { + // Use the class name as the description + RequiredSize = DISPLAY_NAME_LEN; + (VOID)SetupDiClassNameFromGuidW(&m_ClassGuid, + m_DisplayName, + RequiredSize, + &RequiredSize); + } + + // Get the image index for this class + (VOID)SetupDiGetClassImageIndex(m_ImageListData, + &m_ClassGuid, + &m_ClassImage); + + return true; +} + +bool +CNode::SetupDeviceNode() +{ + WCHAR ClassGuidString[MAX_GUID_STRING_LEN]; + ULONG ulLength; + CONFIGRET cr; + +// ATLASSERT(m_DeviceId == NULL); + + // Get the length of the device id string + cr = CM_Get_Device_ID_Size(&ulLength, m_DevInst, 0); + if (cr == CR_SUCCESS) + { + // We alloc heap here because this will be stored in the lParam of the TV + m_DeviceId = (LPWSTR)HeapAlloc(GetProcessHeap(), + 0, + (ulLength + 1) * sizeof(WCHAR)); + if (m_DeviceId) + { + // Now get the actual device id + cr = CM_Get_Device_IDW(m_DevInst, + m_DeviceId, + ulLength + 1, + 0); + if (cr != CR_SUCCESS) + { + HeapFree(GetProcessHeap(), 0, m_DeviceId); + m_DeviceId = NULL; + } + } + } + + // Make sure we got the string + if (m_DeviceId == NULL) + return false; + + // Get the current status of the device + cr = CM_Get_DevNode_Status_Ex(&m_Status, + &m_ProblemNumber, + m_DevInst, + 0, + NULL); + if (cr != CR_SUCCESS) + { + HeapFree(GetProcessHeap(), 0, m_DeviceId); + m_DeviceId = NULL; + return false; + } + + // Check if the device has a problem + if (m_Status & DN_HAS_PROBLEM) + { + m_OverlayImage = 1; + } + + // The disabled overlay takes precidence over the problem overlay + if (m_ProblemNumber & (CM_PROB_DISABLED | CM_PROB_HARDWARE_DISABLED)) + { + m_OverlayImage = 2; + } + + + // Get the class guid for this device + ulLength = MAX_GUID_STRING_LEN * sizeof(WCHAR); + cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst, + CM_DRP_CLASSGUID, + NULL, + ClassGuidString, + &ulLength, + 0); + if (cr == CR_SUCCESS) + { + // Convert the string to a proper guid + CLSIDFromString(ClassGuidString, &m_ClassGuid); + } + else + { + // It's a device with no driver + m_ClassGuid = GUID_DEVCLASS_UNKNOWN; + } + + + // Get the image for the class this device is in + SetupDiGetClassImageIndex(m_ImageListData, + &m_ClassGuid, + &m_ClassImage); + + // Get the description for the device + ulLength = DISPLAY_NAME_LEN * sizeof(WCHAR); + cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst, + CM_DRP_FRIENDLYNAME, + NULL, + m_DisplayName, + &ulLength, + 0); + if (cr != CR_SUCCESS) + { + ulLength = DISPLAY_NAME_LEN * sizeof(WCHAR); + cr = CM_Get_DevNode_Registry_PropertyW(m_DevInst, + CM_DRP_DEVICEDESC, + NULL, + m_DisplayName, + &ulLength, + 0); + + } + + // Cleanup if something failed + if (cr != CR_SUCCESS) + { + HeapFree(GetProcessHeap(), 0, m_DeviceId); + m_DeviceId = NULL; + } + + return (cr == CR_SUCCESS ? true : false); +} + +void +CNode::Cleanup() +{ + if (m_DeviceId) + { + HeapFree(GetProcessHeap(), 0, m_DeviceId); + m_DeviceId = NULL; + } +} + +DWORD +CNode::ConvertResourceDescriptorToString( + _Inout_z_ LPWSTR ResourceDescriptor, + _In_ DWORD ResourceDescriptorSize +) +{ + WCHAR ModulePath[MAX_PATH]; + WCHAR ResString[256]; + INT ResourceId; + HMODULE hModule; + LPWSTR ptr; + DWORD Size; + DWORD dwError; + + + // First check for a semi colon */ + ptr = wcschr(ResourceDescriptor, L';'); + if (ptr) + { + // This must be an inf based descriptor, the desc is after the semi colon + wcscpy_s(ResourceDescriptor, ResourceDescriptorSize, ++ptr); + dwError = ERROR_SUCCESS; + } + else + { + // This must be a dll resource based descriptor. Find the comma + ptr = wcschr(ResourceDescriptor, L','); + if (ptr == NULL) return ERROR_INVALID_DATA; + + // Terminate the string where the comma was + *ptr = UNICODE_NULL; + + // Expand any environment strings + Size = ExpandEnvironmentStringsW(&ResourceDescriptor[1], ModulePath, MAX_PATH); + if (Size > MAX_PATH) return ERROR_BUFFER_OVERFLOW; + if (Size == 0) return GetLastError(); + + // Put the comma back and move past it + *ptr = L','; + ptr++; + + // Load the dll + hModule = LoadLibraryExW(ModulePath, NULL, LOAD_LIBRARY_AS_DATAFILE); + if (hModule == NULL) return GetLastError(); + + // Convert the resource id to a number + ResourceId = _wtoi(ptr); + + // If the number is negative, make it positive + if (ResourceId < 0) ResourceId = -ResourceId; + + // Load the string from the dll + if (LoadStringW(hModule, ResourceId, ResString, 256)) + { + wcscpy_s(ResourceDescriptor, ResourceDescriptorSize, ResString); + dwError = ERROR_SUCCESS; + } + else + { + dwError = GetLastError(); + } + + // Free the library + FreeLibrary(hModule); + } + + return dwError; +} diff --git a/reactos/dll/win32/devmgr/devmgmt/Node.h b/reactos/dll/win32/devmgr/devmgmt/Node.h new file mode 100644 index 00000000000..2a858bbd409 --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/Node.h @@ -0,0 +1,78 @@ +#pragma once + +#define DISPLAY_NAME_LEN 256 +#define ROOT_NAME_SIZE MAX_COMPUTERNAME_LENGTH + 1 + +enum NodeType +{ + NodeClass, + NodeDevice +}; + +typedef ULONG Actions; +#define Update 0x01 +#define Enable 0x02 +#define Disable 0x04 +#define Uninstall 0x08 + + +class CNode +{ +private: + PSP_CLASSIMAGELIST_DATA m_ImageListData; + NodeType m_NodeType; + DEVINST m_DevInst; + Actions m_Actions; + LPWSTR m_DeviceId; + WCHAR m_DisplayName[DISPLAY_NAME_LEN]; + GUID m_ClassGuid; + INT m_ClassImage; + ULONG m_Status; + ULONG m_ProblemNumber; + INT m_OverlayImage; + +public: + CNode( + _In_ LPGUID ClassGuid, + _In_ PSP_CLASSIMAGELIST_DATA ImageListData + ); + + CNode( + _In_ DEVINST Device, + _In_ PSP_CLASSIMAGELIST_DATA ImageListData + ); + + ~CNode(); + + bool Setup(); + + LPGUID GetClassGuid() { return &m_ClassGuid; } + DEVINST GetDeviceInst() { return m_DevInst; } + + LPWSTR GetDisplayName() { return m_DisplayName; } + INT GetClassImage() { return m_ClassImage; } + INT GetOverlayImage() { return m_OverlayImage; } + LPWSTR GetDeviceId() { return m_DeviceId; } + Actions GetActions() { return m_Actions; } + + bool HasProblem() { return !!(m_ProblemNumber); } + bool HasProperties(); + bool IsHidden(); + bool CanDisable(); + bool IsDisabled(); + bool IsStarted(); + bool IsInstalled(); + bool CanInstall() { return TRUE; } // unimplemented + bool CanUninstall() { return TRUE; } // unimplemented + +private: + bool SetupClassNode(); + bool SetupDeviceNode(); + void Cleanup(); + + DWORD ConvertResourceDescriptorToString( + _Inout_z_ LPWSTR ResourceDescriptor, + _In_ DWORD ResourceDescriptorSize + ); +}; + diff --git a/reactos/dll/win32/devmgr/devmgmt/Resource.h b/reactos/dll/win32/devmgr/devmgmt/Resource.h new file mode 100644 index 00000000000..3184e152acf --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/Resource.h @@ -0,0 +1,63 @@ +#define IDC_STATIC -1 + +#define IDI_MAIN_ICON 20 +#define IDB_ROOT_IMAGE 21 +#define IDB_TOOLBAR 22 + +/* windows */ +#define IDC_TREEVIEW 50 +#define IDC_TOOLBAR 51 +#define IDC_STATUSBAR 52 +#define IDR_MAINMENU 53 +#define IDR_POPUP 54 + +/* Actions */ +#define IDC_PROPERTIES 100 +#define IDC_SCAN_HARDWARE 101 +#define IDC_ENABLE_DRV 102 +#define IDC_DISABLE_DRV 103 +#define IDC_UPDATE_DRV 104 +#define IDC_UNINSTALL_DRV 105 +#define IDC_ADD_HARDWARE 106 +#define IDC_ABOUT 107 +#define IDC_EXIT 108 + +/* view menu */ +#define IDC_DEVBYTYPE 200 +#define IDC_DEVBYCONN 201 +#define IDC_RESBYTYPE 202 +#define IDC_RESBYCONN 203 +#define IDC_SHOWHIDDEN 204 + +/* about box info */ +#define IDD_ABOUTBOX 300 +#define IDC_LICENSE_EDIT 301 +#define IDS_APPNAME 302 +#define IDS_LICENSE 303 + +/* tooltips */ +#define IDS_TOOLTIP_PROP 6000 +#define IDS_TOOLTIP_REFRESH 6001 +#define IDS_TOOLTIP_HELP 6002 + +/* menu hints */ +#define IDS_HINT_BLANK 20000 +#define IDS_HINT_REFRESH 20002 +#define IDS_HINT_PROP 20003 +#define IDS_HINT_HELP 20004 +#define IDS_HINT_ABOUT 20005 +#define IDS_HINT_EXIT 20006 + +#define IDS_HINT_DEV_BY_TYPE 20020 +#define IDS_HINT_DEV_BY_CONN 20021 +#define IDS_HINT_RES_BY_TYPE 20022 +#define IDS_HINT_RES_BY_CONN 20023 + +/* system menu hints */ +#define IDS_HINT_SYS_RESTORE 21001 +#define IDS_HINT_SYS_MOVE 21002 +#define IDS_HINT_SYS_SIZE 21003 +#define IDS_HINT_SYS_MINIMIZE 21004 +#define IDS_HINT_SYS_MAXIMIZE 21005 +#define IDS_HINT_SYS_CLOSE 21006 + diff --git a/reactos/dll/win32/devmgr/devmgmt/devmgmt.h b/reactos/dll/win32/devmgr/devmgmt/devmgmt.h new file mode 100644 index 00000000000..f8abae2728a --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/devmgmt.h @@ -0,0 +1,9 @@ +#pragma once +#define WIN32_LEAN_AND_MEAN +#include +#pragma once +#include "resource.h" + +extern HINSTANCE g_hInstance; +extern HANDLE ProcessHeap; + diff --git a/reactos/dll/win32/devmgr/devmgmt/lang/en-US.rc b/reactos/dll/win32/devmgr/devmgmt/lang/en-US.rc new file mode 100644 index 00000000000..caf82ca4c4c --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/lang/en-US.rc @@ -0,0 +1,72 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +IDR_MAINMENU MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "E&xit", IDC_EXIT + END + POPUP "Action" + BEGIN + MENUITEM "Update driver software..." IDC_UPDATE_DRV + MENUITEM "Disable" IDC_DISABLE_DRV + MENUITEM "Uninstall" IDC_UNINSTALL_DRV + MENUITEM SEPARATOR + MENUITEM "Scan for hardware changes" IDC_SCAN_HARDWARE + MENUITEM "Add hardware" IDC_ADD_HARDWARE, GRAYED + MENUITEM SEPARATOR + MENUITEM "Properties", IDC_PROPERTIES + END + POPUP "View" + BEGIN + MENUITEM "Devices by type", IDC_DEVBYTYPE + MENUITEM "Devices by connection", IDC_DEVBYCONN + MENUITEM "Resources by type", IDC_RESBYTYPE, GRAYED + MENUITEM "Resources by connection", IDC_RESBYCONN, GRAYED + MENUITEM SEPARATOR + MENUITEM "Show hidden devices", IDC_SHOWHIDDEN + END + POPUP "Help" + BEGIN + MENUITEM "About", IDC_ABOUT + END +END + +IDR_POPUP MENU +BEGIN + POPUP "popup" + BEGIN + MENUITEM "Properties", IDC_PROPERTIES, GRAYED + END +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_TOOLTIP_PROP "Properties" + IDS_TOOLTIP_REFRESH "Scan for hardware changes" + IDS_TOOLTIP_HELP "Help" +END + +/* Hints */ +STRINGTABLE DISCARDABLE +BEGIN + IDS_APPNAME "ReactOS Device Manager" + IDS_HINT_BLANK " " + IDS_HINT_EXIT " Exits the program." + IDS_HINT_REFRESH " Scan for changed or new Plug and Play devices." + IDS_HINT_PROP " Open property dialog for the current selection." + IDS_HINT_HELP " Display help window." + IDS_HINT_ABOUT " About ReactOS Device Manager." + + IDS_HINT_DEV_BY_TYPE "Displays devices by hardware type" + IDS_HINT_DEV_BY_CONN "Displays devices by connection" + IDS_HINT_RES_BY_TYPE "Diaplays resources by type" + IDS_HINT_RES_BY_CONN "Displays resources by connection type" + + IDS_HINT_SYS_RESTORE " Restores this window to normal size." + IDS_HINT_SYS_MOVE " Moves this window." + IDS_HINT_SYS_SIZE " Resizes this window." + IDS_HINT_SYS_MINIMIZE " Collapses this window to an icon." + IDS_HINT_SYS_MAXIMIZE " Expands this window to fill this screen." + IDS_HINT_SYS_CLOSE " Closes this window." +END diff --git a/reactos/dll/win32/devmgr/devmgmt/res/computer.ico b/reactos/dll/win32/devmgr/devmgmt/res/computer.ico new file mode 100644 index 00000000000..56edfcbb2af Binary files /dev/null and b/reactos/dll/win32/devmgr/devmgmt/res/computer.ico differ diff --git a/reactos/dll/win32/devmgr/devmgmt/res/root.bmp b/reactos/dll/win32/devmgr/devmgmt/res/root.bmp new file mode 100644 index 00000000000..e2734fb590b Binary files /dev/null and b/reactos/dll/win32/devmgr/devmgmt/res/root.bmp differ diff --git a/reactos/dll/win32/devmgr/devmgmt/res/toolbar.bmp b/reactos/dll/win32/devmgr/devmgmt/res/toolbar.bmp new file mode 100644 index 00000000000..c7dd5aca9df Binary files /dev/null and b/reactos/dll/win32/devmgr/devmgmt/res/toolbar.bmp differ diff --git a/reactos/dll/win32/devmgr/devmgmt/rsrc.rc b/reactos/dll/win32/devmgr/devmgmt/rsrc.rc new file mode 100644 index 00000000000..2d5011127c0 --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/rsrc.rc @@ -0,0 +1,80 @@ +#include +#include "resource.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +IDI_MAIN_ICON ICON "res/computer.ico" +IDB_ROOT_IMAGE BITMAP "res/root.bmp" + +/* main toolbar icons */ +IDB_TOOLBAR BITMAP DISCARDABLE "res/toolbar.bmp" + +// UTF-8 +#pragma code_page(65001) + +#ifdef LANGUAGE_BG_BG + #include "lang/bg-BG.rc" +#endif +#ifdef LANGUAGE_DE_DE + #include "lang/de-DE.rc" +#endif +#ifdef LANGUAGE_EL_GR + #include "lang/el-GR.rc" +#endif +#ifdef LANGUAGE_EN_US + #include "lang/en-US.rc" +#endif +#ifdef LANGUAGE_ES_ES + #include "lang/es-ES.rc" +#endif +#ifdef LANGUAGE_FR_FR + #include "lang/fr-FR.rc" +#endif +#ifdef LANGUAGE_HE_IL + #include "lang/he-IL.rc" +#endif +#ifdef LANGUAGE_ID_ID + #include "lang/id-ID.rc" +#endif +#ifdef LANGUAGE_IT_IT + #include "lang/it-IT.rc" +#endif +#ifdef LANGUAGE_JA_JP + #include "lang/ja-JP.rc" +#endif +#ifdef LANGUAGE_KO_KR + #include "lang/ko-KR.rc" +#endif +#ifdef LANGUAGE_NB_NO + #include "lang/no-NO.rc" +#endif +#ifdef LANGUAGE_PL_PL + #include "lang/pl-PL.rc" +#endif +#ifdef LANGUAGE_PT_BR + #include "lang/pt-BR.rc" +#endif +#ifdef LANGUAGE_RO_RO + #include "lang/ro-RO.rc" +#endif +#ifdef LANGUAGE_RU_RU + #include "lang/ru-RU.rc" +#endif +#ifdef LANGUAGE_SK_SK + #include "lang/sk-SK.rc" +#endif +#ifdef LANGUAGE_SV_SE + #include "lang/sv-SE.rc" +#endif +#ifdef LANGUAGE_TH_TH + #include "lang/th-TH.rc" +#endif +#ifdef LANGUAGE_TR_TR + #include "lang/tr-TR.rc" +#endif +#ifdef LANGUAGE_UK_UA + #include "lang/uk-UA.rc" +#endif +#ifdef LANGUAGE_ZH_CN + #include "lang/zh-CN.rc" +#endif diff --git a/reactos/dll/win32/devmgr/devmgmt/stdafx.h b/reactos/dll/win32/devmgr/devmgmt/stdafx.h new file mode 100644 index 00000000000..e233e57063c --- /dev/null +++ b/reactos/dll/win32/devmgr/devmgmt/stdafx.h @@ -0,0 +1,50 @@ +#pragma once + +#ifndef __REACTOS___ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit +#include +#include +#include +#include + +#else + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#endif \ No newline at end of file