1002 lines
28 KiB
C++
1002 lines
28 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (C) 1997-1999 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 <commctrl.h>
|
||
|
#include <comctrlp.h>
|
||
|
#include <windowsx.h>
|
||
|
#include <hwtab.h>
|
||
|
|
||
|
#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
|
||
|
);
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
#define DevicePropertiesEx DevicePropertiesExW
|
||
|
#define DeviceProblemWizard DeviceProblemWizardW
|
||
|
#define DeviceProblemText DeviceProblemTextW
|
||
|
#else
|
||
|
#define DevicePropertiesEx DevicePropertiesExA
|
||
|
#define DeviceProblemWizard DeviceProblemWizardA
|
||
|
#define DeviceProblemText DeviceProblemTextA
|
||
|
#endif
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* 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)
|
||
|
{
|
||
|
//Assert(iNumClass);
|
||
|
|
||
|
// 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;
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
DWORD cbRequired;
|
||
|
|
||
|
// 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));
|
||
|
wsprintf(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)))) {
|
||
|
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)))) {
|
||
|
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);
|
||
|
}
|