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

397 lines
10 KiB

  1. #include "shellprv.h"
  2. #define BALLOON_SHOW_TIME 15000 // 15 sec
  3. #define BALLOON_REPEAT_DELAY 10000 // 10 sec
  4. #define WM_NOTIFY_MESSAGE (WM_USER + 100)
  5. #define IDT_REMINDER 1
  6. #define IDT_DESTROY 2
  7. #define IDT_QUERYCANCEL 3
  8. #define IDT_NOBALLOON 4
  9. class CUserNotification : public IUserNotification
  10. {
  11. public:
  12. CUserNotification();
  13. // IUnknown
  14. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  15. STDMETHODIMP_(ULONG) AddRef();
  16. STDMETHODIMP_(ULONG) Release();
  17. // IUserNotification
  18. STDMETHODIMP SetBalloonInfo(LPCWSTR pszTitle, LPCWSTR pszText, DWORD dwInfoFlags);
  19. STDMETHODIMP SetBalloonRetry(DWORD dwShowTime, DWORD dwInterval, UINT cRetryCount);
  20. STDMETHODIMP SetIconInfo(HICON hIcon, LPCWSTR pszToolTip);
  21. STDMETHODIMP Show(IQueryContinue *pqc, DWORD dwContinuePollInterval);
  22. STDMETHODIMP PlaySound(LPCWSTR pszSoundName);
  23. private:
  24. ~CUserNotification();
  25. static LRESULT CALLBACK s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  26. LRESULT CALLBACK _WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
  27. HRESULT _GetWindow();
  28. BOOL _SyncInfo();
  29. BOOL _SyncIcon();
  30. void _DelayDestroy(HRESULT hrDone);
  31. void _Timeout();
  32. void _RemoveNotifyIcon();
  33. LONG _cRef;
  34. HWND _hwnd;
  35. HICON _hicon;
  36. DWORD _dwShowTime;
  37. DWORD _dwInterval;
  38. UINT _cRetryCount;
  39. HRESULT _hrDone;
  40. DWORD _dwContinuePollInterval;
  41. IQueryContinue *_pqc;
  42. DWORD _dwInfoFlags;
  43. WCHAR *_pszTitle;
  44. WCHAR *_pszText;
  45. WCHAR *_pszToolTip;
  46. };
  47. CUserNotification::CUserNotification()
  48. : _cRef(1), _cRetryCount(-1), _dwShowTime(BALLOON_SHOW_TIME),
  49. _dwInterval(BALLOON_REPEAT_DELAY), _dwInfoFlags(NIIF_NONE)
  50. {
  51. }
  52. CUserNotification::~CUserNotification()
  53. {
  54. Str_SetPtrW(&_pszToolTip, NULL);
  55. Str_SetPtrW(&_pszTitle, NULL);
  56. Str_SetPtrW(&_pszText, NULL);
  57. if (_hwnd)
  58. {
  59. _RemoveNotifyIcon();
  60. DestroyWindow(_hwnd);
  61. }
  62. if (_hicon)
  63. DestroyIcon(_hicon);
  64. }
  65. STDMETHODIMP_(ULONG) CUserNotification::AddRef()
  66. {
  67. return InterlockedIncrement(&_cRef);
  68. }
  69. STDMETHODIMP_(ULONG) CUserNotification::Release()
  70. {
  71. ASSERT( 0 != _cRef );
  72. ULONG cRef = InterlockedDecrement(&_cRef);
  73. if ( 0 == cRef )
  74. {
  75. delete this;
  76. }
  77. return cRef;
  78. }
  79. HRESULT CUserNotification::QueryInterface(REFIID riid, void **ppv)
  80. {
  81. static const QITAB qit[] =
  82. {
  83. QITABENT(CUserNotification, IUserNotification),
  84. { 0 },
  85. };
  86. return QISearch(this, qit, riid, ppv);
  87. }
  88. HRESULT CUserNotification::SetBalloonInfo(LPCWSTR pszTitle, LPCWSTR pszText, DWORD dwInfoFlags)
  89. {
  90. Str_SetPtrW(&_pszTitle, pszTitle);
  91. Str_SetPtrW(&_pszText, pszText);
  92. _dwInfoFlags = dwInfoFlags;
  93. _SyncInfo(); // may fail if balloon _hwnd has not been created yet
  94. return S_OK;
  95. }
  96. HRESULT CUserNotification::SetBalloonRetry(DWORD dwShowTime, DWORD dwInterval, UINT cRetryCount)
  97. {
  98. if (-1 != dwShowTime)
  99. _dwShowTime = dwShowTime;
  100. if (-1 != dwInterval)
  101. _dwInterval = dwInterval;
  102. _cRetryCount = cRetryCount;
  103. return S_OK;
  104. }
  105. HRESULT CUserNotification::SetIconInfo(HICON hIcon, LPCWSTR pszToolTip)
  106. {
  107. if (_hicon)
  108. DestroyIcon(_hicon);
  109. if (hIcon == NULL)
  110. {
  111. _hicon = NULL;
  112. switch(_dwInfoFlags & NIIF_ICON_MASK)
  113. {
  114. case NIIF_INFO:
  115. _hicon = LoadIcon(NULL, IDI_INFORMATION);
  116. break;
  117. case NIIF_WARNING:
  118. _hicon = LoadIcon(NULL, IDI_WARNING);
  119. break;
  120. case NIIF_ERROR:
  121. _hicon = LoadIcon(NULL, IDI_ERROR);
  122. break;
  123. }
  124. }
  125. else
  126. {
  127. _hicon = CopyIcon(hIcon);
  128. }
  129. Str_SetPtrW(&_pszToolTip, pszToolTip);
  130. _SyncIcon();
  131. return S_OK;
  132. }
  133. // returns:
  134. // S_OK
  135. // user clicked on the balloon or icon
  136. // S_FALSE
  137. // query continue callback (pcq) cancelled the notification UI
  138. // HRESULT_FROM_WIN32(ERROR_CANCELLED)
  139. // timeouts expired (user ignored the UI)
  140. HRESULT CUserNotification::Show(IQueryContinue *pqc, DWORD dwContinuePollInterval)
  141. {
  142. HRESULT hr = _GetWindow();
  143. if (SUCCEEDED(hr))
  144. {
  145. if (pqc)
  146. {
  147. _pqc = pqc; // don't need a ref since we don't return from here
  148. _dwContinuePollInterval = dwContinuePollInterval > 100 ? dwContinuePollInterval : 500;
  149. SetTimer(_hwnd, IDT_QUERYCANCEL, _dwContinuePollInterval, NULL);
  150. }
  151. // if there is no balloon info specified then there won't be a "balloon timeout" event
  152. // thus we need to do this ourselves. this lets people use this object for non balloon
  153. // notification icons
  154. if ((NULL == _pszTitle) && (NULL == _pszText))
  155. {
  156. SetTimer(_hwnd, IDT_NOBALLOON, _dwShowTime, NULL);
  157. }
  158. MSG msg;
  159. while (GetMessage(&msg, NULL, 0, 0))
  160. {
  161. TranslateMessage(&msg);
  162. DispatchMessage(&msg);
  163. }
  164. hr = _hrDone;
  165. if (pqc)
  166. {
  167. KillTimer(_hwnd, IDT_QUERYCANCEL); // in case any are in the queue
  168. _pqc = NULL; // to avoid possible problems
  169. }
  170. }
  171. return hr;
  172. }
  173. HRESULT CUserNotification::PlaySound(LPCWSTR pszSoundName)
  174. {
  175. SHPlaySound(pszSoundName);
  176. return S_OK;
  177. }
  178. // take down our notification icon
  179. void CUserNotification::_RemoveNotifyIcon()
  180. {
  181. NOTIFYICONDATA nid = { sizeof(nid), _hwnd, 0 };
  182. Shell_NotifyIcon(NIM_DELETE, &nid);
  183. }
  184. // the balloon related data (title, body test, dwInfoFlags, timeout
  185. BOOL CUserNotification::_SyncInfo()
  186. {
  187. BOOL bRet = FALSE;
  188. if (_hwnd)
  189. {
  190. NOTIFYICONDATA nid = { sizeof(nid), _hwnd, 0, NIF_INFO };
  191. if (_pszTitle)
  192. {
  193. StringCchCopy(nid.szInfoTitle, ARRAYSIZE(nid.szInfoTitle), _pszTitle); // ok to truncate
  194. }
  195. if (_pszText)
  196. {
  197. StringCchCopy(nid.szInfo, ARRAYSIZE(nid.szInfo), _pszText); // ok to truncate
  198. }
  199. nid.dwInfoFlags = _dwInfoFlags;
  200. nid.uTimeout = _dwShowTime;
  201. bRet = Shell_NotifyIcon(NIM_MODIFY, &nid);
  202. }
  203. return bRet;
  204. }
  205. BOOL CUserNotification::_SyncIcon()
  206. {
  207. BOOL bRet = FALSE;
  208. if (_hwnd)
  209. {
  210. NOTIFYICONDATA nid = { sizeof(nid), _hwnd, 0, NIF_ICON | NIF_TIP};
  211. nid.hIcon = _hicon ? _hicon : LoadIcon(NULL, IDI_WINLOGO);
  212. if (_pszToolTip)
  213. {
  214. StringCchCopy(nid.szTip, ARRAYSIZE(nid.szTip), _pszToolTip); // ok to truncate
  215. }
  216. bRet = Shell_NotifyIcon(NIM_MODIFY, &nid);
  217. }
  218. return bRet;
  219. }
  220. HRESULT CUserNotification::_GetWindow()
  221. {
  222. HRESULT hr = S_OK;
  223. if (NULL == _hwnd)
  224. {
  225. _hwnd = SHCreateWorkerWindow(s_WndProc, NULL, 0, 0, NULL, this);
  226. if (_hwnd)
  227. {
  228. NOTIFYICONDATA nid = { sizeof(nid), _hwnd, 0, NIF_MESSAGE, WM_NOTIFY_MESSAGE };
  229. if (Shell_NotifyIcon(NIM_ADD, &nid))
  230. {
  231. _SyncIcon();
  232. _SyncInfo();
  233. }
  234. else
  235. {
  236. DestroyWindow(_hwnd);
  237. _hwnd = NULL;
  238. hr = E_FAIL;
  239. }
  240. }
  241. }
  242. return hr;
  243. }
  244. void CUserNotification::_DelayDestroy(HRESULT hrDone)
  245. {
  246. _hrDone = hrDone;
  247. SetTimer(_hwnd, IDT_DESTROY, 250, NULL);
  248. }
  249. void CUserNotification::_Timeout()
  250. {
  251. if (_cRetryCount)
  252. {
  253. _cRetryCount--;
  254. SetTimer(_hwnd, IDT_REMINDER, _dwInterval, NULL);
  255. }
  256. else
  257. {
  258. // timeout, same HRESULT as user cancel
  259. _DelayDestroy(HRESULT_FROM_WIN32(ERROR_CANCELLED));
  260. }
  261. }
  262. LRESULT CALLBACK CUserNotification::_WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
  263. {
  264. LRESULT lres = 0;
  265. switch (uMsg)
  266. {
  267. case WM_NCDESTROY:
  268. SetWindowLongPtr(_hwnd, 0, NULL);
  269. _hwnd = NULL;
  270. break;
  271. case WM_TIMER:
  272. KillTimer(_hwnd, wParam); // make all timers single shot
  273. switch (wParam)
  274. {
  275. case IDT_REMINDER:
  276. _SyncInfo();
  277. break;
  278. case IDT_DESTROY:
  279. _RemoveNotifyIcon();
  280. PostQuitMessage(0); // exit our msg loop
  281. break;
  282. case IDT_QUERYCANCEL:
  283. if (_pqc && (S_OK == _pqc->QueryContinue()))
  284. SetTimer(_hwnd, IDT_QUERYCANCEL, _dwContinuePollInterval, NULL);
  285. else
  286. _DelayDestroy(S_FALSE); // callback cancelled
  287. break;
  288. case IDT_NOBALLOON:
  289. _Timeout();
  290. break;
  291. }
  292. break;
  293. case WM_NOTIFY_MESSAGE:
  294. switch (lParam)
  295. {
  296. case NIN_BALLOONSHOW:
  297. case NIN_BALLOONHIDE:
  298. break;
  299. case NIN_BALLOONTIMEOUT:
  300. _Timeout();
  301. break;
  302. case NIN_BALLOONUSERCLICK:
  303. case WM_LBUTTONDOWN:
  304. case WM_RBUTTONDOWN:
  305. _DelayDestroy(S_OK); // user click
  306. break;
  307. default:
  308. break;
  309. }
  310. break;
  311. default:
  312. lres = DefWindowProc(_hwnd, uMsg, wParam, lParam);
  313. break;
  314. }
  315. return lres;
  316. }
  317. LRESULT CALLBACK CUserNotification::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  318. {
  319. CUserNotification *pun = (CUserNotification *)GetWindowLongPtr(hwnd, 0);
  320. if (WM_CREATE == uMsg)
  321. {
  322. CREATESTRUCT *pcs = (CREATESTRUCT *)lParam;
  323. pun = (CUserNotification *)pcs->lpCreateParams;
  324. pun->_hwnd = hwnd;
  325. SetWindowLongPtr(hwnd, 0, (LONG_PTR)pun);
  326. }
  327. return pun ? pun->_WndProc(uMsg, wParam, lParam) : DefWindowProc(hwnd, uMsg, wParam, lParam);
  328. }
  329. STDAPI CUserNotification_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv)
  330. {
  331. HRESULT hr;
  332. CUserNotification* p = new CUserNotification();
  333. if (p)
  334. {
  335. hr = p->QueryInterface(riid, ppv);
  336. p->Release();
  337. }
  338. else
  339. {
  340. hr = E_OUTOFMEMORY;
  341. *ppv = NULL;
  342. }
  343. return hr;
  344. }