From a676d524c9f2f358d87fcdf6247d50d68fb3158f Mon Sep 17 00:00:00 2001 From: Ged Murphy Date: Tue, 16 Jun 2015 21:13:28 +0000 Subject: [PATCH] [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 --- .../dll/win32/devmgr/devmgmt/DeviceView.cpp | 873 ++++++++++++++++++ reactos/dll/win32/devmgr/devmgmt/DeviceView.h | 131 +++ .../dll/win32/devmgr/devmgmt/MainWindow.cpp | 764 +++++++++++++++ reactos/dll/win32/devmgr/devmgmt/MainWindow.h | 70 ++ reactos/dll/win32/devmgr/devmgmt/Node.cpp | 375 ++++++++ reactos/dll/win32/devmgr/devmgmt/Node.h | 78 ++ reactos/dll/win32/devmgr/devmgmt/Resource.h | 63 ++ reactos/dll/win32/devmgr/devmgmt/devmgmt.h | 9 + .../dll/win32/devmgr/devmgmt/lang/en-US.rc | 72 ++ .../dll/win32/devmgr/devmgmt/res/computer.ico | Bin 0 -> 40070 bytes reactos/dll/win32/devmgr/devmgmt/res/root.bmp | Bin 0 -> 822 bytes .../dll/win32/devmgr/devmgmt/res/toolbar.bmp | Bin 0 -> 6198 bytes reactos/dll/win32/devmgr/devmgmt/rsrc.rc | 80 ++ reactos/dll/win32/devmgr/devmgmt/stdafx.h | 50 + 14 files changed, 2565 insertions(+) create mode 100644 reactos/dll/win32/devmgr/devmgmt/DeviceView.cpp create mode 100644 reactos/dll/win32/devmgr/devmgmt/DeviceView.h create mode 100644 reactos/dll/win32/devmgr/devmgmt/MainWindow.cpp create mode 100644 reactos/dll/win32/devmgr/devmgmt/MainWindow.h create mode 100644 reactos/dll/win32/devmgr/devmgmt/Node.cpp create mode 100644 reactos/dll/win32/devmgr/devmgmt/Node.h create mode 100644 reactos/dll/win32/devmgr/devmgmt/Resource.h create mode 100644 reactos/dll/win32/devmgr/devmgmt/devmgmt.h create mode 100644 reactos/dll/win32/devmgr/devmgmt/lang/en-US.rc create mode 100644 reactos/dll/win32/devmgr/devmgmt/res/computer.ico create mode 100644 reactos/dll/win32/devmgr/devmgmt/res/root.bmp create mode 100644 reactos/dll/win32/devmgr/devmgmt/res/toolbar.bmp create mode 100644 reactos/dll/win32/devmgr/devmgmt/rsrc.rc create mode 100644 reactos/dll/win32/devmgr/devmgmt/stdafx.h 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 0000000000000000000000000000000000000000..56edfcbb2af172e924f1fc4aec31ab71134d2087 GIT binary patch literal 40070 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%0AO7-d`THOXlM)s&!u}j&q4s|VC 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 literal 0 HcmV?d00001 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