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.

276 lines
7.4 KiB

  1. // File: taskbar.cpp
  2. #include "precomp.h"
  3. #include "taskbar.h"
  4. static HWND g_hwndHidden = NULL;
  5. const TCHAR g_cszHiddenWndClassName[] = _TEXT("MnmSrvcHiddenWindow");
  6. BOOL g_fTaskBarIconAdded = FALSE;
  7. BOOL g_fTimerRunning = FALSE;
  8. extern INmManager * g_pMgr;
  9. extern int g_cPersonsInConf;
  10. extern DWORD g_dwMainThreadID;
  11. // This routine starts a timer to periodically retry adding the taskbar icon.
  12. // This is necessary in case the taskbar is not showing at the time the
  13. // service is launched, or the taskbar is destroyed by a logoff-logon sequence.
  14. VOID StartTaskbarTimer(VOID)
  15. {
  16. if ( !g_fTimerRunning)
  17. {
  18. ASSERT(g_hwndHidden);
  19. SetTimer(g_hwndHidden, 0, 5000, NULL);
  20. g_fTimerRunning = TRUE;
  21. }
  22. }
  23. VOID KillTaskbarTimer(VOID)
  24. {
  25. if ( g_fTimerRunning )
  26. {
  27. KillTimer ( g_hwndHidden, 0 );
  28. g_fTimerRunning = FALSE;
  29. }
  30. }
  31. LRESULT CALLBACK HiddenWndProc( HWND hwnd, UINT uMsg,
  32. WPARAM wParam, LPARAM lParam)
  33. {
  34. switch(uMsg)
  35. {
  36. case WM_USERCHANGED:
  37. case WM_ENDSESSION:
  38. // A user is logging on or off... We don't know which but
  39. // since the desktop is changing we assume our taskbar icon
  40. // is toast. Start a timer to periodically try to add it back
  41. // until it succeeds.
  42. g_fTaskBarIconAdded = FALSE;
  43. StartTaskbarTimer();
  44. break;
  45. case WM_TASKBAR_NOTIFY:
  46. {
  47. if (WM_RBUTTONUP == lParam)
  48. {
  49. ::OnRightClickTaskbar();
  50. }
  51. break;
  52. }
  53. case WM_TIMER:
  54. AddTaskbarIcon();
  55. break;
  56. case WM_DESTROY:
  57. {
  58. // NULL the global variable:
  59. g_hwndHidden = NULL;
  60. return 0;
  61. }
  62. default:
  63. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  64. }
  65. return FALSE;
  66. }
  67. BOOL AddTaskbarIcon(VOID)
  68. {
  69. BOOL bRet = FALSE;
  70. ASSERT( NULL == g_hwndHidden );
  71. {
  72. // Register hidden window class:
  73. WNDCLASS wcHidden =
  74. {
  75. 0L,
  76. HiddenWndProc,
  77. 0,
  78. 0,
  79. GetModuleHandle(NULL),
  80. NULL,
  81. NULL,
  82. NULL,
  83. NULL,
  84. g_cszHiddenWndClassName
  85. };
  86. if (!RegisterClass(&wcHidden))
  87. {
  88. ERROR_OUT(("Could not register hidden wnd classes"));
  89. return FALSE;
  90. }
  91. // Create a hidden window for event processing:
  92. g_hwndHidden = ::CreateWindow( g_cszHiddenWndClassName,
  93. _TEXT(""),
  94. WS_POPUP, // not visible!
  95. 0, 0, 0, 0,
  96. NULL,
  97. NULL,
  98. GetModuleHandle(NULL),
  99. NULL);
  100. }
  101. if (NULL == g_hwndHidden)
  102. {
  103. ERROR_OUT(("Could not create hidden windows"));
  104. return FALSE;
  105. }
  106. // Place a 16x16 icon in the taskbar notification area:
  107. NOTIFYICONDATA tnid;
  108. tnid.cbSize = sizeof(NOTIFYICONDATA);
  109. tnid.hWnd = g_hwndHidden;
  110. tnid.uID = ID_TASKBAR_ICON;
  111. tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  112. tnid.uCallbackMessage = WM_TASKBAR_NOTIFY;
  113. tnid.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_SM_WORLD));
  114. ::LoadString(GetModuleHandle(NULL), IDS_MNMSRVC_TITLE,
  115. tnid.szTip, CCHMAX(tnid.szTip));
  116. // Attempt to add the icon. This may fail because there is no taskbar
  117. // (no user desktop shown). Warn if this is so... We will retry on
  118. // a periodic timer.
  119. if (FALSE == (bRet = Shell_NotifyIcon(NIM_ADD, &tnid)))
  120. {
  121. #ifdef DEBUG
  122. if ( !g_fTimerRunning )
  123. WARNING_OUT(("Could not add notify icon!"));
  124. #endif // DEBUG
  125. // Start the taskbar timer to periodically retry until this succeeds
  126. StartTaskbarTimer();
  127. }
  128. else
  129. {
  130. g_fTaskBarIconAdded = TRUE;
  131. KillTaskbarTimer(); // Kill timer if necessary
  132. }
  133. if (NULL != tnid.hIcon)
  134. {
  135. DestroyIcon(tnid.hIcon);
  136. }
  137. return bRet;
  138. }
  139. BOOL RemoveTaskbarIcon(VOID)
  140. {
  141. NOTIFYICONDATA tnid;
  142. BOOL ret;
  143. if ( !g_fTaskBarIconAdded || NULL == g_hwndHidden )
  144. {
  145. return FALSE;
  146. }
  147. tnid.cbSize = sizeof(NOTIFYICONDATA);
  148. tnid.hWnd = g_hwndHidden;
  149. tnid.uID = ID_TASKBAR_ICON;
  150. ret = Shell_NotifyIcon(NIM_DELETE, &tnid);
  151. g_fTaskBarIconAdded = FALSE;
  152. if (g_hwndHidden)
  153. {
  154. DestroyWindow(g_hwndHidden);
  155. g_hwndHidden = NULL;
  156. }
  157. return ret;
  158. }
  159. BOOL OnRightClickTaskbar()
  160. {
  161. TRACE_OUT(("OnRightClickTaskbar called"));
  162. POINT ptClick;
  163. if (FALSE == ::GetCursorPos(&ptClick))
  164. {
  165. ptClick.x = ptClick.y = 0;
  166. }
  167. // Get the menu for the popup from the resource file.
  168. HMENU hMenu = ::LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_TASKBAR_POPUP));
  169. if (NULL == hMenu)
  170. {
  171. return FALSE;
  172. }
  173. // Get the first menu in it which we will use for the call to
  174. // TrackPopup(). This could also have been created on the fly using
  175. // CreatePopupMenu and then we could have used InsertMenu() or
  176. // AppendMenu.
  177. HMENU hMenuTrackPopup = ::GetSubMenu(hMenu, 0);
  178. // Draw and track the "floating" popup
  179. // According to the font view code, there is a bug in USER which causes
  180. // TrackPopupMenu to work incorrectly when the window doesn't have the
  181. // focus. The work-around is to temporarily create a hidden window and
  182. // make it the foreground and focus window.
  183. HWND hwndDummy = ::CreateWindow(_TEXT("STATIC"), NULL, 0,
  184. ptClick.x,
  185. ptClick.y,
  186. 1, 1, HWND_DESKTOP,
  187. NULL, GetModuleHandle(NULL), NULL);
  188. if (NULL != hwndDummy)
  189. {
  190. HWND hwndPrev = ::GetForegroundWindow(); // to restore
  191. TPMPARAMS tpmp;
  192. tpmp.cbSize = sizeof(tpmp);
  193. tpmp.rcExclude.right = 1 + (tpmp.rcExclude.left = ptClick.x);
  194. tpmp.rcExclude.bottom = 1 + (tpmp.rcExclude.top = ptClick.y);
  195. ::SetForegroundWindow(hwndDummy);
  196. ::SetFocus(hwndDummy);
  197. int iRet = ::TrackPopupMenuEx( hMenuTrackPopup,
  198. TPM_RETURNCMD | TPM_HORIZONTAL | TPM_RIGHTALIGN |
  199. TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
  200. ptClick.x,
  201. ptClick.y,
  202. hwndDummy,
  203. &tpmp);
  204. // Restore the previous foreground window (before destroying hwndDummy).
  205. if (hwndPrev)
  206. {
  207. ::SetForegroundWindow(hwndPrev);
  208. }
  209. ::DestroyWindow(hwndDummy);
  210. switch (iRet)
  211. {
  212. case IDM_TBPOPUP_STOP:
  213. {
  214. PostThreadMessage(g_dwMainThreadID, WM_QUIT, 0, 0);
  215. break;
  216. }
  217. default:
  218. break;
  219. }
  220. }
  221. // We are finished with the menu now, so destroy it
  222. ::RemoveMenu(hMenu, 0, MF_BYPOSITION);
  223. ::DestroyMenu(hMenuTrackPopup);
  224. ::DestroyMenu(hMenu);
  225. return TRUE;
  226. }