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.

999 lines
25 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: hotplug.c
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "HotPlug.h"
  11. TCHAR szUnknown[64];
  12. TCHAR szHotPlugFlags[]=TEXT("HotPlugFlags");
  13. TCHAR HOTPLUG_NOTIFY_CLASS_NAME[] = TEXT("HotPlugNotifyClass");
  14. typedef int
  15. (*PDEVICEPROPERTIES)(
  16. HWND hwndParent,
  17. LPTSTR MachineName,
  18. LPTSTR DeviceID,
  19. BOOL ShowDeviceTree
  20. );
  21. //
  22. // colors used to highlight removal relationships for selected device
  23. //
  24. COLORREF RemovalImageBkColor;
  25. COLORREF NormalImageBkColor;
  26. COLORREF RemovalTextColor;
  27. HWND g_hwndNotify = NULL;
  28. HMODULE hDevMgr=NULL;
  29. PDEVICEPROPERTIES pDeviceProperties = NULL;
  30. #define IDH_DISABLEHELP ((DWORD)(-1))
  31. #define IDH_hwwizard_devices_list 15301 // (SysTreeView32)
  32. #define idh_hwwizard_stop 15305 // "&Stop" (Button)
  33. #define idh_hwwizard_display_components 15307 // "&Display device components" (Button)
  34. #define idh_hwwizard_properties 15311 // "&Properties" (Button)
  35. #define idh_hwwizard_close 15309 // "&Close" (Button)
  36. DWORD UnplugtHelpIDs[] = {
  37. IDC_STOPDEVICE, idh_hwwizard_stop, // "&Stop" (Button)
  38. IDC_PROPERTIES, idh_hwwizard_properties, // "&Properties" (Button)
  39. IDC_VIEWOPTION, idh_hwwizard_display_components, // "&Display device components" (Button)
  40. IDC_DEVICETREE, IDH_hwwizard_devices_list, // "" (SysTreeView32)
  41. IDCLOSE, idh_hwwizard_close,
  42. IDC_MACHINENAME, NO_HELP,
  43. IDC_HDWDEVICES, NO_HELP,
  44. IDC_NOHELP1, NO_HELP,
  45. IDC_NOHELP2, NO_HELP,
  46. IDC_NOHELP3, NO_HELP,
  47. IDC_DEVICEDESC, NO_HELP,
  48. 0,0
  49. };
  50. void
  51. OnRemoveDevice(
  52. HWND hDlg,
  53. PDEVICETREE DeviceTree,
  54. BOOL Eject
  55. )
  56. {
  57. DEVINST DeviceInstance;
  58. CONFIGRET ConfigRet;
  59. HTREEITEM hTreeItem;
  60. PDEVTREENODE DeviceTreeNode;
  61. PTCHAR DeviceName;
  62. DeviceTreeNode = DeviceTree->ChildRemovalList;
  63. if (!DeviceTreeNode) {
  64. return;
  65. }
  66. //
  67. // Confirm with the user that they really want
  68. // to remove this device and all of its attached devices.
  69. // The dialog returns standard IDOK, IDCANCEL etc. for results.
  70. // if anything besides IDOK don't do anything.
  71. //
  72. DialogBoxParam(hHotPlug,
  73. MAKEINTRESOURCE(DLG_CONFIRMREMOVE),
  74. hDlg,
  75. (DLGPROC)RemoveConfirmDlgProc,
  76. (LPARAM)DeviceTree
  77. );
  78. return;
  79. }
  80. void
  81. OnTvnSelChanged(
  82. PDEVICETREE DeviceTree,
  83. NM_TREEVIEW *nmTreeView
  84. )
  85. {
  86. PDEVTREENODE DeviceTreeNode = (PDEVTREENODE)(nmTreeView->itemNew.lParam);
  87. PTCHAR DeviceName, ProblemText;
  88. ULONG DevNodeStatus, Problem;
  89. CONFIGRET ConfigRet;
  90. TCHAR Buffer[MAX_PATH*2];
  91. if (DeviceTree->RedrawWait) {
  92. return;
  93. }
  94. //
  95. // Clear Removal list for previously selected Node
  96. //
  97. ClearRemovalList(DeviceTree);
  98. //
  99. // Save the selected treenode.
  100. //
  101. DeviceTree->SelectedTreeNode = DeviceTreeNode;
  102. //
  103. // No device is selected
  104. //
  105. if (!DeviceTreeNode) {
  106. EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_STOPDEVICE), FALSE);
  107. EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_PROPERTIES), FALSE);
  108. SetDlgItemText(DeviceTree->hDlg, IDC_DEVICEDESC, TEXT(""));
  109. return;
  110. }
  111. //
  112. // reset the text for the selected item
  113. //
  114. DeviceName = FetchDeviceName(DeviceTreeNode);
  115. if (!DeviceName) {
  116. DeviceName = szUnknown;
  117. }
  118. wsprintf(Buffer,
  119. TEXT("%s %s"),
  120. DeviceName,
  121. DeviceTreeNode->Location ? DeviceTreeNode->Location : TEXT("")
  122. );
  123. SetDlgItemText(DeviceTree->hDlg, IDC_DEVICEDESC, Buffer);
  124. //
  125. // Turn on the stop\eject button, and set text accordingly.
  126. //
  127. ConfigRet = CM_Get_DevNode_Status_Ex(&DevNodeStatus,
  128. &Problem,
  129. DeviceTreeNode->DevInst,
  130. 0,
  131. DeviceTree->hMachine
  132. );
  133. if (ConfigRet != CR_SUCCESS) {
  134. DevNodeStatus = 0;
  135. Problem = 0;
  136. }
  137. //
  138. // Any removable (but not surprise removable) device is OK, except
  139. // if the user already removed it.
  140. //
  141. if (Problem != CM_PROB_HELD_FOR_EJECT) {
  142. EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_STOPDEVICE), TRUE);
  143. } else {
  144. EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_STOPDEVICE), FALSE);
  145. }
  146. EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_PROPERTIES), TRUE);
  147. //
  148. // reset the overlay icons if device state has changed
  149. //
  150. if (DeviceTreeNode->Problem != Problem || DeviceTreeNode->DevNodeStatus != DevNodeStatus) {
  151. TV_ITEM tv;
  152. tv.mask = TVIF_STATE;
  153. tv.stateMask = TVIS_OVERLAYMASK;
  154. tv.hItem = DeviceTreeNode->hTreeItem;
  155. if (DeviceTreeNode->Problem == CM_PROB_DISABLED) {
  156. tv.state = INDEXTOOVERLAYMASK(IDI_DISABLED_OVL - IDI_CLASSICON_OVERLAYFIRST + 1);
  157. } else if (DeviceTreeNode->Problem) {
  158. tv.state = INDEXTOOVERLAYMASK(IDI_PROBLEM_OVL - IDI_CLASSICON_OVERLAYFIRST + 1);
  159. } else {
  160. tv.state = INDEXTOOVERLAYMASK(0);
  161. }
  162. TreeView_SetItem(DeviceTree->hwndTree, &tv);
  163. }
  164. //
  165. // Starting from the TopLevel removal node, build up the removal lists
  166. //
  167. DeviceTreeNode = TopLevelRemovalNode(DeviceTree, DeviceTreeNode);
  168. //
  169. // Add devices to ChildRemoval list
  170. //
  171. DeviceTree->ChildRemovalList = DeviceTreeNode;
  172. DeviceTreeNode->NextChildRemoval = DeviceTreeNode;
  173. InvalidateTreeItemRect(DeviceTree->hwndTree, DeviceTreeNode->hTreeItem);
  174. AddChildRemoval(DeviceTree, &DeviceTreeNode->ChildSiblingList);
  175. //
  176. // Add eject amd removal relations
  177. //
  178. AddEjectToRemoval(DeviceTree);
  179. }
  180. int
  181. OnCustomDraw(
  182. HWND hDlg,
  183. PDEVICETREE DeviceTree,
  184. LPNMTVCUSTOMDRAW nmtvCustomDraw
  185. )
  186. {
  187. PDEVTREENODE DeviceTreeNode = (PDEVTREENODE)(nmtvCustomDraw->nmcd.lItemlParam);
  188. if (nmtvCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT) {
  189. return CDRF_NOTIFYITEMDRAW;
  190. }
  191. //
  192. // If this node is in the Removal list, then do special
  193. // highlighting.
  194. //
  195. if (DeviceTreeNode->NextChildRemoval) {
  196. //
  197. // set text color if its not the selected item
  198. //
  199. if (DeviceTree->SelectedTreeNode != DeviceTreeNode) {
  200. nmtvCustomDraw->clrText = RemovalTextColor;
  201. }
  202. //
  203. // Highlight the image-icon background
  204. //
  205. ImageList_SetBkColor(DeviceTree->ClassImageList.ImageList,
  206. RemovalImageBkColor
  207. );
  208. } else {
  209. //
  210. // Normal image-icon background
  211. //
  212. ImageList_SetBkColor(DeviceTree->ClassImageList.ImageList,
  213. NormalImageBkColor
  214. );
  215. }
  216. return CDRF_DODEFAULT;
  217. }
  218. void
  219. OnSysColorChange(
  220. HWND hDlg,
  221. PDEVICETREE DeviceTree
  222. )
  223. {
  224. COLORREF ColorWindow, ColorHighlight;
  225. BYTE Red, Green, Blue;
  226. //
  227. // Fetch the colors used for removal highlighting
  228. //
  229. ColorWindow = GetSysColor(COLOR_WINDOW);
  230. ColorHighlight = GetSysColor(COLOR_HIGHLIGHT);
  231. Red = (BYTE)(((WORD)GetRValue(ColorWindow) + (WORD)GetRValue(ColorHighlight)) >> 1);
  232. Green = (BYTE)(((WORD)GetGValue(ColorWindow) + (WORD)GetGValue(ColorHighlight)) >> 1);
  233. Blue = (BYTE)(((WORD)GetBValue(ColorWindow) + (WORD)GetBValue(ColorHighlight)) >> 1);
  234. RemovalImageBkColor = RGB(Red, Green, Blue);
  235. RemovalTextColor = ColorHighlight;
  236. NormalImageBkColor = ColorWindow;
  237. // Update the ImageList Background color
  238. if (DeviceTree->ClassImageList.cbSize) {
  239. ImageList_SetBkColor(DeviceTree->ClassImageList.ImageList,
  240. ColorWindow
  241. );
  242. }
  243. }
  244. void
  245. OnTvnItemExpanding(
  246. HWND hDlg,
  247. PDEVICETREE DeviceTree,
  248. NM_TREEVIEW *nmTreeView
  249. )
  250. {
  251. PDEVTREENODE DeviceTreeNode = (PDEVTREENODE)(nmTreeView->itemNew.lParam);
  252. //
  253. // don't allow collapse of root items with children
  254. //
  255. if (!DeviceTreeNode->ParentNode &&
  256. (nmTreeView->action == TVE_COLLAPSE ||
  257. nmTreeView->action == TVE_COLLAPSERESET ||
  258. (nmTreeView->action == TVE_TOGGLE &&
  259. (nmTreeView->itemNew.state & TVIS_EXPANDED))) ) {
  260. SetDlgMsgResult(hDlg, WM_NOTIFY, TRUE);
  261. } else {
  262. SetDlgMsgResult(hDlg, WM_NOTIFY, FALSE);
  263. }
  264. }
  265. void
  266. OnContextMenu(
  267. HWND hDlg,
  268. PDEVICETREE DeviceTree
  269. )
  270. {
  271. int IdCmd;
  272. CONFIGRET ConfigRet;
  273. POINT ptPopup;
  274. RECT rect;
  275. HMENU hMenu;
  276. PDEVTREENODE DeviceTreeNode;
  277. TCHAR Buffer[MAX_PATH];
  278. DeviceTreeNode = DeviceTree->SelectedTreeNode;
  279. if (!DeviceTreeNode) {
  280. return;
  281. }
  282. TreeView_GetItemRect(DeviceTree->hwndTree,
  283. DeviceTreeNode->hTreeItem,
  284. &rect,
  285. TRUE
  286. );
  287. ptPopup.x = (rect.left+rect.right)/2;
  288. ptPopup.y = (rect.top+rect.bottom)/2;
  289. ClientToScreen(DeviceTree->hwndTree, &ptPopup);
  290. hMenu = CreatePopupMenu();
  291. if (!hMenu) {
  292. return;
  293. }
  294. //
  295. // if device is running add stop item
  296. //
  297. if (DeviceTreeNode->DevNodeStatus & DN_STARTED) {
  298. LoadString(hHotPlug,
  299. IDS_STOP,
  300. Buffer,
  301. SIZECHARS(Buffer)
  302. );
  303. AppendMenu(hMenu, MF_STRING, IDC_STOPDEVICE, Buffer);
  304. }
  305. //
  306. // add Properties item (link to device mgr).
  307. //
  308. LoadString(hHotPlug,
  309. IDS_PROPERTIES,
  310. Buffer,
  311. SIZECHARS(Buffer)
  312. );
  313. AppendMenu(hMenu, MF_STRING, IDC_PROPERTIES, Buffer);
  314. IdCmd = TrackPopupMenu(hMenu,
  315. TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN | TPM_NONOTIFY,
  316. ptPopup.x,
  317. ptPopup.y,
  318. 0,
  319. hDlg,
  320. NULL
  321. );
  322. DestroyMenu(hMenu);
  323. if (!IdCmd) {
  324. return;
  325. }
  326. switch (IdCmd) {
  327. case IDC_STOPDEVICE:
  328. OnRemoveDevice(hDlg, DeviceTree, FALSE);
  329. break;
  330. case IDC_PROPERTIES: {
  331. if (pDeviceProperties) {
  332. (*pDeviceProperties)(
  333. hDlg,
  334. DeviceTree->hMachine ? DeviceTree->MachineName : NULL,
  335. DeviceTreeNode->InstanceId,
  336. FALSE
  337. );
  338. }
  339. }
  340. break;
  341. }
  342. return;
  343. }
  344. void
  345. OnRightClick(
  346. HWND hDlg,
  347. PDEVICETREE DeviceTree,
  348. NMHDR * nmhdr
  349. )
  350. {
  351. DWORD dwPos;
  352. TV_ITEM tvi;
  353. TV_HITTESTINFO tvht;
  354. PDEVTREENODE DeviceTreeNode;
  355. if (nmhdr->hwndFrom != DeviceTree->hwndTree) {
  356. return;
  357. }
  358. dwPos = GetMessagePos();
  359. tvht.pt.x = LOWORD(dwPos);
  360. tvht.pt.y = HIWORD(dwPos);
  361. ScreenToClient(DeviceTree->hwndTree, &tvht.pt);
  362. tvi.hItem = TreeView_HitTest(DeviceTree->hwndTree, &tvht);
  363. if (!tvi.hItem) {
  364. return;
  365. }
  366. tvi.mask = TVIF_PARAM;
  367. if (!TreeView_GetItem(DeviceTree->hwndTree, &tvi)) {
  368. return;
  369. }
  370. DeviceTreeNode = (PDEVTREENODE)tvi.lParam;
  371. if (!DeviceTreeNode) {
  372. return;
  373. }
  374. //
  375. // Make the current right click item, the selected item
  376. //
  377. if (DeviceTreeNode != DeviceTree->SelectedTreeNode) {
  378. TreeView_SelectItem(DeviceTree->hwndTree, DeviceTreeNode->hTreeItem);
  379. }
  380. }
  381. void
  382. OnViewOptionClicked(
  383. HWND hDlg,
  384. PDEVICETREE DeviceTree
  385. )
  386. {
  387. BOOL bChecked;
  388. DWORD HotPlugFlags, NewFlags;
  389. HKEY hKey = NULL;
  390. PDEVTREENODE DeviceTreeNode;
  391. DEVINST DeviceInstance;
  392. HTREEITEM hTreeItem;
  393. TV_ITEM TvItem;
  394. //
  395. // checked means "show complex view"
  396. //
  397. bChecked = IsDlgButtonChecked(hDlg, IDC_VIEWOPTION);
  398. //
  399. // Update HotPlugs registry if needed.
  400. //
  401. NewFlags = HotPlugFlags = GetHotPlugFlags(&hKey);
  402. if (hKey) {
  403. if (bChecked) {
  404. NewFlags |= HOTPLUG_REGFLAG_VIEWALL;
  405. } else {
  406. NewFlags &= ~HOTPLUG_REGFLAG_VIEWALL;
  407. }
  408. if (NewFlags != HotPlugFlags) {
  409. RegSetValueEx(hKey,
  410. szHotPlugFlags,
  411. 0,
  412. REG_DWORD,
  413. (LPBYTE)&NewFlags,
  414. sizeof(NewFlags)
  415. );
  416. }
  417. if (hKey) {
  418. RegCloseKey(hKey);
  419. }
  420. }
  421. if (!DeviceTree->ComplexView && bChecked) {
  422. DeviceTree->ComplexView = TRUE;
  423. } else if (DeviceTree->ComplexView && !bChecked) {
  424. DeviceTree->ComplexView = FALSE;
  425. } else {
  426. // we are in the correct state, nothing to do.
  427. return;
  428. }
  429. //
  430. // redraw the entire tree.
  431. //
  432. RefreshTree(DeviceTree);
  433. return;
  434. }
  435. LRESULT
  436. hotplugNotifyWndProc(
  437. HWND hWnd,
  438. UINT uMsg,
  439. WPARAM wParam,
  440. LPARAM lParam
  441. )
  442. {
  443. HWND hMainWnd;
  444. hMainWnd = (HWND)GetWindowLongPtr(hWnd, GWLP_USERDATA);
  445. switch (uMsg) {
  446. case WM_CREATE:
  447. {
  448. hMainWnd = (HWND)((CREATESTRUCT*)lParam)->lpCreateParams;
  449. SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)hMainWnd);
  450. break;
  451. }
  452. case WM_DEVICECHANGE:
  453. {
  454. if (DBT_DEVNODES_CHANGED == wParam) {
  455. // While we are in WM_DEVICECHANGE context,
  456. // no CM apis can be called because it would
  457. // deadlock. Here, we schedule a timer so that
  458. // we can handle the message later on.
  459. SetTimer(hMainWnd, TIMERID_DEVICECHANGE, 1000, NULL);
  460. }
  461. break;
  462. }
  463. default:
  464. break;
  465. }
  466. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  467. }
  468. BOOL
  469. CreateNotifyWindow(
  470. HWND hWnd
  471. )
  472. {
  473. WNDCLASS wndClass;
  474. if (!GetClassInfo(hHotPlug, HOTPLUG_NOTIFY_CLASS_NAME, &wndClass)) {
  475. memset(&wndClass, 0, sizeof(wndClass));
  476. wndClass.lpfnWndProc = hotplugNotifyWndProc;
  477. wndClass.hInstance = hHotPlug;
  478. wndClass.lpszClassName = HOTPLUG_NOTIFY_CLASS_NAME;
  479. if (!RegisterClass(&wndClass)) {
  480. return FALSE;
  481. }
  482. }
  483. g_hwndNotify = CreateWindowEx(WS_EX_TOOLWINDOW,
  484. HOTPLUG_NOTIFY_CLASS_NAME,
  485. TEXT(""),
  486. WS_DLGFRAME | WS_BORDER | WS_DISABLED,
  487. CW_USEDEFAULT,
  488. CW_USEDEFAULT,
  489. 0,
  490. 0,
  491. NULL,
  492. NULL,
  493. hHotPlug,
  494. (void *)hWnd
  495. );
  496. return(NULL != g_hwndNotify);
  497. }
  498. BOOL
  499. InitDevTreeDlgProc(
  500. HWND hDlg,
  501. PDEVICETREE DeviceTree
  502. )
  503. {
  504. CONFIGRET ConfigRet;
  505. HWND hwndTree;
  506. HTREEITEM hTreeItem;
  507. DEVINST DeviceInstance;
  508. DWORD HotPlugFlags;
  509. HICON hIcon;
  510. HWND hwndParent;
  511. DeviceTree->AllowRefresh = TRUE;
  512. CreateNotifyWindow(hDlg);
  513. hDevMgr = LoadLibrary(TEXT("devmgr.dll"));
  514. if (hDevMgr) {
  515. pDeviceProperties = (PDEVICEPROPERTIES)GetProcAddress(hDevMgr, "DevicePropertiesW");
  516. }
  517. hIcon = LoadIcon(hHotPlug,MAKEINTRESOURCE(IDI_HOTPLUGICON));
  518. if (hIcon) {
  519. SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
  520. SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
  521. }
  522. hwndParent = GetParent(hDlg);
  523. if (hwndParent) {
  524. SendMessage(hwndParent, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
  525. SendMessage(hwndParent, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
  526. }
  527. DeviceTree->hDlg = hDlg;
  528. DeviceTree->hwndTree = hwndTree = GetDlgItem(hDlg, IDC_DEVICETREE);
  529. LoadString(hHotPlug,
  530. IDS_UNKNOWN,
  531. (PTCHAR)szUnknown,
  532. SIZECHARS(szUnknown)
  533. );
  534. if (DeviceTree->hMachine) {
  535. SetDlgItemText(hDlg, IDC_MACHINENAME, DeviceTree->MachineName);
  536. }
  537. //
  538. // Disable the Stop button, until an item is selected.
  539. //
  540. EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_STOPDEVICE), FALSE);
  541. EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_PROPERTIES), FALSE);
  542. // Get the Class Icon Image Lists
  543. DeviceTree->ClassImageList.cbSize = sizeof(SP_CLASSIMAGELIST_DATA);
  544. if (SetupDiGetClassImageList(&DeviceTree->ClassImageList)) {
  545. TreeView_SetImageList(hwndTree, DeviceTree->ClassImageList.ImageList, TVSIL_NORMAL);
  546. } else {
  547. DeviceTree->ClassImageList.cbSize = 0;
  548. }
  549. OnSysColorChange(hDlg, DeviceTree);
  550. HotPlugFlags = GetHotPlugFlags(NULL);
  551. if (HotPlugFlags & HOTPLUG_REGFLAG_VIEWALL) {
  552. DeviceTree->ComplexView = TRUE;
  553. CheckDlgButton(hDlg, IDC_VIEWOPTION, BST_CHECKED);
  554. } else {
  555. DeviceTree->ComplexView = FALSE;
  556. CheckDlgButton(hDlg, IDC_VIEWOPTION, BST_UNCHECKED);
  557. }
  558. //
  559. // Get the root devnode.
  560. //
  561. ConfigRet = CM_Locate_DevNode_Ex(&DeviceTree->DevInst,
  562. NULL,
  563. CM_LOCATE_DEVNODE_NORMAL,
  564. DeviceTree->hMachine
  565. );
  566. if (ConfigRet != CR_SUCCESS) {
  567. return FALSE;
  568. }
  569. RefreshTree(DeviceTree);
  570. if (DeviceTree->EjectDeviceInstanceId) {
  571. DEVINST EjectDevInst;
  572. PDEVTREENODE DeviceTreeNode;
  573. //
  574. // we are removing a specific device, find it
  575. // and post a message to trigger device removal.
  576. //
  577. ConfigRet = CM_Locate_DevNode_Ex(&EjectDevInst,
  578. DeviceTree->EjectDeviceInstanceId,
  579. CM_LOCATE_DEVNODE_NORMAL,
  580. DeviceTree->hMachine
  581. );
  582. if (ConfigRet != CR_SUCCESS) {
  583. return FALSE;
  584. }
  585. DeviceTreeNode = DevTreeNodeByDevInst(EjectDevInst,
  586. &DeviceTree->ChildSiblingList
  587. );
  588. if (!DeviceTreeNode) {
  589. return FALSE;
  590. }
  591. TreeView_SelectItem(hwndTree, DeviceTreeNode->hTreeItem);
  592. PostMessage(hDlg, WUM_EJECTDEVINST, 0, 0);
  593. } else {
  594. ShowWindow(hDlg, SW_SHOW);
  595. }
  596. return TRUE;
  597. }
  598. void
  599. OnContextHelp(
  600. LPHELPINFO HelpInfo,
  601. PDWORD ContextHelpIDs
  602. )
  603. {
  604. // Define an array of dword pairs,
  605. // where the first of each pair is the control ID,
  606. // and the second is the context ID for a help topic,
  607. // which is used in the help file.
  608. if (HelpInfo->iContextType == HELPINFO_WINDOW) { // must be for a control
  609. WinHelp((HWND)HelpInfo->hItemHandle,
  610. TEXT("hardware.hlp"),
  611. HELP_WM_HELP,
  612. (DWORD_PTR)(void *)ContextHelpIDs
  613. );
  614. }
  615. }
  616. LRESULT CALLBACK DevTreeDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  617. {
  618. PDEVICETREE DeviceTree = NULL;
  619. BOOL Status = TRUE;
  620. if (message == WM_INITDIALOG) {
  621. DeviceTree = (PDEVICETREE)lParam;
  622. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)DeviceTree);
  623. if (DeviceTree) {
  624. InitDevTreeDlgProc(hDlg, DeviceTree);
  625. }
  626. return TRUE;
  627. }
  628. // retrieve private data from window long (stored there during WM_INITDIALOG)
  629. DeviceTree = (PDEVICETREE)GetWindowLongPtr(hDlg, DWLP_USER);
  630. switch (message) {
  631. case WM_DESTROY:
  632. // Destroy the Notification Window
  633. if (g_hwndNotify && IsWindow(g_hwndNotify)) {
  634. DestroyWindow(g_hwndNotify);
  635. g_hwndNotify = NULL;
  636. }
  637. // Clear the DeviceTree
  638. TreeView_DeleteAllItems(DeviceTree->hwndTree);
  639. // clean up the class image list.
  640. if (DeviceTree->ClassImageList.cbSize) {
  641. SetupDiDestroyClassImageList(&DeviceTree->ClassImageList);
  642. DeviceTree->ClassImageList.cbSize = 0;
  643. }
  644. // Clean up the device tree
  645. ClearRemovalList(DeviceTree);
  646. RemoveChildSiblings(DeviceTree, &DeviceTree->ChildSiblingList);
  647. if (hDevMgr) {
  648. FreeLibrary(hDevMgr);
  649. hDevMgr = NULL;
  650. pDeviceProperties = NULL;
  651. }
  652. break;
  653. case WM_CLOSE:
  654. SendMessage(hDlg, WM_COMMAND, IDCANCEL, 0L);
  655. break;
  656. case WM_COMMAND:
  657. {
  658. UINT Control = GET_WM_COMMAND_ID(wParam, lParam);
  659. UINT Cmd = GET_WM_COMMAND_CMD(wParam, lParam);
  660. switch (Control) {
  661. case IDC_VIEWOPTION:
  662. if (Cmd == BN_CLICKED) {
  663. OnViewOptionClicked(hDlg, DeviceTree);
  664. }
  665. break;
  666. case IDC_STOPDEVICE:
  667. OnRemoveDevice(hDlg, DeviceTree, FALSE);
  668. break;
  669. case IDOK: // enter -> default to expand\collapse the selected tree node
  670. if (DeviceTree->SelectedTreeNode) {
  671. TreeView_Expand(DeviceTree->hwndTree,
  672. DeviceTree->SelectedTreeNode->hTreeItem, TVE_TOGGLE);
  673. }
  674. break;
  675. case IDC_PROPERTIES:
  676. if (DeviceTree->SelectedTreeNode && pDeviceProperties) {
  677. (*pDeviceProperties)(hDlg,
  678. DeviceTree->hMachine ? DeviceTree->MachineName : NULL,
  679. DeviceTree->SelectedTreeNode->InstanceId, FALSE);
  680. }
  681. break;
  682. case IDCLOSE:
  683. case IDCANCEL:
  684. EndDialog(hDlg, IDCANCEL);
  685. break;
  686. }
  687. }
  688. break;
  689. // Listen for Tree notifications
  690. case WM_NOTIFY:
  691. switch (((NMHDR *)lParam)->code) {
  692. case TVN_SELCHANGED:
  693. OnTvnSelChanged(DeviceTree, (NM_TREEVIEW *)lParam);
  694. break;
  695. case TVN_ITEMEXPANDING:
  696. OnTvnItemExpanding(hDlg,DeviceTree,(NM_TREEVIEW *)lParam);
  697. break;
  698. case TVN_KEYDOWN:
  699. {
  700. TV_KEYDOWN *tvKeyDown = (TV_KEYDOWN *)lParam;
  701. if (tvKeyDown->wVKey == VK_DELETE) {
  702. OnRemoveDevice(hDlg, DeviceTree, TRUE);
  703. }
  704. }
  705. break;
  706. case NM_CUSTOMDRAW:
  707. if (IDC_DEVICETREE == ((NMHDR *)lParam)->idFrom) {
  708. SetDlgMsgResult(hDlg, WM_NOTIFY, OnCustomDraw(hDlg, DeviceTree, (NMTVCUSTOMDRAW *)lParam));
  709. }
  710. break;
  711. case NM_RETURN:
  712. // we don't get this in a dialog, see IDOK
  713. break;
  714. case NM_DBLCLK:
  715. OnRemoveDevice(hDlg, DeviceTree, TRUE);
  716. SetDlgMsgResult(hDlg, WM_NOTIFY, TRUE);
  717. break;
  718. case NM_RCLICK:
  719. OnRightClick(hDlg,DeviceTree, (NMHDR *)lParam);
  720. break;
  721. default:
  722. return FALSE;
  723. }
  724. break;
  725. case WUM_EJECTDEVINST:
  726. OnRemoveDevice(hDlg, DeviceTree, TRUE);
  727. EndDialog(hDlg, IDCANCEL);
  728. break;
  729. case WM_SYSCOLORCHANGE:
  730. HotPlugPropagateMessage(hDlg, message, wParam, lParam);
  731. OnSysColorChange(hDlg,DeviceTree);
  732. break;
  733. case WM_TIMER:
  734. if (TIMERID_DEVICECHANGE == wParam) {
  735. KillTimer(hDlg, TIMERID_DEVICECHANGE);
  736. DeviceTree->RefreshEvent = TRUE;
  737. if (DeviceTree->AllowRefresh) {
  738. OnTimerDeviceChange(DeviceTree);
  739. }
  740. }
  741. break;
  742. case WM_SETCURSOR:
  743. if (DeviceTree->RedrawWait || DeviceTree->RefreshEvent) {
  744. SetCursor(LoadCursor(NULL, IDC_WAIT));
  745. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 1);
  746. break;
  747. }
  748. return FALSE;
  749. case WM_CONTEXTMENU:
  750. //
  751. // handle kbd- shift-F10, mouse rclick is invoked from NM_RCLICK
  752. //
  753. if ((HWND)wParam == DeviceTree->hwndTree) {
  754. OnContextMenu(hDlg, DeviceTree);
  755. break;
  756. } else {
  757. WinHelp((HWND)wParam, TEXT("hardware.hlp"), HELP_CONTEXTMENU,
  758. (DWORD_PTR)(void *)(PDWORD)UnplugtHelpIDs);
  759. }
  760. return FALSE;
  761. case WM_HELP:
  762. OnContextHelp((LPHELPINFO)lParam, (PDWORD)UnplugtHelpIDs);
  763. break;
  764. default:
  765. return FALSE;
  766. }
  767. return TRUE;
  768. }