/*++ Copyright (C) Microsoft Corporation Module Name: hwtab.cpp Abstract: implement the hardware tab functions and UI. Author: William Hsieh (williamh) created Revision History: --*/ #include "devmgr.h" #include #include #include #include #define THIS_DLL g_hInstance /***************************************************************************** * * Exported stuff * *****************************************************************************/ // Stuff in api.h that we can't #include because api.h can't be #include'd // by anyone other than api.cpp. STDAPI_(int) DevicePropertiesExA( HWND hwndParent, LPCSTR MachineName, LPCSTR DeviceID, DWORD Flags, BOOL ShowDeviceTree ); STDAPI_(int) DevicePropertiesExW( HWND hwndParent, LPCWSTR MachineName, LPCWSTR DeviceID, DWORD Flags, BOOL ShowDeviceTree ); STDAPI_(int) DeviceProblemWizardA( HWND hwndParent, LPCSTR MachineName, LPCSTR DeviceId ); STDAPI_(int) DeviceProblemWizardW( HWND hwndParent, LPCWSTR MachineName, LPCWSTR DeviceId ); STDAPI_(UINT) DeviceProblemTextA( HMACHINE hMachine, DEVNODE DevNode, ULONG ProblemNumber, LPSTR Buffer, UINT BufferSize ); STDAPI_(UINT) DeviceProblemTextW( HMACHINE hMachine, DEVNODE DevNode, ULONG ProblemNumber, LPWSTR Buffer, UINT BufferSize ); #define DevicePropertiesEx DevicePropertiesExW #define DeviceProblemWizard DeviceProblemWizardW #define DeviceProblemText DeviceProblemTextW /***************************************************************************** * * General remark about SetupDi functions * * Windows NT and Windows 98 implement many of the SetupDi query * functions differently if you are querying for the buffer size. * * Windows 98 returns FALSE, and GetLastError() returns * ERROR_INSUFFICIENT_BUFFER. * * Windows NT returns TRUE. * * So all calls to SetupDi functions that do querying for the buffer * size should be wrapped with BUFFERQUERY_SUCCEEDED. * *****************************************************************************/ #define BUFFERQUERY_SUCCEEDED(f) \ ((f) || GetLastError() == ERROR_INSUFFICIENT_BUFFER) /***************************************************************************** * * Context help * *****************************************************************************/ #include "devgenpg.h" #define idh_devmgr_hardware_trblsht 400100 #define idh_devmgr_hardware_properties 400200 #define idh_devmgr_hardware_listview 400300 const DWORD c_HWTabHelpIDs[] = { IDC_HWTAB_LVSTATIC, idh_devmgr_hardware_listview, IDC_HWTAB_LISTVIEW, idh_devmgr_hardware_listview, IDC_HWTAB_GROUPBOX, IDH_DISABLEHELP, IDC_HWTAB_MFG, idh_devmgr_general_manufacturer, IDC_HWTAB_LOC, idh_devmgr_general_location, IDC_HWTAB_STATUS, idh_devmgr_general_device_status, IDC_HWTAB_TSHOOT, idh_devmgr_hardware_trblsht, IDC_HWTAB_PROP, idh_devmgr_hardware_properties, 0, 0 }; typedef TCHAR TLINE[LINE_LEN]; typedef struct { int devClass; int dsaItem; } LISTITEM, *LPLISTITEM; typedef struct { GUID devGuid; // device class guid we are managing TLINE tszClass; // Array of friendly name of class HDSA hdsaDinf; // array of SP_DEVINFO_DATA structures HDEVINFO hdev; // hdsaDinfo refers to this int iImage; // image index within master imagelist } CLASSDATA, *LPCLASSDATA; /***************************************************************************** * * CHWTab * * The Hardware Tab page. * *****************************************************************************/ class CHWTab { private: CHWTab(const GUID *pguid, int iNumClass, DWORD dwViewMode); ~CHWTab(); void *operator new(size_t cb) { return LocalAlloc(LPTR, cb); } void operator delete(void *p) { LocalFree(p); } void RebuildDeviceList(); void Reset(); BOOL GetDeviceRegistryProperty(HDEVINFO hDev, DWORD dwProp, PSP_DEVINFO_DATA pdinf, LPTSTR ptsz, DWORD ctch); void SprintfItem(UINT ids, UINT idc, LPCTSTR ptszText); static INT_PTR CALLBACK DialogProc(HWND hdlg, UINT wm, WPARAM wp, LPARAM lp); static LRESULT CALLBACK ParentSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp, UINT_PTR uidSubclass, DWORD_PTR dwRefData); friend HWND DeviceCreateHardwarePage(HWND hwndParent, const GUID *pguid); friend HWND DeviceCreateHardwarePageEx(HWND hwndParent, const GUID *pguid, int iNumClass, DWORD dwViewMode); BOOL OnInitDialog(HWND hdlg); void RemoveListItems(HWND hwndList); void OnItemChanged(LPNMLISTVIEW pnmlv); void OnProperties(void); void OnTshoot(void); void OnSetText(LPCTSTR ptszText); void OnHelp(LPHELPINFO phi); void OnContextMenu(HWND hwnd); void SetControlPositions(int idcFirst, int idcLast, int dx, int dy, UINT flags); // // Helpers for SetWindowPositions. // void GrowControls(int idcFirst, int idcLast, int dx, int dy) { SetControlPositions(idcFirst, idcLast, dx, dy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); } void ShiftControls(int idcFirst, int idcLast, int dx, int dy) { SetControlPositions(idcFirst, idcLast, dx, dy, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); } void RepositionControls(); inline PSP_DEVINFO_DATA GetPdinf(LPLISTITEM pListItem) { return (PSP_DEVINFO_DATA)DSA_GetItemPtr(_pCD[pListItem->devClass].hdsaDinf, pListItem->dsaItem); } private: HWND _hdlg; // the dialog box itself HWND _hwndList; // The listview int _iNumClass; // Number of class guids DWORD _dwViewMode; // Dictates size of list box LPCLASSDATA _pCD; // Class data for each devClass to represent SP_CLASSIMAGELIST_DATA _imageListData; // Class image list data }; // // Constructor. // CHWTab::CHWTab(const GUID *pguid, int iNumClass, DWORD dwViewMode) : _pCD(NULL) { // Since the _dwViewMode is a devisor, we need to make sure it's valid _imageListData.ImageList = NULL; _dwViewMode = dwViewMode; if (_dwViewMode < HWTAB_LARGELIST) { _dwViewMode = HWTAB_LARGELIST; } if (_dwViewMode > HWTAB_SMALLLIST) { _dwViewMode = HWTAB_SMALLLIST; } _iNumClass = iNumClass; _pCD = new CLASSDATA[_iNumClass]; if (_pCD && pguid) { DWORD cbRequired; memset(_pCD, 0, sizeof(CLASSDATA) * _iNumClass); int devClass; for (devClass = 0; devClass < _iNumClass; devClass++) { _pCD[devClass].hdev = INVALID_HANDLE_VALUE; _pCD[devClass].devGuid = (GUID) pguid[devClass]; } //get the driver class image list _imageListData.cbSize = sizeof(SP_CLASSIMAGELIST_DATA); if (!SetupDiGetClassImageList(&_imageListData)) { _imageListData.ImageList = NULL; } for (devClass = 0; devClass < _iNumClass; devClass++) { _pCD[devClass].iImage = -1; SetupDiGetClassDescription(&_pCD[devClass].devGuid, _pCD[devClass].tszClass, sizeof(TLINE), &cbRequired); if (_imageListData.ImageList) { // Get the image index for our little guy int iImageIndex; if (SetupDiGetClassImageIndex(&_imageListData, &_pCD[devClass].devGuid, &iImageIndex)) { _pCD[devClass].iImage = iImageIndex; } } } } } CHWTab::~CHWTab() { Reset(); if (_imageListData.ImageList) { SetupDiDestroyClassImageList(&_imageListData); } if (_pCD) { delete _pCD; _pCD = NULL; } } // // Return to normal, ready for the next go-round. This also frees all // dynamically allocated stuff. // void CHWTab::Reset() { int devClass; for (devClass = 0; devClass < _iNumClass; devClass++) { if (_pCD[devClass].hdsaDinf) { DSA_Destroy(_pCD[devClass].hdsaDinf); _pCD[devClass].hdsaDinf = NULL; } if (_pCD[devClass].hdev != INVALID_HANDLE_VALUE) { SetupDiDestroyDeviceInfoList(_pCD[devClass].hdev); _pCD[devClass].hdev = INVALID_HANDLE_VALUE; } } } // // Helper function that calls SetupDiGetDeviceRegistryProperty // and copes with things like detecting the various error modes // properly. // BOOL CHWTab::GetDeviceRegistryProperty(HDEVINFO hDev, DWORD dwProp, PSP_DEVINFO_DATA pdinf, LPTSTR ptsz, DWORD ctch) { DWORD cbRequired; ptsz[0] = TEXT('\0'); SetupDiGetDeviceRegistryProperty(hDev, pdinf, dwProp, 0, (LPBYTE)ptsz, ctch * sizeof(TCHAR), &cbRequired); return ptsz[0]; } // // Change the size/position of controls idcFirst through idcLast. // Change the size/position by (dx, dy). // flags specifies what exactly is changing. // void CHWTab::SetControlPositions(int idcFirst, int idcLast, int dx, int dy, UINT flags) { HDWP hdwp = BeginDeferWindowPos(idcLast - idcFirst + 1); for (int idc = idcFirst; idc <= idcLast; idc++) { if (hdwp) { RECT rc; HWND hwnd = GetDlgItem(_hdlg, idc); GetWindowRect(hwnd, &rc); MapWindowRect(HWND_DESKTOP, _hdlg, &rc); hdwp = DeferWindowPos(hdwp, hwnd, NULL, rc.left + dx, rc.top + dy, rc.right - rc.left + dx, rc.bottom - rc.top + dy, flags); } } if (hdwp) { EndDeferWindowPos(hdwp); } } // // Reposition and resize our controls based on the size we need to be. // void CHWTab::RepositionControls() { // // First, see how much slack space we have. // RECT rcDlg, rcParent; GetClientRect(_hdlg, &rcDlg); GetClientRect(GetParent(_hdlg), &rcParent); // // Make ourselves as big as our parent. // SetWindowPos(_hdlg, NULL, 0, 0, rcParent.right, rcParent.bottom, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); // // Now do a little more math... // int cyExtra = rcParent.bottom - rcDlg.bottom; int cxExtra = rcParent.right - rcDlg.right; // // The extra vertical space is split between the listview and // the groupbox. The amount of split is determined by _dwViewMode. // Larger modes give more and more space to the listview. // int cyTop = cyExtra / _dwViewMode; int cyBottom = cyExtra - cyTop; // // Horizontally grow the controls that reach the full width of the // dialog box. // GrowControls(IDC_HWTAB_HSIZEFIRST, IDC_HWTAB_HSIZELAST, cxExtra, 0); // // Grow the top half. // GrowControls(IDC_HWTAB_LISTVIEW, IDC_HWTAB_LISTVIEW, 0, cyTop); // // Move all the bottom things down. // ShiftControls(IDC_HWTAB_VMOVEFIRST, IDC_HWTAB_VMOVELAST, 0, cyTop); // // Grow the groupbox by the pixels we are granting it. // GrowControls(IDC_HWTAB_VSIZEFIRST, IDC_HWTAB_VSIZELAST, 0, cyBottom); // // And the buttons move with the bottom right corner. // ShiftControls(IDC_HWTAB_VDOWNFIRST, IDC_HWTAB_VDOWNLAST, cxExtra, cyBottom); } LRESULT CHWTab::ParentSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp, UINT_PTR uidSubclass, DWORD_PTR dwRefData) { CHWTab *self = (CHWTab *)dwRefData; LRESULT lres = 0; UNREFERENCED_PARAMETER(uidSubclass); switch (wm) { case WM_SIZE: self->RepositionControls(); break; case WM_NOTIFY: lres = DefSubclassProc(hwnd, wm, wp, lp); if (lres) break; // Parent already handled lres = SendMessage(self->_hdlg, wm, wp, lp); break; // Work around a bug in USER where if you press Enter, the WM_COMMAND // gets sent to the wrong window if it belongs to a nested dialog. case WM_COMMAND: if (GET_WM_COMMAND_HWND(wp, lp) && GetParent(GET_WM_COMMAND_HWND(wp, lp)) == self->_hdlg) { lres = SendMessage(self->_hdlg, wm, wp, lp); } else { lres = DefSubclassProc(hwnd, wm, wp, lp); } break; case WM_DISPLAYCHANGE: case WM_SETTINGCHANGE: case WM_SYSCOLORCHANGE: lres = DefSubclassProc(hwnd, wm, wp, lp); lres = SendMessage(self->_hdlg, wm, wp, lp); break; default: lres = DefSubclassProc(hwnd, wm, wp, lp); break; } return lres; } // // One-time dialog initialization. // BOOL CHWTab::OnInitDialog(HWND hdlg) { _hdlg = hdlg; _hwndList = GetDlgItem(_hdlg, IDC_HWTAB_LISTVIEW); SetWindowLongPtr(_hdlg, DWLP_USER, (LONG_PTR)this); RepositionControls(); // // The "Name" column gets 75% and the "Type" column gets 25%. // Subtract out the size of a vertical scrollbar in case we // get one. // RECT rc; GetClientRect(_hwndList, &rc); rc.right -= GetSystemMetrics(SM_CXVSCROLL); LVCOLUMN col; TCHAR szTitle[64]; col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; col.fmt = LVCFMT_LEFT; col.cx = rc.right * 3 / 4; col.pszText = szTitle; LoadString(THIS_DLL, IDS_HWTAB_LV_NAME, szTitle, ARRAYLEN(szTitle)); ListView_InsertColumn(_hwndList, 0, &col); col.cx = rc.right - col.cx; LoadString(THIS_DLL, IDS_HWTAB_LV_TYPE, szTitle, ARRAYLEN(szTitle)); ListView_InsertColumn(_hwndList, 1, &col); if (_imageListData.ImageList) { ListView_SetImageList(_hwndList, _imageListData.ImageList, LVSIL_SMALL); } ListView_SetExtendedListViewStyle(_hwndList, LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP); // Need to subclass parent to take over all parent functionality if (!SetWindowSubclass(GetParent(hdlg), ParentSubclassProc, 0, (DWORD_PTR)this)) { DestroyWindow(hdlg); } return TRUE; } void CHWTab::RemoveListItems(HWND hwndList) { LVITEM lviName; LPLISTITEM plistItem; int cItems = ListView_GetItemCount(hwndList); int iItem; for (iItem = 0; iItem < cItems; iItem++) { lviName.mask = LVIF_PARAM; lviName.iSubItem = 0; // column 0 lviName.iItem = iItem; ListView_GetItem(hwndList,&lviName); plistItem = (LPLISTITEM) lviName.lParam; if (plistItem) { delete plistItem; } } ListView_DeleteAllItems(_hwndList); } // // Rebuild the list of devices. // // This is done whenever we get focus. We cache the results from last time // and invalidate the cache when we are told that hardware has changed. void CHWTab::RebuildDeviceList() { HCURSOR hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT)); int devClass; // First clear out the existing listview RemoveListItems(_hwndList); Reset(); // Get all the devices of our class for (devClass = 0; devClass < _iNumClass; devClass++) { _pCD[devClass].hdsaDinf = DSA_Create(sizeof(SP_DEVINFO_DATA), 4); if (!_pCD[devClass].hdsaDinf) goto done; _pCD[devClass].hdev = SetupDiGetClassDevs(&_pCD[devClass].devGuid, 0, 0, DIGCF_PROFILE | DIGCF_PRESENT); if (_pCD[devClass].hdev == INVALID_HANDLE_VALUE) goto done; // Study the class in preparation for adding it to our listview int idev; LVITEM lviName, lviType; TCHAR tszName[LINE_LEN]; lviName.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE; lviName.iSubItem = 0; // column 0 lviName.iImage = _pCD[devClass].iImage; // image (or -1 if no image) lviName.pszText = tszName; // name goes here lviName.iItem = DA_LAST; // Always append // The second column contains the class description, which is the same // for all items. lviType.mask = LVIF_TEXT; lviType.iSubItem = 1; lviType.pszText = _pCD[devClass].tszClass; for (idev = 0; ; idev++) { SP_DEVINFO_DATA dinf; BOOL fHidden = FALSE; dinf.cbSize = sizeof(dinf); if (SetupDiEnumDeviceInfo(_pCD[devClass].hdev, idev, &dinf)) { // Device status - Don't want to show devices with DN_NO_SHOW_IN_DM set, as a rule. ULONG Status, Problem; if (CM_Get_DevNode_Status_Ex(&Status, &Problem, dinf.DevInst, 0, NULL) == CR_SUCCESS) { if (Status & DN_NO_SHOW_IN_DM) // No, UI, mark this device as hidden. { fHidden = TRUE; } } LPLISTITEM pListItem = new LISTITEM; if (!pListItem) break; pListItem->devClass = devClass; pListItem->dsaItem = DSA_AppendItem(_pCD[devClass].hdsaDinf, &dinf); lviName.lParam = (LPARAM) pListItem; if (lviName.lParam < 0) { delete pListItem; break; // Out of memory } // Try the friendly name. If that doesn't work, then try // the device name. If that doesn't work, then say "Unknown". if (!GetDeviceRegistryProperty(_pCD[devClass].hdev, SPDRP_FRIENDLYNAME, &dinf, tszName, ARRAYLEN(tszName)) && !GetDeviceRegistryProperty(_pCD[devClass].hdev, SPDRP_DEVICEDESC , &dinf, tszName, ARRAYLEN(tszName))) { LoadString(THIS_DLL, IDS_HWTAB_UNKNOWN, tszName, ARRAYLEN(tszName)); } // Give our parent a chance to filter the item before we insert it // Return TRUE to reject the item from the list. NMHWTAB nmht; nmht.nm.hwndFrom = _hdlg; nmht.nm.idFrom = 0; nmht.nm.code = HWN_FILTERITEM; nmht.hdev = _pCD[devClass].hdev; nmht.pdinf = &dinf; nmht.fHidden = fHidden; SendMessage(GetParent(_hdlg), WM_NOTIFY, nmht.nm.idFrom, (LPARAM)&nmht); if (!nmht.fHidden) { // Add the Item lviType.iItem = ListView_InsertItem(_hwndList, &lviName); if (lviType.iItem >= 0) { ListView_SetItem(_hwndList, &lviType); } else { delete pListItem; } } else { // clean up the item; it got filtered away delete pListItem; } } // Stop on any error after the 100'th device to keep us from going // berzerk if we start getting strange errors like ERROR_GENERAL_FAILURE. else if (GetLastError() == ERROR_NO_MORE_ITEMS || idev > 100) { break; } } // Select the first item so the info pane contains stuff ListView_SetItemState(_hwndList, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); } done: SetCursor(hcurPrev); } void CHWTab::SprintfItem(UINT ids, UINT idc, LPCTSTR ptszText) { TCHAR tszMsg[MAX_PATH]; TCHAR tszOut[MAX_PATH + LINE_LEN]; LoadString(THIS_DLL, ids, tszMsg, ARRAYLEN(tszMsg)); StringCchPrintf(tszOut, ARRAYLEN(tszOut), tszMsg, ptszText); SetDlgItemText(_hdlg, idc, tszOut); } void CHWTab::OnItemChanged(LPNMLISTVIEW pnmlv) { PSP_DEVINFO_DATA pdinf; LPLISTITEM pListItem = (LPLISTITEM) pnmlv->lParam; if ((pnmlv->uChanged & LVIF_STATE) && (pnmlv->uNewState & LVIS_FOCUSED) && (pdinf = GetPdinf(pListItem)) != NULL) { TCHAR tsz[LINE_LEN]; // Manufacturer GetDeviceRegistryProperty(_pCD[pListItem->devClass].hdev, SPDRP_MFG, pdinf, tsz, ARRAYLEN(tsz)); SprintfItem(IDS_HWTAB_MFG, IDC_HWTAB_MFG, tsz); // Location if (GetLocationInformation(pdinf->DevInst, tsz, ARRAYLEN(tsz), NULL) != CR_SUCCESS) { LoadString(g_hInstance, IDS_UNKNOWN, tsz, ARRAYLEN(tsz)); } SprintfItem(IDS_HWTAB_LOC, IDC_HWTAB_LOC, tsz); // Device status - have to go to CM for this one ULONG Status, Problem; if (CM_Get_DevNode_Status_Ex(&Status, &Problem, pdinf->DevInst, 0, NULL) == CR_SUCCESS && DeviceProblemText(NULL, pdinf->DevInst, Problem, tsz, ARRAYLEN(tsz))) { // Yippee } else { tsz[0] = TEXT('\0'); // Darn } SprintfItem(IDS_HWTAB_STATUS, IDC_HWTAB_STATUS, tsz); //let our parent know that something changed NMHWTAB nmht; nmht.nm.hwndFrom = _hdlg; nmht.nm.idFrom = 0; nmht.nm.code = HWN_SELECTIONCHANGED; nmht.hdev = _pCD[pListItem->devClass].hdev; nmht.pdinf = pdinf; SendMessage(GetParent(_hdlg), WM_NOTIFY, nmht.nm.idFrom, (LPARAM)&nmht); } } void CHWTab::OnProperties(void) { LVITEM lvi; PSP_DEVINFO_DATA pdinf; lvi.mask = LVIF_PARAM; lvi.iSubItem = 0; // column 0 lvi.iItem = ListView_GetNextItem(_hwndList, -1, LVNI_FOCUSED); if (lvi.iItem >= 0 && ListView_GetItem(_hwndList, &lvi) && (pdinf = GetPdinf((LPLISTITEM) lvi.lParam)) != NULL) { DWORD cchRequired; LPLISTITEM pListItem; LPTSTR ptszDevid; pListItem = (LPLISTITEM) lvi.lParam; if (BUFFERQUERY_SUCCEEDED( SetupDiGetDeviceInstanceId(_pCD[pListItem->devClass].hdev, pdinf, NULL, 0, &cchRequired)) && ((ptszDevid = (LPTSTR)LocalAlloc(LPTR, cchRequired * sizeof(TCHAR))) != NULL)) { if (SetupDiGetDeviceInstanceId(_pCD[pListItem->devClass].hdev, pdinf, ptszDevid, cchRequired, NULL)) { DevicePropertiesEx(GetParent(_hdlg), NULL, ptszDevid, 0, FALSE); } LocalFree(ptszDevid); } } } void CHWTab::OnTshoot(void) { LVITEM lvi; PSP_DEVINFO_DATA pdinf; lvi.mask = LVIF_PARAM; lvi.iSubItem = 0; // column 0 lvi.iItem = ListView_GetNextItem(_hwndList, -1, LVNI_FOCUSED); if (lvi.iItem >= 0 && ListView_GetItem(_hwndList, &lvi) && (pdinf = GetPdinf((LPLISTITEM) lvi.lParam)) != NULL) { DWORD cchRequired; LPLISTITEM pListItem; LPTSTR ptszDevid; pListItem = (LPLISTITEM) lvi.lParam; if (BUFFERQUERY_SUCCEEDED( SetupDiGetDeviceInstanceId(_pCD[pListItem->devClass].hdev, pdinf, NULL, 0, &cchRequired)) && ((ptszDevid = (LPTSTR)LocalAlloc(LPTR, cchRequired * sizeof(TCHAR))) != NULL)) { if (SetupDiGetDeviceInstanceId(_pCD[pListItem->devClass].hdev, pdinf, ptszDevid, cchRequired, NULL)) { DeviceProblemWizard(GetParent(_hdlg), NULL, ptszDevid); } LocalFree(ptszDevid); } } } // // SetText is how the caller tells us what our troubleshooter // command line is. // void CHWTab::OnSetText(LPCTSTR ptszText) { BOOL fEnable = ptszText && ptszText[0]; HWND hwndTS = GetDlgItem(_hdlg, IDC_HWTAB_TSHOOT); EnableWindow(hwndTS, fEnable); ShowWindow(hwndTS, fEnable ? SW_SHOW : SW_HIDE); } void CHWTab::OnHelp(LPHELPINFO phi) { WinHelp((HWND)phi->hItemHandle, DEVMGR_HELP_FILE_NAME, HELP_WM_HELP, (ULONG_PTR)c_HWTabHelpIDs); } void CHWTab::OnContextMenu(HWND hwnd) { WinHelp(hwnd, DEVMGR_HELP_FILE_NAME, HELP_CONTEXTMENU, (ULONG_PTR)c_HWTabHelpIDs); } // // Dialog procedure (yay). // INT_PTR CALLBACK CHWTab::DialogProc(HWND hdlg, UINT wm, WPARAM wp, LPARAM lp) { CHWTab *self = (CHWTab *)GetWindowLongPtr(hdlg, DWLP_USER); if (wm == WM_INITDIALOG) { self = (CHWTab *)lp; return self->OnInitDialog(hdlg); } // Ignores messages that arrive before WM_INITDIALOG if (!self) return FALSE; switch (wm) { case WM_DISPLAYCHANGE: case WM_SETTINGCHANGE: case WM_SYSCOLORCHANGE: SendMessage(self->_hwndList, wm, wp, lp); break; case WM_NOTIFY: { LPNMHDR pnm = (LPNMHDR)lp; switch (pnm->code) { case PSN_SETACTIVE: self->RebuildDeviceList(); break; case LVN_ITEMCHANGED: if (pnm->hwndFrom == self->_hwndList) { self->OnItemChanged((LPNMLISTVIEW)pnm); } break; case NM_DBLCLK: if (pnm->hwndFrom == self->_hwndList) { DWORD dwPos = GetMessagePos(); LVHITTESTINFO hti; hti.pt.x = GET_X_LPARAM(dwPos); hti.pt.y = GET_Y_LPARAM(dwPos); ScreenToClient(self->_hwndList, &hti.pt); ListView_HitTest(self->_hwndList, &hti); if (hti.iItem >= 0) { self->OnProperties(); } } break; } } break; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wp, lp)) { case IDC_HWTAB_PROP: self->OnProperties(); break; case IDC_HWTAB_TSHOOT: self->OnTshoot(); break; } break; case WM_SETTEXT: self->OnSetText((LPCTSTR)lp); break; case WM_NCDESTROY: if (self && self->_hwndList) { self->RemoveListItems(self->_hwndList); } RemoveWindowSubclass(GetParent(hdlg), ParentSubclassProc, 0); delete self; break; case WM_HELP: self->OnHelp((LPHELPINFO)lp); break; case WM_CONTEXTMENU: self->OnContextMenu((HWND)wp); break; } return FALSE; } // // Create a Hardware page for the specified GUID. // // Parameters: // // hwndParent - The dummy frame window created by the caller // pguid - The setup device class GUID we will manage // // Returns: // // HWND of the created subdialog. // // Usage: // // When your control panel applet needs a Hardware page, create // a blank dialog template titled "Hardware" and add it to your // control panel. Set the size of the blank to be the size you // want the final Hardware Tab page to be. // // Your dialog box procedure should go like this: // // BOOL HardwareDlgProc(HWND hdlg, UINT uMsg, WPARAM wp, LPARAM lp) { // switch (uMsg) { // // case WM_INITDIALOG: // // GUID_DEVCLASS_MOUSE is in devguid.h // hwndHW = DeviceCreateHardwarePage(hdlg, &GUID_DEVCLASS_MOUSE); // if (hwndHW) { // // Optional - Set the troubleshooter command line. // // Do this if you want a Troubleshoot button. // SetWindowText(hwndHW, // TEXT("hh.exe mk:@MSITStore:tshoot.chm::/hdw_drives.htm")); // } else { // DestroyWindow(hdlg); // catastrophic failure // } // return TRUE; // } // return FALSE; // } // STDAPI_(HWND) DeviceCreateHardwarePageEx(HWND hwndParent, const GUID *pguid, int iNumClass, DWORD dwViewMode) { if (!hwndParent || !pguid) { return NULL; } HCURSOR hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT)); CHWTab *self = new CHWTab(pguid, iNumClass, dwViewMode); HWND hwnd; if (self) { hwnd = CreateDialogParam(THIS_DLL, MAKEINTRESOURCE(IDD_HWTAB), hwndParent, CHWTab::DialogProc, (LPARAM)self); if (!hwnd) { delete self; hwnd = NULL; } } else { hwnd = NULL; } SetCursor(hcurPrev); return hwnd; } STDAPI_(HWND) DeviceCreateHardwarePage(HWND hwndParent, const GUID *pguid) { return DeviceCreateHardwarePageEx(hwndParent, pguid, 1, HWTAB_SMALLLIST); }