Source code of Windows XP (NT5)
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.

1001 lines
28 KiB

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