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.

1539 lines
42 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: rconfirm.c
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "HotPlug.h"
  11. #define NOTIFYICONDATA_SZINFO 256
  12. #define NOTIFYICONDATA_SZINFOTITLE 64
  13. #define WM_NOTIFY_MESSAGE (WM_USER + 100)
  14. extern HMODULE hHotPlug;
  15. DWORD
  16. WaitDlgMessagePump(
  17. HWND hDlg,
  18. DWORD nCount,
  19. LPHANDLE Handles
  20. )
  21. {
  22. DWORD WaitReturn;
  23. MSG Msg;
  24. while ((WaitReturn = MsgWaitForMultipleObjects(nCount,
  25. Handles,
  26. FALSE,
  27. INFINITE,
  28. QS_ALLINPUT
  29. ))
  30. == nCount)
  31. {
  32. while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
  33. if (!IsDialogMessage(hDlg,&Msg)) {
  34. TranslateMessage(&Msg);
  35. DispatchMessage(&Msg);
  36. }
  37. }
  38. }
  39. return WaitReturn;
  40. }
  41. int
  42. InsertDeviceNodeListView(
  43. HWND hwndList,
  44. PDEVICETREE DeviceTree,
  45. PDEVTREENODE DeviceTreeNode,
  46. INT lvIndex
  47. )
  48. {
  49. LV_ITEM lviItem;
  50. TCHAR Buffer[MAX_PATH];
  51. lviItem.mask = LVIF_TEXT | LVIF_PARAM;
  52. lviItem.iItem = lvIndex;
  53. lviItem.iSubItem = 0;
  54. if (SetupDiGetClassImageIndex(&DeviceTree->ClassImageList,
  55. &DeviceTreeNode->ClassGuid,
  56. &lviItem.iImage
  57. ))
  58. {
  59. lviItem.mask |= LVIF_IMAGE;
  60. }
  61. lviItem.pszText = FetchDeviceName(DeviceTreeNode);
  62. if (!lviItem.pszText) {
  63. lviItem.pszText = Buffer;
  64. wsprintf(Buffer,
  65. TEXT("%s %s"),
  66. szUnknown,
  67. DeviceTreeNode->Location ? DeviceTreeNode->Location : TEXT("")
  68. );
  69. }
  70. lviItem.lParam = (LPARAM) DeviceTreeNode;
  71. return ListView_InsertItem(hwndList, &lviItem);
  72. }
  73. DWORD
  74. RemoveThread(
  75. PVOID pvDeviceTree
  76. )
  77. {
  78. PDEVICETREE DeviceTree = (PDEVICETREE)pvDeviceTree;
  79. PDEVTREENODE DeviceTreeNode;
  80. DeviceTreeNode = DeviceTree->ChildRemovalList;
  81. return(CM_Request_Device_Eject_Ex(DeviceTreeNode->DevInst,
  82. NULL,
  83. NULL,
  84. 0,
  85. 0,
  86. DeviceTree->hMachine
  87. ));
  88. }
  89. BOOL
  90. OnOkRemove(
  91. HWND hDlg,
  92. PDEVICETREE DeviceTree
  93. )
  94. {
  95. HCURSOR hCursor;
  96. PDEVTREENODE DeviceTreeNode;
  97. HANDLE hThread;
  98. DWORD ThreadId;
  99. DWORD WaitReturn;
  100. PTCHAR DeviceName;
  101. TCHAR szReply[MAX_PATH];
  102. TCHAR Buffer[MAX_PATH];
  103. BOOL bSuccess;
  104. hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  105. DeviceTreeNode = DeviceTree->ChildRemovalList;
  106. DeviceTree->RedrawWait = TRUE;
  107. hThread = CreateThread(NULL,
  108. 0,
  109. RemoveThread,
  110. DeviceTree,
  111. 0,
  112. &ThreadId
  113. );
  114. if (!hThread) {
  115. return FALSE;
  116. }
  117. //
  118. // disable the ok\cancel buttons
  119. //
  120. EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
  121. EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
  122. WaitReturn = WaitDlgMessagePump(hDlg, 1, &hThread);
  123. bSuccess =
  124. (WaitReturn == 0 &&
  125. GetExitCodeThread(hThread, &WaitReturn) &&
  126. WaitReturn == CR_SUCCESS );
  127. SetCursor(hCursor);
  128. DeviceTree->RedrawWait = FALSE;
  129. CloseHandle(hThread);
  130. return bSuccess;
  131. }
  132. #define idh_hwwizard_confirm_stop_list 15321 // "" (SysListView32)
  133. DWORD RemoveConfirmHelpIDs[] = {
  134. IDC_REMOVELIST, idh_hwwizard_confirm_stop_list,
  135. IDC_NOHELP1, NO_HELP,
  136. IDC_NOHELP2, NO_HELP,
  137. IDC_NOHELP3, NO_HELP,
  138. 0,0
  139. };
  140. BOOL
  141. InitRemoveConfirmDlgProc(
  142. HWND hDlg,
  143. PDEVICETREE DeviceTree
  144. )
  145. {
  146. HWND hwndList;
  147. PDEVTREENODE DeviceTreeNode;
  148. int lvIndex;
  149. LV_COLUMN lvcCol;
  150. PDEVTREENODE Next;
  151. HICON hIcon;
  152. hIcon = LoadIcon(hHotPlug,MAKEINTRESOURCE(IDI_HOTPLUGICON));
  153. if (hIcon) {
  154. SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
  155. SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
  156. }
  157. DeviceTreeNode = DeviceTree->ChildRemovalList;
  158. if (!DeviceTreeNode) {
  159. return FALSE;
  160. }
  161. DeviceTree->hwndRemove = hDlg;
  162. hwndList = GetDlgItem(hDlg, IDC_REMOVELIST);
  163. ListView_SetImageList(hwndList, DeviceTree->ClassImageList.ImageList, LVSIL_SMALL);
  164. ListView_DeleteAllItems(hwndList);
  165. // Insert a column for the class list
  166. lvcCol.mask = LVCF_FMT | LVCF_WIDTH;
  167. lvcCol.fmt = LVCFMT_LEFT;
  168. lvcCol.iSubItem = 0;
  169. ListView_InsertColumn(hwndList, 0, (LV_COLUMN FAR *)&lvcCol);
  170. SendMessage(hwndList, WM_SETREDRAW, FALSE, 0L);
  171. //
  172. // Walk the removal list and add each of them to the listbox.
  173. //
  174. lvIndex = 0;
  175. do {
  176. InsertDeviceNodeListView(hwndList, DeviceTree, DeviceTreeNode, lvIndex++);
  177. DeviceTreeNode = DeviceTreeNode->NextChildRemoval;
  178. } while (DeviceTreeNode != DeviceTree->ChildRemovalList);
  179. ListView_SetItemState(hwndList, 0, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
  180. ListView_EnsureVisible(hwndList, 0, FALSE);
  181. ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE_USEHEADER);
  182. SendMessage(hwndList, WM_SETREDRAW, TRUE, 0L);
  183. return TRUE;
  184. }
  185. LRESULT CALLBACK
  186. RemoveConfirmDlgProc(
  187. HWND hDlg,
  188. UINT message,
  189. WPARAM wParam,
  190. LPARAM lParam
  191. )
  192. /*++
  193. Routine Description:
  194. DialogProc to confirm user really wants to remove the devices.
  195. Arguments:
  196. standard stuff.
  197. Return Value:
  198. LRESULT
  199. --*/
  200. {
  201. PDEVICETREE DeviceTree=NULL;
  202. BOOL Status = TRUE;
  203. if (message == WM_INITDIALOG) {
  204. DeviceTree = (PDEVICETREE)lParam;
  205. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)DeviceTree);
  206. if (DeviceTree) {
  207. if (DeviceTree->HideUI) {
  208. PostMessage(hDlg, WUM_EJECTDEVINST, 0, 0);
  209. } else {
  210. InitRemoveConfirmDlgProc(hDlg, DeviceTree);
  211. }
  212. }
  213. return TRUE;
  214. }
  215. //
  216. // retrieve private data from window long (stored there during WM_INITDIALOG)
  217. //
  218. DeviceTree = (PDEVICETREE)GetWindowLongPtr(hDlg, DWLP_USER);
  219. switch (message) {
  220. case WM_DESTROY:
  221. DeviceTree->hwndRemove = NULL;
  222. break;
  223. case WM_CLOSE:
  224. SendMessage (hDlg, WM_COMMAND, IDCANCEL, 0L);
  225. break;
  226. case WM_COMMAND:
  227. switch(wParam) {
  228. case IDOK:
  229. EndDialog(hDlg, OnOkRemove(hDlg, DeviceTree) ? IDOK : IDCANCEL);
  230. break;
  231. case IDCLOSE:
  232. case IDCANCEL:
  233. EndDialog(hDlg, IDCANCEL);
  234. break;
  235. }
  236. break;
  237. case WUM_EJECTDEVINST:
  238. EndDialog(hDlg, OnOkRemove(hDlg, DeviceTree) ? IDOK : IDCANCEL);
  239. break;
  240. case WM_SYSCOLORCHANGE:
  241. break;
  242. case WM_CONTEXTMENU:
  243. WinHelp((HWND)wParam,
  244. TEXT("hardware.hlp"),
  245. HELP_CONTEXTMENU,
  246. (DWORD_PTR)(LPVOID)(PDWORD)RemoveConfirmHelpIDs
  247. );
  248. return FALSE;
  249. case WM_HELP:
  250. OnContextHelp((LPHELPINFO)lParam, RemoveConfirmHelpIDs);
  251. break;
  252. case WM_SETCURSOR:
  253. if (DeviceTree->RedrawWait || DeviceTree->RefreshEvent) {
  254. SetCursor(LoadCursor(NULL, IDC_WAIT));
  255. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 1);
  256. }
  257. break;
  258. default:
  259. return FALSE;
  260. }
  261. return TRUE;
  262. }
  263. #if UNDOCK_WARNING
  264. #define idh_hwwizard_unsafe_remove_list 15330 // "" (SysListView32)
  265. DWORD SurpriseWarnHelpIDs[] = {
  266. IDC_REMOVELIST, idh_hwwizard_unsafe_remove_list,
  267. IDC_NOHELP1, NO_HELP,
  268. IDC_NOHELP2, NO_HELP,
  269. IDC_NOHELP3, NO_HELP,
  270. IDC_NOHELP4, NO_HELP,
  271. IDC_NOHELP5, NO_HELP,
  272. IDC_STATIC, NO_HELP,
  273. 0,0
  274. };
  275. BOOL
  276. InitSurpriseWarnDlgProc(
  277. IN HWND hDlg,
  278. IN PSURPRISE_WARN_COLLECTION SurpriseWarnCollection
  279. )
  280. {
  281. HWND hwndList;
  282. HICON hIcon;
  283. hIcon = LoadIcon(hHotPlug,MAKEINTRESOURCE(IDI_HOTPLUGICON));
  284. if (hIcon) {
  285. SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
  286. SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
  287. }
  288. hwndList = GetDlgItem(hDlg, IDC_REMOVELIST);
  289. SendMessage(hwndList, WM_SETREDRAW, FALSE, 0L);
  290. ListView_DeleteAllItems(hwndList);
  291. DeviceCollectionPopulateListView(
  292. (PDEVICE_COLLECTION) SurpriseWarnCollection,
  293. hwndList
  294. );
  295. ListView_SetItemState(hwndList, 0, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
  296. ListView_EnsureVisible(hwndList, 0, FALSE);
  297. ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE_USEHEADER);
  298. SendMessage(hwndList, WM_SETREDRAW, TRUE, 0L);
  299. return TRUE;
  300. }
  301. BOOL
  302. InitSurpriseUndockDlgProc(
  303. IN HWND hDlg,
  304. IN PSURPRISE_WARN_COLLECTION SurpriseWarnCollection
  305. )
  306. {
  307. HICON hIcon;
  308. TCHAR szFormat[512];
  309. TCHAR szMessage[MAX_DEVICE_ID_LEN + 200];
  310. ULONG dockDeviceIndex;
  311. hIcon = LoadIcon(hHotPlug,MAKEINTRESOURCE(IDI_UNDOCKICON));
  312. if (hIcon) {
  313. SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
  314. SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
  315. }
  316. LoadString(hHotPlug, IDS_UNSAFE_UNDOCK, szFormat, SIZECHARS(szFormat));
  317. if (!DeviceCollectionGetDockDeviceIndex(
  318. (PDEVICE_COLLECTION) SurpriseWarnCollection,
  319. &dockDeviceIndex
  320. )) {
  321. return FALSE;
  322. }
  323. if (!DeviceCollectionFormatDeviceText(
  324. (PDEVICE_COLLECTION) SurpriseWarnCollection,
  325. dockDeviceIndex,
  326. szFormat,
  327. SIZECHARS(szMessage),
  328. szMessage
  329. )) {
  330. return FALSE;
  331. }
  332. SetDlgItemText(hDlg, IDC_UNDOCK_MESSAGE, szMessage);
  333. return TRUE;
  334. }
  335. LRESULT CALLBACK
  336. SurpriseWarnDlgProc(
  337. HWND hDlg,
  338. UINT message,
  339. WPARAM wParam,
  340. LPARAM lParam
  341. )
  342. /*++
  343. Routine Description:
  344. DialogProc to confirm user really wants to remove the devices.
  345. Arguments:
  346. standard stuff.
  347. Return Value:
  348. LRESULT
  349. --*/
  350. {
  351. PSURPRISE_WARN_COLLECTION surpriseWarnCollection = NULL;
  352. BOOL status = TRUE;
  353. if (message == WM_INITDIALOG) {
  354. surpriseWarnCollection = (PSURPRISE_WARN_COLLECTION) lParam;
  355. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  356. status = (surpriseWarnCollection->DockInList) ?
  357. InitSurpriseUndockDlgProc(hDlg, surpriseWarnCollection) :
  358. InitSurpriseWarnDlgProc(hDlg, surpriseWarnCollection);
  359. if (!status) {
  360. EndDialog(hDlg, IDABORT);
  361. }
  362. return TRUE;
  363. }
  364. //
  365. // retrieve private data from window long (stored there during WM_INITDIALOG)
  366. //
  367. surpriseWarnCollection =
  368. (PSURPRISE_WARN_COLLECTION) GetWindowLongPtr(hDlg, DWLP_USER);
  369. switch (message) {
  370. case WM_DESTROY:
  371. break;
  372. case WM_CLOSE:
  373. SendMessage (hDlg, WM_COMMAND, IDCANCEL, 0L);
  374. break;
  375. case WM_COMMAND:
  376. switch(wParam) {
  377. case IDOK:
  378. case IDCLOSE:
  379. surpriseWarnCollection->SuppressSurprise =
  380. IsDlgButtonChecked(hDlg, IDC_SUPPRESS_SURPRISE);
  381. case IDCANCEL:
  382. EndDialog(hDlg, wParam);
  383. break;
  384. }
  385. break;
  386. case WM_SYSCOLORCHANGE:
  387. break;
  388. case WM_HELP:
  389. OnContextHelp((LPHELPINFO)lParam, SurpriseWarnHelpIDs);
  390. break;
  391. case WM_CONTEXTMENU:
  392. WinHelp((HWND)wParam,
  393. TEXT("hardware.hlp"),
  394. HELP_CONTEXTMENU,
  395. (DWORD_PTR)(LPVOID)(PDWORD)SurpriseWarnHelpIDs
  396. );
  397. return FALSE;
  398. case WM_NOTIFY:
  399. switch (((NMHDR FAR *)lParam)->code) {
  400. case LVN_ITEMCHANGED: {
  401. break;
  402. }
  403. }
  404. default:
  405. return FALSE;
  406. }
  407. return TRUE;
  408. }
  409. #endif // UNDOCK_WARNING
  410. #if BUBBLES
  411. LRESULT
  412. CALLBACK
  413. SurpriseWarnBalloonProc(
  414. HWND hWnd,
  415. UINT message,
  416. WPARAM wParam,
  417. LPARAM lParam
  418. )
  419. {
  420. NOTIFYICONDATA nid;
  421. static HICON hHotPlugIcon = NULL;
  422. static BOOL bDialogDisplayed = FALSE;
  423. PSURPRISE_WARN_COLLECTION surpriseWarnCollection;
  424. UINT_PTR timer;
  425. HANDLE hUndockTimer, hUndockEvent;
  426. static BOOL bCheckIfDeviceIsPresent = FALSE;
  427. switch (message) {
  428. case WM_CREATE:
  429. surpriseWarnCollection = (PSURPRISE_WARN_COLLECTION)((CREATESTRUCT*)lParam)->lpCreateParams;
  430. SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) surpriseWarnCollection);
  431. //
  432. // Display the hotplug taskbar icon along with the surprise removal warning balloon.
  433. // The balloon will stay up for 60 seconds or until the user clicks on the balloon
  434. // or icon to display the dialog that lists the devices that were surprise removed.
  435. // After the user closes the dialog the balloon and taskbar icon will go away.
  436. //
  437. hHotPlugIcon = LoadIcon(hHotPlug, MAKEINTRESOURCE(IDI_HOTPLUGICON));
  438. ZeroMemory(&nid, sizeof(nid));
  439. nid.cbSize = sizeof(nid);
  440. nid.hWnd = hWnd;
  441. nid.uID = WM_NOTIFY_MESSAGE;
  442. nid.hIcon = hHotPlugIcon;
  443. nid.uFlags = NIF_MESSAGE | NIF_ICON;
  444. nid.uCallbackMessage = WM_NOTIFY_MESSAGE;
  445. Shell_NotifyIcon(NIM_ADD, &nid);
  446. nid.uVersion = NOTIFYICON_VERSION;
  447. Shell_NotifyIcon(NIM_SETVERSION, &nid);
  448. nid.uFlags = NIF_INFO;
  449. nid.uTimeout = 45000;
  450. nid.dwInfoFlags = NIIF_ERROR;
  451. LoadString(hHotPlug,
  452. IDS_HOTPLUG_TITLE,
  453. nid.szInfoTitle,
  454. SIZECHARS(nid.szInfoTitle)
  455. );
  456. LoadString(hHotPlug,
  457. IDS_HOTPLUG_REMOVE_INFO,
  458. nid.szInfo,
  459. SIZECHARS(nid.szInfo)
  460. );
  461. Shell_NotifyIcon(NIM_MODIFY, &nid);
  462. surpriseWarnCollection->DialogTicker = 0;
  463. OpenGetSurpriseUndockObjects(&hUndockTimer, &hUndockEvent);
  464. if (hUndockTimer) {
  465. if (WaitForSingleObject(hUndockTimer, 0) == WAIT_TIMEOUT) {
  466. //
  467. // The "we're free" event isn't signalled. Nuke this bubble.
  468. //
  469. DestroyWindow(hWnd);
  470. }
  471. CloseHandle(hUndockTimer);
  472. }
  473. if (hUndockEvent) {
  474. CloseHandle(hUndockEvent);
  475. }
  476. SetTimer(hWnd, 0, 250, NULL);
  477. //
  478. // Wait for five seconds before we check to see if the user re-inserted
  479. // the device that we are warning them about.
  480. //
  481. SetTimer(hWnd, TIMERID_DEVICECHANGE, 5000, NULL);
  482. break;
  483. case WM_NOTIFY_MESSAGE:
  484. switch(lParam) {
  485. case NIN_BALLOONTIMEOUT:
  486. DestroyWindow(hWnd);
  487. break;
  488. case NIN_BALLOONUSERCLICK:
  489. case WM_LBUTTONUP:
  490. case WM_RBUTTONUP:
  491. if (!bDialogDisplayed) {
  492. surpriseWarnCollection = (PSURPRISE_WARN_COLLECTION) GetWindowLongPtr(hWnd, GWLP_USERDATA);
  493. //
  494. // Set this so we only display the dialog once!
  495. //
  496. bDialogDisplayed = TRUE;
  497. DialogBoxParam(hHotPlug,
  498. MAKEINTRESOURCE(DLG_SURPRISEWARN),
  499. NULL,
  500. SurpriseWarnDlgProc,
  501. (LPARAM)surpriseWarnCollection
  502. );
  503. DestroyWindow(hWnd);
  504. }
  505. break;
  506. default:
  507. break;
  508. }
  509. break;
  510. case WM_TIMER:
  511. surpriseWarnCollection = (PSURPRISE_WARN_COLLECTION) GetWindowLongPtr(hWnd, GWLP_USERDATA);
  512. if (wParam == TIMERID_DEVICECHANGE) {
  513. KillTimer(hWnd, TIMERID_DEVICECHANGE);
  514. bCheckIfDeviceIsPresent = TRUE;
  515. //
  516. // If all of the devices are present in the machine, then that means
  517. // the user pluged in the device that they just surprise removed,
  518. // so make the balloon go away since the user got the message.
  519. //
  520. if (DeviceCollectionCheckIfAllPresent((PDEVICE_COLLECTION)surpriseWarnCollection)) {
  521. DestroyWindow(hWnd);
  522. }
  523. } else if (wParam == 0) {
  524. surpriseWarnCollection->DialogTicker++;
  525. OpenGetSurpriseUndockObjects(&hUndockTimer, &hUndockEvent);
  526. if (hUndockTimer) {
  527. if (WaitForSingleObject(hUndockTimer, 0) == WAIT_TIMEOUT) {
  528. //
  529. // The "we're free" event isn't signalled. Nuke this bubble.
  530. //
  531. DestroyWindow(hWnd);
  532. }
  533. CloseHandle(hUndockTimer);
  534. }
  535. if (hUndockEvent) {
  536. CloseHandle(hUndockEvent);
  537. }
  538. if (surpriseWarnCollection->DialogTicker >=
  539. surpriseWarnCollection->MaxWaitForDock*4) {
  540. //
  541. // We've waited long enough for this to be associated with a
  542. // surprise undock. Stop wasting CPU cycles polling....
  543. //
  544. KillTimer(hWnd, 0);
  545. }
  546. }
  547. break;
  548. case WM_DEVICECHANGE:
  549. if (DBT_DEVNODES_CHANGED == wParam && bCheckIfDeviceIsPresent) {
  550. SetTimer(hWnd, TIMERID_DEVICECHANGE, 1000, NULL);
  551. }
  552. break;
  553. case WM_DESTROY:
  554. ZeroMemory(&nid, sizeof(nid));
  555. nid.cbSize = sizeof(nid);
  556. nid.hWnd = hWnd;
  557. nid.uID = WM_NOTIFY_MESSAGE;
  558. Shell_NotifyIcon(NIM_DELETE, &nid);
  559. if (hHotPlugIcon) {
  560. DestroyIcon(hHotPlugIcon);
  561. }
  562. surpriseWarnCollection = (PSURPRISE_WARN_COLLECTION) GetWindowLongPtr(hWnd, GWLP_USERDATA);
  563. if (surpriseWarnCollection->DialogTicker <
  564. surpriseWarnCollection->MaxWaitForDock*4) {
  565. KillTimer(hWnd, 0);
  566. }
  567. PostQuitMessage(0);
  568. break;
  569. default:
  570. break;
  571. }
  572. return DefWindowProc(hWnd, message, wParam, lParam);
  573. }
  574. #endif // BUBBLES
  575. LRESULT CALLBACK
  576. SafeRemovalBalloonProc(
  577. HWND hWnd,
  578. UINT message,
  579. WPARAM wParam,
  580. LPARAM lParam
  581. )
  582. {
  583. NOTIFYICONDATA nid;
  584. static HICON hHotPlugIcon = NULL;
  585. TCHAR szFormat[512];
  586. PDEVICE_COLLECTION safeRemovalCollection;
  587. static BOOL bCheckIfDeviceIsRemoved = FALSE;
  588. switch (message) {
  589. case WM_CREATE:
  590. safeRemovalCollection = (PDEVICE_COLLECTION) ((CREATESTRUCT*)lParam)->lpCreateParams;
  591. SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) safeRemovalCollection);
  592. hHotPlugIcon = (HICON)LoadImage(hHotPlug,
  593. MAKEINTRESOURCE(IDI_HOTPLUGICON),
  594. IMAGE_ICON,
  595. GetSystemMetrics(SM_CXSMICON),
  596. GetSystemMetrics(SM_CYSMICON),
  597. 0
  598. );
  599. ZeroMemory(&nid, sizeof(nid));
  600. nid.cbSize = sizeof(nid);
  601. nid.hWnd = hWnd;
  602. nid.uID = WM_NOTIFY_MESSAGE;
  603. nid.hIcon = hHotPlugIcon;
  604. nid.uFlags = NIF_MESSAGE | NIF_ICON;
  605. nid.uCallbackMessage = WM_NOTIFY_MESSAGE;
  606. Shell_NotifyIcon(NIM_ADD, &nid);
  607. nid.uVersion = NOTIFYICON_VERSION;
  608. Shell_NotifyIcon(NIM_SETVERSION, &nid);
  609. nid.uFlags = NIF_INFO;
  610. nid.uTimeout = 10000;
  611. nid.dwInfoFlags = NIIF_INFO;
  612. LoadString(hHotPlug,
  613. IDS_REMOVAL_COMPLETE_TITLE,
  614. nid.szInfoTitle,
  615. SIZECHARS(nid.szInfoTitle)
  616. );
  617. LoadString(hHotPlug, IDS_REMOVAL_COMPLETE_TEXT, szFormat, SIZECHARS(szFormat));
  618. DeviceCollectionFormatDeviceText(
  619. safeRemovalCollection,
  620. 0,
  621. szFormat,
  622. SIZECHARS(nid.szInfo),
  623. nid.szInfo
  624. );
  625. Shell_NotifyIcon(NIM_MODIFY, &nid);
  626. SetTimer(hWnd, TIMERID_DEVICECHANGE, 5000, NULL);
  627. break;
  628. case WM_NOTIFY_MESSAGE:
  629. switch(lParam) {
  630. case NIN_BALLOONTIMEOUT:
  631. case NIN_BALLOONUSERCLICK:
  632. DestroyWindow(hWnd);
  633. break;
  634. default:
  635. break;
  636. }
  637. break;
  638. case WM_DEVICECHANGE:
  639. if ((DBT_DEVNODES_CHANGED == wParam) && bCheckIfDeviceIsRemoved) {
  640. SetTimer(hWnd, TIMERID_DEVICECHANGE, 1000, NULL);
  641. }
  642. break;
  643. case WM_TIMER:
  644. if (wParam == TIMERID_DEVICECHANGE) {
  645. KillTimer(hWnd, TIMERID_DEVICECHANGE);
  646. bCheckIfDeviceIsRemoved = TRUE;
  647. safeRemovalCollection = (PDEVICE_COLLECTION) GetWindowLongPtr(hWnd, GWLP_USERDATA);
  648. if (DeviceCollectionCheckIfAllRemoved(safeRemovalCollection)) {
  649. DestroyWindow(hWnd);
  650. }
  651. }
  652. break;
  653. case WM_DESTROY:
  654. ZeroMemory(&nid, sizeof(nid));
  655. nid.cbSize = sizeof(nid);
  656. nid.hWnd = hWnd;
  657. nid.uID = WM_NOTIFY_MESSAGE;
  658. Shell_NotifyIcon(NIM_DELETE, &nid);
  659. if (hHotPlugIcon) {
  660. DestroyIcon(hHotPlugIcon);
  661. }
  662. PostQuitMessage(0);
  663. break;
  664. default:
  665. break;
  666. }
  667. return DefWindowProc(hWnd, message, wParam, lParam);
  668. }
  669. LRESULT CALLBACK
  670. DockSafeRemovalBalloonProc(
  671. HWND hWnd,
  672. UINT message,
  673. WPARAM wParam,
  674. LPARAM lParam
  675. )
  676. {
  677. NOTIFYICONDATA nid;
  678. static HICON hHotPlugIcon = NULL;
  679. TCHAR szFormat[512];
  680. PDEVICE_COLLECTION safeRemovalCollection;
  681. static BOOL bCheckIfReDocked = FALSE;
  682. BOOL bIsDockStationPresent;
  683. switch (message) {
  684. case WM_CREATE:
  685. safeRemovalCollection = (PDEVICE_COLLECTION) ((CREATESTRUCT*)lParam)->lpCreateParams;
  686. SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) safeRemovalCollection);
  687. hHotPlugIcon = (HICON)LoadImage(hHotPlug,
  688. MAKEINTRESOURCE(IDI_UNDOCKICON),
  689. IMAGE_ICON,
  690. GetSystemMetrics(SM_CXSMICON),
  691. GetSystemMetrics(SM_CYSMICON),
  692. 0
  693. );
  694. ZeroMemory(&nid, sizeof(nid));
  695. nid.cbSize = sizeof(nid);
  696. nid.hWnd = hWnd;
  697. nid.uID = WM_NOTIFY_MESSAGE;
  698. nid.hIcon = hHotPlugIcon;
  699. nid.uFlags = NIF_MESSAGE | NIF_ICON;
  700. nid.uCallbackMessage = WM_NOTIFY_MESSAGE;
  701. Shell_NotifyIcon(NIM_ADD, &nid);
  702. nid.uVersion = NOTIFYICON_VERSION;
  703. Shell_NotifyIcon(NIM_SETVERSION, &nid);
  704. nid.uFlags = NIF_INFO;
  705. nid.uTimeout = 10000;
  706. nid.dwInfoFlags = NIIF_INFO;
  707. LoadString(hHotPlug,
  708. IDS_UNDOCK_COMPLETE_TITLE,
  709. nid.szInfoTitle,
  710. SIZECHARS(nid.szInfoTitle)
  711. );
  712. LoadString(hHotPlug, IDS_UNDOCK_COMPLETE_TEXT, szFormat, SIZECHARS(szFormat));
  713. DeviceCollectionFormatDeviceText(
  714. safeRemovalCollection,
  715. 0,
  716. szFormat,
  717. SIZECHARS(nid.szInfo),
  718. nid.szInfo
  719. );
  720. Shell_NotifyIcon(NIM_MODIFY, &nid);
  721. SetTimer(hWnd, TIMERID_DEVICECHANGE, 5000, NULL);
  722. break;
  723. case WM_NOTIFY_MESSAGE:
  724. switch(lParam) {
  725. case NIN_BALLOONTIMEOUT:
  726. case NIN_BALLOONUSERCLICK:
  727. DestroyWindow(hWnd);
  728. break;
  729. default:
  730. break;
  731. }
  732. break;
  733. case WM_DEVICECHANGE:
  734. if ((DBT_CONFIGCHANGED == wParam) && bCheckIfReDocked) {
  735. SetTimer(hWnd, TIMERID_DEVICECHANGE, 1000, NULL);
  736. }
  737. break;
  738. case WM_TIMER:
  739. if (wParam == TIMERID_DEVICECHANGE) {
  740. KillTimer(hWnd, TIMERID_DEVICECHANGE);
  741. bCheckIfReDocked = TRUE;
  742. //
  743. // Check if the docking station is now present, this means that the
  744. // user redocked the machine and that we should kill the safe to
  745. // undock balloon.
  746. //
  747. bIsDockStationPresent = FALSE;
  748. CM_Is_Dock_Station_Present(&bIsDockStationPresent);
  749. if (bIsDockStationPresent) {
  750. DestroyWindow(hWnd);
  751. }
  752. }
  753. break;
  754. case WM_DESTROY:
  755. ZeroMemory(&nid, sizeof(nid));
  756. nid.cbSize = sizeof(nid);
  757. nid.hWnd = hWnd;
  758. nid.uID = WM_NOTIFY_MESSAGE;
  759. Shell_NotifyIcon(NIM_DELETE, &nid);
  760. if (hHotPlugIcon) {
  761. DestroyIcon(hHotPlugIcon);
  762. }
  763. PostQuitMessage(0);
  764. break;
  765. default:
  766. break;
  767. }
  768. return DefWindowProc(hWnd, message, wParam, lParam);
  769. }
  770. BOOL
  771. VetoedRemovalUI(
  772. IN PVETO_DEVICE_COLLECTION VetoedRemovalCollection
  773. )
  774. {
  775. HANDLE hVetoEvent = NULL;
  776. TCHAR szEventName[MAX_PATH];
  777. TCHAR szFormat[512];
  778. TCHAR szMessage[512];
  779. TCHAR szTitle[256];
  780. PTSTR culpritDeviceId;
  781. PTSTR vetoedDeviceInstancePath;
  782. PTCHAR pStr;
  783. ULONG messageBase;
  784. //
  785. // The first device in the list is the device that failed ejection.
  786. // The next "device" is the name of the vetoer. It may in fact not be a
  787. // device.
  788. //
  789. vetoedDeviceInstancePath = DeviceCollectionGetDeviceInstancePath(
  790. (PDEVICE_COLLECTION) VetoedRemovalCollection,
  791. 0
  792. );
  793. culpritDeviceId = DeviceCollectionGetDeviceInstancePath(
  794. (PDEVICE_COLLECTION) VetoedRemovalCollection,
  795. 1
  796. );
  797. //
  798. // We will now check to see if this same veto message is already being
  799. // displayed. We do this by creating a named event where the name
  800. // contains the three elements that make a veto message unique:
  801. // 1) device instance id
  802. // 2) veto type
  803. // 3) veto operation
  804. //
  805. // If we find an identical veto message already being displayed then we wil
  806. // just go away silently. This prevents multiple identical veto messages
  807. // from showing up on the screen.
  808. //
  809. _snwprintf(szEventName,
  810. SIZECHARS(szEventName),
  811. TEXT("Local\\VETO-%d-%d-%s"),
  812. (DWORD)VetoedRemovalCollection->VetoType,
  813. VetoedRemovalCollection->VetoedOperation,
  814. culpritDeviceId
  815. );
  816. //
  817. // Replace all of the backslashes (except the first one for Local\)
  818. // with pound characters since CreateEvent does not like backslashes.
  819. //
  820. pStr = StrChr(szEventName, TEXT('\\'));
  821. if (pStr) {
  822. pStr++;
  823. }
  824. while (pStr = StrChr(pStr, TEXT('\\'))) {
  825. *pStr = TEXT('#');
  826. }
  827. hVetoEvent = CreateEvent(NULL,
  828. FALSE,
  829. TRUE,
  830. szEventName
  831. );
  832. if (hVetoEvent) {
  833. if (WaitForSingleObject(hVetoEvent, 0) != WAIT_OBJECT_0) {
  834. //
  835. // This means that this veto message is already being displayed
  836. // by another hotplug process...so just go away.
  837. //
  838. CloseHandle(hVetoEvent);
  839. return FALSE;
  840. }
  841. }
  842. //
  843. // Create the veto text
  844. //
  845. switch(VetoedRemovalCollection->VetoedOperation) {
  846. case VETOED_UNDOCK:
  847. case VETOED_WARM_UNDOCK:
  848. messageBase = IDS_DOCKVETO_BASE;
  849. break;
  850. case VETOED_STANDBY:
  851. messageBase = IDS_SLEEPVETO_BASE;
  852. break;
  853. case VETOED_HIBERNATE:
  854. messageBase = IDS_HIBERNATEVETO_BASE;
  855. break;
  856. case VETOED_REMOVAL:
  857. case VETOED_EJECT:
  858. case VETOED_WARM_EJECT:
  859. default:
  860. messageBase = IDS_VETO_BASE;
  861. break;
  862. }
  863. switch(VetoedRemovalCollection->VetoType) {
  864. case PNP_VetoWindowsApp:
  865. if (culpritDeviceId) {
  866. //
  867. // Tell our user the name of the offending application.
  868. //
  869. LoadString(hHotPlug, messageBase+VetoedRemovalCollection->VetoType, szFormat, SIZECHARS(szFormat));
  870. DeviceCollectionFormatDeviceText(
  871. (PDEVICE_COLLECTION) VetoedRemovalCollection,
  872. 1,
  873. szFormat,
  874. SIZECHARS(szMessage),
  875. szMessage
  876. );
  877. } else {
  878. //
  879. // No application, use the "some app" message.
  880. //
  881. messageBase += (IDS_VETO_UNKNOWNWINDOWSAPP - IDS_VETO_WINDOWSAPP);
  882. LoadString(hHotPlug, messageBase+VetoedRemovalCollection->VetoType, szMessage, SIZECHARS(szMessage));
  883. }
  884. break;
  885. case PNP_VetoWindowsService:
  886. case PNP_VetoDriver:
  887. case PNP_VetoLegacyDriver:
  888. //
  889. // PNP_VetoWindowsService, PNP_VetoDriver and PNP_VetoLegacyDriver
  890. // are passed through the service manager to get friendlier names.
  891. //
  892. LoadString(hHotPlug, messageBase+VetoedRemovalCollection->VetoType, szFormat, SIZECHARS(szFormat));
  893. //
  894. // For these veto types, entry index 1 is the vetoing service.
  895. //
  896. DeviceCollectionFormatServiceText(
  897. (PDEVICE_COLLECTION) VetoedRemovalCollection,
  898. 1,
  899. szFormat,
  900. SIZECHARS(szMessage),
  901. szMessage
  902. );
  903. break;
  904. case PNP_VetoDevice:
  905. if ((VetoedRemovalCollection->VetoedOperation == VETOED_WARM_UNDOCK) &&
  906. (!lstrcmp(culpritDeviceId, vetoedDeviceInstancePath))) {
  907. messageBase += (IDS_DOCKVETO_WARM_EJECT - IDS_DOCKVETO_DEVICE);
  908. }
  909. //
  910. // Fall through.
  911. //
  912. case PNP_VetoLegacyDevice:
  913. case PNP_VetoPendingClose:
  914. case PNP_VetoOutstandingOpen:
  915. case PNP_VetoNonDisableable:
  916. case PNP_VetoIllegalDeviceRequest:
  917. //
  918. // Include the veto ID in the display output
  919. //
  920. LoadString(hHotPlug, messageBase+VetoedRemovalCollection->VetoType, szFormat, SIZECHARS(szFormat));
  921. DeviceCollectionFormatDeviceText(
  922. (PDEVICE_COLLECTION) VetoedRemovalCollection,
  923. 1,
  924. szFormat,
  925. SIZECHARS(szMessage),
  926. szMessage
  927. );
  928. break;
  929. case PNP_VetoInsufficientRights:
  930. //
  931. // Use the device itself in the display, but only if we are not
  932. // in the dock case.
  933. //
  934. if ((VetoedRemovalCollection->VetoedOperation == VETOED_UNDOCK)||
  935. (VetoedRemovalCollection->VetoedOperation == VETOED_WARM_UNDOCK)) {
  936. LoadString(hHotPlug, messageBase+VetoedRemovalCollection->VetoType, szMessage, SIZECHARS(szMessage));
  937. break;
  938. }
  939. //
  940. // Fall through.
  941. //
  942. case PNP_VetoInsufficientPower:
  943. case PNP_VetoTypeUnknown:
  944. //
  945. // Use the device itself in the display
  946. //
  947. LoadString(hHotPlug, messageBase+VetoedRemovalCollection->VetoType, szFormat, SIZECHARS(szFormat));
  948. DeviceCollectionFormatDeviceText(
  949. (PDEVICE_COLLECTION) VetoedRemovalCollection,
  950. 0,
  951. szFormat,
  952. SIZECHARS(szMessage),
  953. szMessage
  954. );
  955. break;
  956. default:
  957. ASSERT(0);
  958. LoadString(hHotPlug, messageBase+PNP_VetoTypeUnknown, szFormat, SIZECHARS(szFormat));
  959. DeviceCollectionFormatDeviceText(
  960. (PDEVICE_COLLECTION) VetoedRemovalCollection,
  961. 0,
  962. szFormat,
  963. SIZECHARS(szMessage),
  964. szMessage
  965. );
  966. break;
  967. }
  968. switch(VetoedRemovalCollection->VetoedOperation) {
  969. case VETOED_EJECT:
  970. case VETOED_WARM_EJECT:
  971. LoadString(hHotPlug, IDS_VETOED_EJECT_TITLE, szFormat, SIZECHARS(szFormat));
  972. break;
  973. case VETOED_UNDOCK:
  974. case VETOED_WARM_UNDOCK:
  975. LoadString(hHotPlug, IDS_VETOED_UNDOCK_TITLE, szFormat, SIZECHARS(szFormat));
  976. break;
  977. case VETOED_STANDBY:
  978. LoadString(hHotPlug, IDS_VETOED_STANDBY_TITLE, szFormat, SIZECHARS(szFormat));
  979. break;
  980. case VETOED_HIBERNATE:
  981. LoadString(hHotPlug, IDS_VETOED_HIBERNATION_TITLE, szFormat, SIZECHARS(szFormat));
  982. break;
  983. default:
  984. ASSERT(0);
  985. //
  986. // Fall through, display something at least...
  987. //
  988. case VETOED_REMOVAL:
  989. LoadString(hHotPlug, IDS_VETOED_REMOVAL_TITLE, szFormat, SIZECHARS(szFormat));
  990. break;
  991. }
  992. switch(VetoedRemovalCollection->VetoedOperation) {
  993. case VETOED_STANDBY:
  994. case VETOED_HIBERNATE:
  995. lstrcpyn(szTitle, szFormat, (int)(min(SIZECHARS(szTitle), lstrlen(szFormat)+1)));
  996. break;
  997. case VETOED_EJECT:
  998. case VETOED_WARM_EJECT:
  999. case VETOED_UNDOCK:
  1000. case VETOED_WARM_UNDOCK:
  1001. case VETOED_REMOVAL:
  1002. default:
  1003. DeviceCollectionFormatDeviceText(
  1004. (PDEVICE_COLLECTION) VetoedRemovalCollection,
  1005. 0,
  1006. szFormat,
  1007. SIZECHARS(szTitle),
  1008. szTitle
  1009. );
  1010. break;
  1011. }
  1012. MessageBox(NULL, szMessage, szTitle, MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND | MB_TOPMOST);
  1013. if (hVetoEvent) {
  1014. CloseHandle(hVetoEvent);
  1015. }
  1016. return TRUE;
  1017. }
  1018. void
  1019. DisplayDriverBlockBalloon(
  1020. IN PDEVICE_COLLECTION blockedDriverCollection
  1021. )
  1022. {
  1023. HRESULT hr;
  1024. TCHAR szMessage[NOTIFYICONDATA_SZINFO]; // same size as NOTIFYICONDATA.szInfo
  1025. TCHAR szFormat[NOTIFYICONDATA_SZINFO]; // same size as NOTIFYICONDATA.szInfo
  1026. TCHAR szTitle[NOTIFYICONDATA_SZINFOTITLE]; // same size as NOTIFYICONDATA.szInfoTitle
  1027. HICON hicon = NULL;
  1028. HANDLE hShellReadyEvent = NULL;
  1029. INT ShellReadyEventCount = 0;
  1030. GUID guidDB, guidID;
  1031. HAPPHELPINFOCONTEXT hAppHelpInfoContext = NULL;
  1032. PTSTR Buffer;
  1033. ULONG BufferSize;
  1034. if (!LoadString(hHotPlug, IDS_BLOCKDRIVER_TITLE, szTitle, SIZECHARS(szTitle))) {
  1035. //
  1036. // The machine is so low on memory that we can't even get the text strings, so
  1037. // just exit.
  1038. //
  1039. return;
  1040. }
  1041. szMessage[0] = TEXT('\0');
  1042. if (blockedDriverCollection->NumDevices == 1) {
  1043. //
  1044. // If we only have one device in the list then we will show specific
  1045. // information about this blocked driver as well as directly launching the
  1046. // help for this blocked driver.
  1047. //
  1048. if (SdbGetStandardDatabaseGUID(SDB_DATABASE_MAIN_DRIVERS, &guidDB) &&
  1049. DeviceCollectionGetGuid((PDEVICE_COLLECTION)blockedDriverCollection,
  1050. &guidID,
  1051. 0)) {
  1052. hAppHelpInfoContext = SdbOpenApphelpInformation(&guidDB, &guidID);
  1053. Buffer = NULL;
  1054. if ((hAppHelpInfoContext) &&
  1055. ((BufferSize = SdbQueryApphelpInformation(hAppHelpInfoContext,
  1056. ApphelpAppName,
  1057. NULL,
  1058. 0)) != 0) &&
  1059. (Buffer = (PTSTR)LocalAlloc(LPTR, BufferSize)) &&
  1060. ((BufferSize = SdbQueryApphelpInformation(hAppHelpInfoContext,
  1061. ApphelpAppName,
  1062. Buffer,
  1063. BufferSize)) != 0)) {
  1064. if (LoadString(hHotPlug, IDS_BLOCKDRIVER_FORMAT, szFormat, SIZECHARS(szFormat)) &&
  1065. (lstrlen(szFormat) + lstrlen(Buffer) < NOTIFYICONDATA_SZINFO)) {
  1066. //
  1067. // The app name and format string will fit into the buffer so
  1068. // use the format for the balloon message.
  1069. //
  1070. _snwprintf(szMessage,
  1071. SIZECHARS(szMessage),
  1072. szFormat,
  1073. Buffer);
  1074. } else {
  1075. //
  1076. // The app name is too large to be formated int he balloon
  1077. // message, so just show the app name.
  1078. //
  1079. lstrcpyn(szMessage, Buffer, SIZECHARS(szMessage));
  1080. }
  1081. }
  1082. if (Buffer) {
  1083. LocalFree(Buffer);
  1084. }
  1085. }
  1086. }
  1087. if (szMessage[0] == TEXT('\0')) {
  1088. //
  1089. // We either have more than one driver, or an error occured while trying
  1090. // to access the specific information about the one driver we received,
  1091. // so just show the generic message.
  1092. //
  1093. if (!LoadString(hHotPlug, IDS_BLOCKDRIVER_MESSAGE, szMessage, SIZECHARS(szMessage))) {
  1094. //
  1095. // The machine is so low on memory that we can't even get the text strings, so
  1096. // just exit.
  1097. //
  1098. return;
  1099. }
  1100. }
  1101. hicon = (HICON)LoadImage(hHotPlug,
  1102. MAKEINTRESOURCE(IDI_BLOCKDRIVER),
  1103. IMAGE_ICON,
  1104. GetSystemMetrics(SM_CXSMICON),
  1105. GetSystemMetrics(SM_CYSMICON),
  1106. 0
  1107. );
  1108. //
  1109. // Make sure the shell is up and running so we can display the balloon.
  1110. //
  1111. while ((hShellReadyEvent = OpenEvent(SYNCHRONIZE, FALSE, TEXT("ShellReadyEvent"))) == NULL) {
  1112. //
  1113. // Sleep for 1 second and then try again.
  1114. //
  1115. Sleep(5000);
  1116. if (ShellReadyEventCount++ > 120) {
  1117. //
  1118. // We have been waiting for the shell for 10 minutes and it still
  1119. // is not around.
  1120. //
  1121. break;
  1122. }
  1123. }
  1124. if (hShellReadyEvent) {
  1125. WaitForSingleObject(hShellReadyEvent, INFINITE);
  1126. CloseHandle(hShellReadyEvent);
  1127. if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) {
  1128. IUserNotification *pun;
  1129. hr = CoCreateInstance(CLSID_UserNotification,
  1130. NULL,
  1131. CLSCTX_INPROC_SERVER,
  1132. IID_IUserNotification,
  1133. (void**)&pun);
  1134. if (SUCCEEDED(hr)) {
  1135. pun->SetIconInfo(hicon, szTitle);
  1136. pun->SetBalloonInfo(szTitle, szMessage, NIIF_WARNING);
  1137. //
  1138. // Try once for 20 seconds
  1139. //
  1140. pun->SetBalloonRetry((20 * 1000), -1, 0);
  1141. hr = pun->Show(NULL, 0);
  1142. //
  1143. // if hr is S_OK then user clicked on the balloon, if it is ERROR_CANCELLED
  1144. // then the balloon timedout.
  1145. //
  1146. if (hr == S_OK) {
  1147. if (blockedDriverCollection->NumDevices == 1) {
  1148. //
  1149. // If we only have one device in the list then just
  1150. // launch the help for that blocked driver.
  1151. //
  1152. BufferSize = SdbQueryApphelpInformation(hAppHelpInfoContext,
  1153. ApphelpHelpCenterURL,
  1154. NULL,
  1155. 0);
  1156. if (BufferSize && (Buffer = (PTSTR)LocalAlloc(LPTR, BufferSize + (lstrlen(TEXT("HELPCTR.EXE -url ")) * sizeof(TCHAR))))) {
  1157. lstrcpy(Buffer, TEXT("HELPCTR.EXE -url "));
  1158. BufferSize = SdbQueryApphelpInformation(hAppHelpInfoContext,
  1159. ApphelpHelpCenterURL,
  1160. (PVOID)&Buffer[lstrlen(TEXT("HELPCTR.EXE -url "))],
  1161. BufferSize);
  1162. ShellExecute(NULL,
  1163. TEXT("open"),
  1164. TEXT("HELPCTR.EXE"),
  1165. Buffer,
  1166. NULL,
  1167. SW_SHOWNORMAL);
  1168. LocalFree(Buffer);
  1169. }
  1170. } else {
  1171. //
  1172. // We have more than one device in the list so launch
  1173. // the summary blocked driver page.
  1174. //
  1175. ShellExecute(NULL,
  1176. TEXT("open"),
  1177. TEXT("HELPCTR.EXE"),
  1178. TEXT("HELPCTR.EXE -url hcp://services/centers/support?topic=hcp://system/sysinfo/sysHealthInfo.htm"),
  1179. NULL,
  1180. SW_SHOWNORMAL
  1181. );
  1182. }
  1183. }
  1184. pun->Release();
  1185. }
  1186. CoUninitialize();
  1187. }
  1188. }
  1189. if (hicon) {
  1190. DestroyIcon(hicon);
  1191. }
  1192. if (hAppHelpInfoContext) {
  1193. SdbCloseApphelpInformation(hAppHelpInfoContext);
  1194. }
  1195. }