Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

997 lines
29 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation
  3. Module Name:
  4. hwtab.cpp
  5. Abstract:
  6. implement the hardware tab functions and UI.
  7. Author:
  8. William Hsieh (williamh) created
  9. Revision History:
  10. --*/
  11. #include "devmgr.h"
  12. #include <commctrl.h>
  13. #include <comctrlp.h>
  14. #include <windowsx.h>
  15. #include <hwtab.h>
  16. #define THIS_DLL g_hInstance
  17. /*****************************************************************************
  18. *
  19. * Exported stuff
  20. *
  21. *****************************************************************************/
  22. // Stuff in api.h that we can't #include because api.h can't be #include'd
  23. // by anyone other than api.cpp.
  24. STDAPI_(int)
  25. DevicePropertiesExA(
  26. HWND hwndParent,
  27. LPCSTR MachineName,
  28. LPCSTR DeviceID,
  29. DWORD Flags,
  30. BOOL ShowDeviceTree
  31. );
  32. STDAPI_(int)
  33. DevicePropertiesExW(
  34. HWND hwndParent,
  35. LPCWSTR MachineName,
  36. LPCWSTR DeviceID,
  37. DWORD Flags,
  38. BOOL ShowDeviceTree
  39. );
  40. STDAPI_(int)
  41. DeviceProblemWizardA(
  42. HWND hwndParent,
  43. LPCSTR MachineName,
  44. LPCSTR DeviceId
  45. );
  46. STDAPI_(int)
  47. DeviceProblemWizardW(
  48. HWND hwndParent,
  49. LPCWSTR MachineName,
  50. LPCWSTR DeviceId
  51. );
  52. STDAPI_(UINT)
  53. DeviceProblemTextA(
  54. HMACHINE hMachine,
  55. DEVNODE DevNode,
  56. ULONG ProblemNumber,
  57. LPSTR Buffer,
  58. UINT BufferSize
  59. );
  60. STDAPI_(UINT)
  61. DeviceProblemTextW(
  62. HMACHINE hMachine,
  63. DEVNODE DevNode,
  64. ULONG ProblemNumber,
  65. LPWSTR Buffer,
  66. UINT BufferSize
  67. );
  68. #define DevicePropertiesEx DevicePropertiesExW
  69. #define DeviceProblemWizard DeviceProblemWizardW
  70. #define DeviceProblemText DeviceProblemTextW
  71. /*****************************************************************************
  72. *
  73. * General remark about SetupDi functions
  74. *
  75. * Windows NT and Windows 98 implement many of the SetupDi query
  76. * functions differently if you are querying for the buffer size.
  77. *
  78. * Windows 98 returns FALSE, and GetLastError() returns
  79. * ERROR_INSUFFICIENT_BUFFER.
  80. *
  81. * Windows NT returns TRUE.
  82. *
  83. * So all calls to SetupDi functions that do querying for the buffer
  84. * size should be wrapped with BUFFERQUERY_SUCCEEDED.
  85. *
  86. *****************************************************************************/
  87. #define BUFFERQUERY_SUCCEEDED(f) \
  88. ((f) || GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  89. /*****************************************************************************
  90. *
  91. * Context help
  92. *
  93. *****************************************************************************/
  94. #include "devgenpg.h"
  95. #define idh_devmgr_hardware_trblsht 400100
  96. #define idh_devmgr_hardware_properties 400200
  97. #define idh_devmgr_hardware_listview 400300
  98. const DWORD c_HWTabHelpIDs[] =
  99. {
  100. IDC_HWTAB_LVSTATIC, idh_devmgr_hardware_listview,
  101. IDC_HWTAB_LISTVIEW, idh_devmgr_hardware_listview,
  102. IDC_HWTAB_GROUPBOX, IDH_DISABLEHELP,
  103. IDC_HWTAB_MFG, idh_devmgr_general_manufacturer,
  104. IDC_HWTAB_LOC, idh_devmgr_general_location,
  105. IDC_HWTAB_STATUS, idh_devmgr_general_device_status,
  106. IDC_HWTAB_TSHOOT, idh_devmgr_hardware_trblsht,
  107. IDC_HWTAB_PROP, idh_devmgr_hardware_properties,
  108. 0, 0
  109. };
  110. typedef TCHAR TLINE[LINE_LEN];
  111. typedef struct
  112. {
  113. int devClass;
  114. int dsaItem;
  115. } LISTITEM, *LPLISTITEM;
  116. typedef struct
  117. {
  118. GUID devGuid; // device class guid we are managing
  119. TLINE tszClass; // Array of friendly name of class
  120. HDSA hdsaDinf; // array of SP_DEVINFO_DATA structures
  121. HDEVINFO hdev; // hdsaDinfo refers to this
  122. int iImage; // image index within master imagelist
  123. } CLASSDATA, *LPCLASSDATA;
  124. /*****************************************************************************
  125. *
  126. * CHWTab
  127. *
  128. * The Hardware Tab page.
  129. *
  130. *****************************************************************************/
  131. class CHWTab {
  132. private:
  133. CHWTab(const GUID *pguid, int iNumClass, DWORD dwViewMode);
  134. ~CHWTab();
  135. void *operator new(size_t cb) { return LocalAlloc(LPTR, cb); }
  136. void operator delete(void *p) { LocalFree(p); }
  137. void RebuildDeviceList();
  138. void Reset();
  139. BOOL GetDeviceRegistryProperty(HDEVINFO hDev, DWORD dwProp, PSP_DEVINFO_DATA pdinf,
  140. LPTSTR ptsz, DWORD ctch);
  141. void SprintfItem(UINT ids, UINT idc, LPCTSTR ptszText);
  142. static INT_PTR CALLBACK DialogProc(HWND hdlg, UINT wm, WPARAM wp, LPARAM lp);
  143. static LRESULT CALLBACK ParentSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp, UINT_PTR uidSubclass, DWORD_PTR dwRefData);
  144. friend HWND DeviceCreateHardwarePage(HWND hwndParent, const GUID *pguid);
  145. friend HWND DeviceCreateHardwarePageEx(HWND hwndParent, const GUID *pguid, int iNumClass, DWORD dwViewMode);
  146. BOOL OnInitDialog(HWND hdlg);
  147. void RemoveListItems(HWND hwndList);
  148. void OnItemChanged(LPNMLISTVIEW pnmlv);
  149. void OnProperties(void);
  150. void OnTshoot(void);
  151. void OnSetText(LPCTSTR ptszText);
  152. void OnHelp(LPHELPINFO phi);
  153. void OnContextMenu(HWND hwnd);
  154. void SetControlPositions(int idcFirst, int idcLast, int dx, int dy, UINT flags);
  155. //
  156. // Helpers for SetWindowPositions.
  157. //
  158. void GrowControls(int idcFirst, int idcLast, int dx, int dy) {
  159. SetControlPositions(idcFirst, idcLast, dx, dy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  160. }
  161. void ShiftControls(int idcFirst, int idcLast, int dx, int dy) {
  162. SetControlPositions(idcFirst, idcLast, dx, dy, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
  163. }
  164. void RepositionControls();
  165. inline PSP_DEVINFO_DATA GetPdinf(LPLISTITEM pListItem) {
  166. return (PSP_DEVINFO_DATA)DSA_GetItemPtr(_pCD[pListItem->devClass].hdsaDinf, pListItem->dsaItem);
  167. }
  168. private:
  169. HWND _hdlg; // the dialog box itself
  170. HWND _hwndList; // The listview
  171. int _iNumClass; // Number of class guids
  172. DWORD _dwViewMode; // Dictates size of list box
  173. LPCLASSDATA _pCD; // Class data for each devClass to represent
  174. SP_CLASSIMAGELIST_DATA _imageListData; // Class image list data
  175. };
  176. //
  177. // Constructor.
  178. //
  179. CHWTab::CHWTab(const GUID *pguid, int iNumClass, DWORD dwViewMode) :
  180. _pCD(NULL)
  181. {
  182. // Since the _dwViewMode is a devisor, we need to make sure it's valid
  183. _imageListData.ImageList = NULL;
  184. _dwViewMode = dwViewMode;
  185. if (_dwViewMode < HWTAB_LARGELIST)
  186. {
  187. _dwViewMode = HWTAB_LARGELIST;
  188. }
  189. if (_dwViewMode > HWTAB_SMALLLIST)
  190. {
  191. _dwViewMode = HWTAB_SMALLLIST;
  192. }
  193. _iNumClass = iNumClass;
  194. _pCD = new CLASSDATA[_iNumClass];
  195. if (_pCD && pguid)
  196. {
  197. DWORD cbRequired;
  198. memset(_pCD, 0, sizeof(CLASSDATA) * _iNumClass);
  199. int devClass;
  200. for (devClass = 0; devClass < _iNumClass; devClass++)
  201. {
  202. _pCD[devClass].hdev = INVALID_HANDLE_VALUE;
  203. _pCD[devClass].devGuid = (GUID) pguid[devClass];
  204. }
  205. //get the driver class image list
  206. _imageListData.cbSize = sizeof(SP_CLASSIMAGELIST_DATA);
  207. if (!SetupDiGetClassImageList(&_imageListData)) {
  208. _imageListData.ImageList = NULL;
  209. }
  210. for (devClass = 0; devClass < _iNumClass; devClass++)
  211. {
  212. _pCD[devClass].iImage = -1;
  213. SetupDiGetClassDescription(&_pCD[devClass].devGuid, _pCD[devClass].tszClass, sizeof(TLINE), &cbRequired);
  214. if (_imageListData.ImageList)
  215. {
  216. // Get the image index for our little guy
  217. int iImageIndex;
  218. if (SetupDiGetClassImageIndex(&_imageListData, &_pCD[devClass].devGuid, &iImageIndex)) {
  219. _pCD[devClass].iImage = iImageIndex;
  220. }
  221. }
  222. }
  223. }
  224. }
  225. CHWTab::~CHWTab()
  226. {
  227. Reset();
  228. if (_imageListData.ImageList) {
  229. SetupDiDestroyClassImageList(&_imageListData);
  230. }
  231. if (_pCD)
  232. {
  233. delete _pCD;
  234. _pCD = NULL;
  235. }
  236. }
  237. //
  238. // Return to normal, ready for the next go-round. This also frees all
  239. // dynamically allocated stuff.
  240. //
  241. void
  242. CHWTab::Reset()
  243. {
  244. int devClass;
  245. for (devClass = 0; devClass < _iNumClass; devClass++)
  246. {
  247. if (_pCD[devClass].hdsaDinf) {
  248. DSA_Destroy(_pCD[devClass].hdsaDinf);
  249. _pCD[devClass].hdsaDinf = NULL;
  250. }
  251. if (_pCD[devClass].hdev != INVALID_HANDLE_VALUE) {
  252. SetupDiDestroyDeviceInfoList(_pCD[devClass].hdev);
  253. _pCD[devClass].hdev = INVALID_HANDLE_VALUE;
  254. }
  255. }
  256. }
  257. //
  258. // Helper function that calls SetupDiGetDeviceRegistryProperty
  259. // and copes with things like detecting the various error modes
  260. // properly.
  261. //
  262. BOOL
  263. CHWTab::GetDeviceRegistryProperty(HDEVINFO hDev, DWORD dwProp, PSP_DEVINFO_DATA pdinf,
  264. LPTSTR ptsz, DWORD ctch)
  265. {
  266. DWORD cbRequired;
  267. ptsz[0] = TEXT('\0');
  268. SetupDiGetDeviceRegistryProperty(hDev, pdinf, dwProp, 0,
  269. (LPBYTE)ptsz, ctch * sizeof(TCHAR),
  270. &cbRequired);
  271. return ptsz[0];
  272. }
  273. //
  274. // Change the size/position of controls idcFirst through idcLast.
  275. // Change the size/position by (dx, dy).
  276. // flags specifies what exactly is changing.
  277. //
  278. void
  279. CHWTab::SetControlPositions(int idcFirst, int idcLast, int dx, int dy, UINT flags)
  280. {
  281. HDWP hdwp = BeginDeferWindowPos(idcLast - idcFirst + 1);
  282. for (int idc = idcFirst; idc <= idcLast; idc++) {
  283. if (hdwp) {
  284. RECT rc;
  285. HWND hwnd = GetDlgItem(_hdlg, idc);
  286. GetWindowRect(hwnd, &rc);
  287. MapWindowRect(HWND_DESKTOP, _hdlg, &rc);
  288. hdwp = DeferWindowPos(hdwp, hwnd, NULL,
  289. rc.left + dx, rc.top + dy,
  290. rc.right - rc.left + dx, rc.bottom - rc.top + dy,
  291. flags);
  292. }
  293. }
  294. if (hdwp) {
  295. EndDeferWindowPos(hdwp);
  296. }
  297. }
  298. //
  299. // Reposition and resize our controls based on the size we need to be.
  300. //
  301. void
  302. CHWTab::RepositionControls()
  303. {
  304. //
  305. // First, see how much slack space we have.
  306. //
  307. RECT rcDlg, rcParent;
  308. GetClientRect(_hdlg, &rcDlg);
  309. GetClientRect(GetParent(_hdlg), &rcParent);
  310. //
  311. // Make ourselves as big as our parent.
  312. //
  313. SetWindowPos(_hdlg, NULL, 0, 0, rcParent.right, rcParent.bottom,
  314. SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  315. //
  316. // Now do a little more math...
  317. //
  318. int cyExtra = rcParent.bottom - rcDlg.bottom;
  319. int cxExtra = rcParent.right - rcDlg.right;
  320. //
  321. // The extra vertical space is split between the listview and
  322. // the groupbox. The amount of split is determined by _dwViewMode.
  323. // Larger modes give more and more space to the listview.
  324. //
  325. int cyTop = cyExtra / _dwViewMode;
  326. int cyBottom = cyExtra - cyTop;
  327. //
  328. // Horizontally grow the controls that reach the full width of the
  329. // dialog box.
  330. //
  331. GrowControls(IDC_HWTAB_HSIZEFIRST, IDC_HWTAB_HSIZELAST, cxExtra, 0);
  332. //
  333. // Grow the top half.
  334. //
  335. GrowControls(IDC_HWTAB_LISTVIEW, IDC_HWTAB_LISTVIEW, 0, cyTop);
  336. //
  337. // Move all the bottom things down.
  338. //
  339. ShiftControls(IDC_HWTAB_VMOVEFIRST, IDC_HWTAB_VMOVELAST, 0, cyTop);
  340. //
  341. // Grow the groupbox by the pixels we are granting it.
  342. //
  343. GrowControls(IDC_HWTAB_VSIZEFIRST, IDC_HWTAB_VSIZELAST, 0, cyBottom);
  344. //
  345. // And the buttons move with the bottom right corner.
  346. //
  347. ShiftControls(IDC_HWTAB_VDOWNFIRST, IDC_HWTAB_VDOWNLAST, cxExtra, cyBottom);
  348. }
  349. LRESULT
  350. CHWTab::ParentSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp, UINT_PTR uidSubclass, DWORD_PTR dwRefData)
  351. {
  352. CHWTab *self = (CHWTab *)dwRefData;
  353. LRESULT lres = 0;
  354. UNREFERENCED_PARAMETER(uidSubclass);
  355. switch (wm)
  356. {
  357. case WM_SIZE:
  358. self->RepositionControls();
  359. break;
  360. case WM_NOTIFY:
  361. lres = DefSubclassProc(hwnd, wm, wp, lp);
  362. if (lres) break; // Parent already handled
  363. lres = SendMessage(self->_hdlg, wm, wp, lp);
  364. break;
  365. // Work around a bug in USER where if you press Enter, the WM_COMMAND
  366. // gets sent to the wrong window if it belongs to a nested dialog.
  367. case WM_COMMAND:
  368. if (GET_WM_COMMAND_HWND(wp, lp) &&
  369. GetParent(GET_WM_COMMAND_HWND(wp, lp)) == self->_hdlg) {
  370. lres = SendMessage(self->_hdlg, wm, wp, lp);
  371. } else {
  372. lres = DefSubclassProc(hwnd, wm, wp, lp);
  373. }
  374. break;
  375. case WM_DISPLAYCHANGE:
  376. case WM_SETTINGCHANGE:
  377. case WM_SYSCOLORCHANGE:
  378. lres = DefSubclassProc(hwnd, wm, wp, lp);
  379. lres = SendMessage(self->_hdlg, wm, wp, lp);
  380. break;
  381. default:
  382. lres = DefSubclassProc(hwnd, wm, wp, lp);
  383. break;
  384. }
  385. return lres;
  386. }
  387. //
  388. // One-time dialog initialization.
  389. //
  390. BOOL
  391. CHWTab::OnInitDialog(HWND hdlg)
  392. {
  393. _hdlg = hdlg;
  394. _hwndList = GetDlgItem(_hdlg, IDC_HWTAB_LISTVIEW);
  395. SetWindowLongPtr(_hdlg, DWLP_USER, (LONG_PTR)this);
  396. RepositionControls();
  397. //
  398. // The "Name" column gets 75% and the "Type" column gets 25%.
  399. // Subtract out the size of a vertical scrollbar in case we
  400. // get one.
  401. //
  402. RECT rc;
  403. GetClientRect(_hwndList, &rc);
  404. rc.right -= GetSystemMetrics(SM_CXVSCROLL);
  405. LVCOLUMN col;
  406. TCHAR szTitle[64];
  407. col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
  408. col.fmt = LVCFMT_LEFT;
  409. col.cx = rc.right * 3 / 4;
  410. col.pszText = szTitle;
  411. LoadString(THIS_DLL, IDS_HWTAB_LV_NAME, szTitle, ARRAYLEN(szTitle));
  412. ListView_InsertColumn(_hwndList, 0, &col);
  413. col.cx = rc.right - col.cx;
  414. LoadString(THIS_DLL, IDS_HWTAB_LV_TYPE, szTitle, ARRAYLEN(szTitle));
  415. ListView_InsertColumn(_hwndList, 1, &col);
  416. if (_imageListData.ImageList)
  417. {
  418. ListView_SetImageList(_hwndList, _imageListData.ImageList, LVSIL_SMALL);
  419. }
  420. ListView_SetExtendedListViewStyle(_hwndList, LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP);
  421. // Need to subclass parent to take over all parent functionality
  422. if (!SetWindowSubclass(GetParent(hdlg), ParentSubclassProc, 0,
  423. (DWORD_PTR)this))
  424. {
  425. DestroyWindow(hdlg);
  426. }
  427. return TRUE;
  428. }
  429. void
  430. CHWTab::RemoveListItems(HWND hwndList)
  431. {
  432. LVITEM lviName;
  433. LPLISTITEM plistItem;
  434. int cItems = ListView_GetItemCount(hwndList);
  435. int iItem;
  436. for (iItem = 0; iItem < cItems; iItem++)
  437. {
  438. lviName.mask = LVIF_PARAM;
  439. lviName.iSubItem = 0; // column 0
  440. lviName.iItem = iItem;
  441. ListView_GetItem(hwndList,&lviName);
  442. plistItem = (LPLISTITEM) lviName.lParam;
  443. if (plistItem)
  444. {
  445. delete plistItem;
  446. }
  447. }
  448. ListView_DeleteAllItems(_hwndList);
  449. }
  450. //
  451. // Rebuild the list of devices.
  452. //
  453. // This is done whenever we get focus. We cache the results from last time
  454. // and invalidate the cache when we are told that hardware has changed.
  455. void
  456. CHWTab::RebuildDeviceList()
  457. {
  458. HCURSOR hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  459. int devClass;
  460. // First clear out the existing listview
  461. RemoveListItems(_hwndList);
  462. Reset();
  463. // Get all the devices of our class
  464. for (devClass = 0; devClass < _iNumClass; devClass++)
  465. {
  466. _pCD[devClass].hdsaDinf = DSA_Create(sizeof(SP_DEVINFO_DATA), 4);
  467. if (!_pCD[devClass].hdsaDinf) goto done;
  468. _pCD[devClass].hdev = SetupDiGetClassDevs(&_pCD[devClass].devGuid, 0, 0,
  469. DIGCF_PROFILE | DIGCF_PRESENT);
  470. if (_pCD[devClass].hdev == INVALID_HANDLE_VALUE) goto done;
  471. // Study the class in preparation for adding it to our listview
  472. int idev;
  473. LVITEM lviName, lviType;
  474. TCHAR tszName[LINE_LEN];
  475. lviName.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
  476. lviName.iSubItem = 0; // column 0
  477. lviName.iImage = _pCD[devClass].iImage; // image (or -1 if no image)
  478. lviName.pszText = tszName; // name goes here
  479. lviName.iItem = DA_LAST; // Always append
  480. // The second column contains the class description, which is the same
  481. // for all items.
  482. lviType.mask = LVIF_TEXT;
  483. lviType.iSubItem = 1;
  484. lviType.pszText = _pCD[devClass].tszClass;
  485. for (idev = 0; ; idev++)
  486. {
  487. SP_DEVINFO_DATA dinf;
  488. BOOL fHidden = FALSE;
  489. dinf.cbSize = sizeof(dinf);
  490. if (SetupDiEnumDeviceInfo(_pCD[devClass].hdev, idev, &dinf)) {
  491. // Device status - Don't want to show devices with DN_NO_SHOW_IN_DM set, as a rule.
  492. ULONG Status, Problem;
  493. if (CM_Get_DevNode_Status_Ex(&Status, &Problem, dinf.DevInst, 0, NULL) == CR_SUCCESS)
  494. {
  495. if (Status & DN_NO_SHOW_IN_DM) // No, UI, mark this device as hidden.
  496. {
  497. fHidden = TRUE;
  498. }
  499. }
  500. LPLISTITEM pListItem = new LISTITEM;
  501. if (!pListItem) break;
  502. pListItem->devClass = devClass;
  503. pListItem->dsaItem = DSA_AppendItem(_pCD[devClass].hdsaDinf, &dinf);
  504. lviName.lParam = (LPARAM) pListItem;
  505. if (lviName.lParam < 0)
  506. {
  507. delete pListItem;
  508. break; // Out of memory
  509. }
  510. // Try the friendly name. If that doesn't work, then try
  511. // the device name. If that doesn't work, then say "Unknown".
  512. if (!GetDeviceRegistryProperty(_pCD[devClass].hdev, SPDRP_FRIENDLYNAME, &dinf, tszName, ARRAYLEN(tszName)) &&
  513. !GetDeviceRegistryProperty(_pCD[devClass].hdev, SPDRP_DEVICEDESC , &dinf, tszName, ARRAYLEN(tszName))) {
  514. LoadString(THIS_DLL, IDS_HWTAB_UNKNOWN, tszName, ARRAYLEN(tszName));
  515. }
  516. // Give our parent a chance to filter the item before we insert it
  517. // Return TRUE to reject the item from the list.
  518. NMHWTAB nmht;
  519. nmht.nm.hwndFrom = _hdlg;
  520. nmht.nm.idFrom = 0;
  521. nmht.nm.code = HWN_FILTERITEM;
  522. nmht.hdev = _pCD[devClass].hdev;
  523. nmht.pdinf = &dinf;
  524. nmht.fHidden = fHidden;
  525. SendMessage(GetParent(_hdlg), WM_NOTIFY, nmht.nm.idFrom, (LPARAM)&nmht);
  526. if (!nmht.fHidden)
  527. {
  528. // Add the Item
  529. lviType.iItem = ListView_InsertItem(_hwndList, &lviName);
  530. if (lviType.iItem >= 0)
  531. {
  532. ListView_SetItem(_hwndList, &lviType);
  533. }
  534. else
  535. {
  536. delete pListItem;
  537. }
  538. }
  539. else
  540. {
  541. // clean up the item; it got filtered away
  542. delete pListItem;
  543. }
  544. }
  545. // Stop on any error after the 100'th device to keep us from going
  546. // berzerk if we start getting strange errors like ERROR_GENERAL_FAILURE.
  547. else if (GetLastError() == ERROR_NO_MORE_ITEMS || idev > 100) {
  548. break;
  549. }
  550. }
  551. // Select the first item so the info pane contains stuff
  552. ListView_SetItemState(_hwndList, 0, LVIS_SELECTED | LVIS_FOCUSED,
  553. LVIS_SELECTED | LVIS_FOCUSED);
  554. }
  555. done:
  556. SetCursor(hcurPrev);
  557. }
  558. void
  559. CHWTab::SprintfItem(UINT ids, UINT idc, LPCTSTR ptszText)
  560. {
  561. TCHAR tszMsg[MAX_PATH];
  562. TCHAR tszOut[MAX_PATH + LINE_LEN];
  563. LoadString(THIS_DLL, ids, tszMsg, ARRAYLEN(tszMsg));
  564. StringCchPrintf(tszOut, ARRAYLEN(tszOut), tszMsg, ptszText);
  565. SetDlgItemText(_hdlg, idc, tszOut);
  566. }
  567. void
  568. CHWTab::OnItemChanged(LPNMLISTVIEW pnmlv)
  569. {
  570. PSP_DEVINFO_DATA pdinf;
  571. LPLISTITEM pListItem = (LPLISTITEM) pnmlv->lParam;
  572. if ((pnmlv->uChanged & LVIF_STATE) &&
  573. (pnmlv->uNewState & LVIS_FOCUSED) &&
  574. (pdinf = GetPdinf(pListItem)) != NULL) {
  575. TCHAR tsz[LINE_LEN];
  576. // Manufacturer
  577. GetDeviceRegistryProperty(_pCD[pListItem->devClass].hdev, SPDRP_MFG, pdinf, tsz, ARRAYLEN(tsz));
  578. SprintfItem(IDS_HWTAB_MFG, IDC_HWTAB_MFG, tsz);
  579. // Location
  580. if (GetLocationInformation(pdinf->DevInst, tsz, ARRAYLEN(tsz), NULL) != CR_SUCCESS) {
  581. LoadString(g_hInstance, IDS_UNKNOWN, tsz, ARRAYLEN(tsz));
  582. }
  583. SprintfItem(IDS_HWTAB_LOC, IDC_HWTAB_LOC, tsz);
  584. // Device status - have to go to CM for this one
  585. ULONG Status, Problem;
  586. if (CM_Get_DevNode_Status_Ex(&Status, &Problem,
  587. pdinf->DevInst, 0, NULL) == CR_SUCCESS &&
  588. DeviceProblemText(NULL, pdinf->DevInst, Problem, tsz, ARRAYLEN(tsz))) {
  589. // Yippee
  590. } else {
  591. tsz[0] = TEXT('\0'); // Darn
  592. }
  593. SprintfItem(IDS_HWTAB_STATUS, IDC_HWTAB_STATUS, tsz);
  594. //let our parent know that something changed
  595. NMHWTAB nmht;
  596. nmht.nm.hwndFrom = _hdlg;
  597. nmht.nm.idFrom = 0;
  598. nmht.nm.code = HWN_SELECTIONCHANGED;
  599. nmht.hdev = _pCD[pListItem->devClass].hdev;
  600. nmht.pdinf = pdinf;
  601. SendMessage(GetParent(_hdlg), WM_NOTIFY, nmht.nm.idFrom, (LPARAM)&nmht);
  602. }
  603. }
  604. void
  605. CHWTab::OnProperties(void)
  606. {
  607. LVITEM lvi;
  608. PSP_DEVINFO_DATA pdinf;
  609. lvi.mask = LVIF_PARAM;
  610. lvi.iSubItem = 0; // column 0
  611. lvi.iItem = ListView_GetNextItem(_hwndList, -1, LVNI_FOCUSED);
  612. if (lvi.iItem >= 0 && ListView_GetItem(_hwndList, &lvi) &&
  613. (pdinf = GetPdinf((LPLISTITEM) lvi.lParam)) != NULL)
  614. {
  615. DWORD cchRequired;
  616. LPLISTITEM pListItem;
  617. LPTSTR ptszDevid;
  618. pListItem = (LPLISTITEM) lvi.lParam;
  619. if (BUFFERQUERY_SUCCEEDED(
  620. SetupDiGetDeviceInstanceId(_pCD[pListItem->devClass].hdev, pdinf, NULL, 0, &cchRequired)) &&
  621. ((ptszDevid = (LPTSTR)LocalAlloc(LPTR, cchRequired * sizeof(TCHAR))) != NULL)) {
  622. if (SetupDiGetDeviceInstanceId(_pCD[pListItem->devClass].hdev, pdinf, ptszDevid, cchRequired, NULL)) {
  623. DevicePropertiesEx(GetParent(_hdlg), NULL, ptszDevid, 0, FALSE);
  624. }
  625. LocalFree(ptszDevid);
  626. }
  627. }
  628. }
  629. void
  630. CHWTab::OnTshoot(void)
  631. {
  632. LVITEM lvi;
  633. PSP_DEVINFO_DATA pdinf;
  634. lvi.mask = LVIF_PARAM;
  635. lvi.iSubItem = 0; // column 0
  636. lvi.iItem = ListView_GetNextItem(_hwndList, -1, LVNI_FOCUSED);
  637. if (lvi.iItem >= 0 && ListView_GetItem(_hwndList, &lvi) &&
  638. (pdinf = GetPdinf((LPLISTITEM) lvi.lParam)) != NULL)
  639. {
  640. DWORD cchRequired;
  641. LPLISTITEM pListItem;
  642. LPTSTR ptszDevid;
  643. pListItem = (LPLISTITEM) lvi.lParam;
  644. if (BUFFERQUERY_SUCCEEDED(
  645. SetupDiGetDeviceInstanceId(_pCD[pListItem->devClass].hdev, pdinf, NULL, 0, &cchRequired)) &&
  646. ((ptszDevid = (LPTSTR)LocalAlloc(LPTR, cchRequired * sizeof(TCHAR))) != NULL)) {
  647. if (SetupDiGetDeviceInstanceId(_pCD[pListItem->devClass].hdev, pdinf, ptszDevid, cchRequired, NULL)) {
  648. DeviceProblemWizard(GetParent(_hdlg), NULL, ptszDevid);
  649. }
  650. LocalFree(ptszDevid);
  651. }
  652. }
  653. }
  654. //
  655. // SetText is how the caller tells us what our troubleshooter
  656. // command line is.
  657. //
  658. void
  659. CHWTab::OnSetText(LPCTSTR ptszText)
  660. {
  661. BOOL fEnable = ptszText && ptszText[0];
  662. HWND hwndTS = GetDlgItem(_hdlg, IDC_HWTAB_TSHOOT);
  663. EnableWindow(hwndTS, fEnable);
  664. ShowWindow(hwndTS, fEnable ? SW_SHOW : SW_HIDE);
  665. }
  666. void
  667. CHWTab::OnHelp(LPHELPINFO phi)
  668. {
  669. WinHelp((HWND)phi->hItemHandle, DEVMGR_HELP_FILE_NAME, HELP_WM_HELP,
  670. (ULONG_PTR)c_HWTabHelpIDs);
  671. }
  672. void
  673. CHWTab::OnContextMenu(HWND hwnd)
  674. {
  675. WinHelp(hwnd, DEVMGR_HELP_FILE_NAME, HELP_CONTEXTMENU,
  676. (ULONG_PTR)c_HWTabHelpIDs);
  677. }
  678. //
  679. // Dialog procedure (yay).
  680. //
  681. INT_PTR CALLBACK
  682. CHWTab::DialogProc(HWND hdlg, UINT wm, WPARAM wp, LPARAM lp)
  683. {
  684. CHWTab *self = (CHWTab *)GetWindowLongPtr(hdlg, DWLP_USER);
  685. if (wm == WM_INITDIALOG) {
  686. self = (CHWTab *)lp;
  687. return self->OnInitDialog(hdlg);
  688. }
  689. // Ignores messages that arrive before WM_INITDIALOG
  690. if (!self) return FALSE;
  691. switch (wm) {
  692. case WM_DISPLAYCHANGE:
  693. case WM_SETTINGCHANGE:
  694. case WM_SYSCOLORCHANGE:
  695. SendMessage(self->_hwndList, wm, wp, lp);
  696. break;
  697. case WM_NOTIFY:
  698. {
  699. LPNMHDR pnm = (LPNMHDR)lp;
  700. switch (pnm->code) {
  701. case PSN_SETACTIVE:
  702. self->RebuildDeviceList();
  703. break;
  704. case LVN_ITEMCHANGED:
  705. if (pnm->hwndFrom == self->_hwndList) {
  706. self->OnItemChanged((LPNMLISTVIEW)pnm);
  707. }
  708. break;
  709. case NM_DBLCLK:
  710. if (pnm->hwndFrom == self->_hwndList) {
  711. DWORD dwPos = GetMessagePos();
  712. LVHITTESTINFO hti;
  713. hti.pt.x = GET_X_LPARAM(dwPos);
  714. hti.pt.y = GET_Y_LPARAM(dwPos);
  715. ScreenToClient(self->_hwndList, &hti.pt);
  716. ListView_HitTest(self->_hwndList, &hti);
  717. if (hti.iItem >= 0) {
  718. self->OnProperties();
  719. }
  720. }
  721. break;
  722. }
  723. }
  724. break;
  725. case WM_COMMAND:
  726. switch (GET_WM_COMMAND_ID(wp, lp)) {
  727. case IDC_HWTAB_PROP:
  728. self->OnProperties();
  729. break;
  730. case IDC_HWTAB_TSHOOT:
  731. self->OnTshoot();
  732. break;
  733. }
  734. break;
  735. case WM_SETTEXT:
  736. self->OnSetText((LPCTSTR)lp);
  737. break;
  738. case WM_NCDESTROY:
  739. if (self && self->_hwndList)
  740. {
  741. self->RemoveListItems(self->_hwndList);
  742. }
  743. RemoveWindowSubclass(GetParent(hdlg), ParentSubclassProc, 0);
  744. delete self;
  745. break;
  746. case WM_HELP:
  747. self->OnHelp((LPHELPINFO)lp);
  748. break;
  749. case WM_CONTEXTMENU:
  750. self->OnContextMenu((HWND)wp);
  751. break;
  752. }
  753. return FALSE;
  754. }
  755. //
  756. // Create a Hardware page for the specified GUID.
  757. //
  758. // Parameters:
  759. //
  760. // hwndParent - The dummy frame window created by the caller
  761. // pguid - The setup device class GUID we will manage
  762. //
  763. // Returns:
  764. //
  765. // HWND of the created subdialog.
  766. //
  767. // Usage:
  768. //
  769. // When your control panel applet needs a Hardware page, create
  770. // a blank dialog template titled "Hardware" and add it to your
  771. // control panel. Set the size of the blank to be the size you
  772. // want the final Hardware Tab page to be.
  773. //
  774. // Your dialog box procedure should go like this:
  775. //
  776. // BOOL HardwareDlgProc(HWND hdlg, UINT uMsg, WPARAM wp, LPARAM lp) {
  777. // switch (uMsg) {
  778. //
  779. // case WM_INITDIALOG:
  780. // // GUID_DEVCLASS_MOUSE is in devguid.h
  781. // hwndHW = DeviceCreateHardwarePage(hdlg, &GUID_DEVCLASS_MOUSE);
  782. // if (hwndHW) {
  783. // // Optional - Set the troubleshooter command line.
  784. // // Do this if you want a Troubleshoot button.
  785. // SetWindowText(hwndHW,
  786. // TEXT("hh.exe mk:@MSITStore:tshoot.chm::/hdw_drives.htm"));
  787. // } else {
  788. // DestroyWindow(hdlg); // catastrophic failure
  789. // }
  790. // return TRUE;
  791. // }
  792. // return FALSE;
  793. // }
  794. //
  795. STDAPI_(HWND) DeviceCreateHardwarePageEx(HWND hwndParent, const GUID *pguid, int iNumClass, DWORD dwViewMode)
  796. {
  797. if (!hwndParent || !pguid) {
  798. return NULL;
  799. }
  800. HCURSOR hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  801. CHWTab *self = new CHWTab(pguid, iNumClass, dwViewMode);
  802. HWND hwnd;
  803. if (self) {
  804. hwnd = CreateDialogParam(THIS_DLL, MAKEINTRESOURCE(IDD_HWTAB),
  805. hwndParent, CHWTab::DialogProc, (LPARAM)self);
  806. if (!hwnd) {
  807. delete self;
  808. hwnd = NULL;
  809. }
  810. } else {
  811. hwnd = NULL;
  812. }
  813. SetCursor(hcurPrev);
  814. return hwnd;
  815. }
  816. STDAPI_(HWND) DeviceCreateHardwarePage(HWND hwndParent, const GUID *pguid)
  817. {
  818. return DeviceCreateHardwarePageEx(hwndParent, pguid, 1, HWTAB_SMALLLIST);
  819. }