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.

1257 lines
34 KiB

  1. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. Microsoft Windows, Copyright (C) Microsoft Corporation, 2000
  3. File: Wlballoon.cpp
  4. Content: Implementation of the notification balloon class.
  5. History: 03-22-2001 dsie created
  6. ------------------------------------------------------------------------------*/
  7. #pragma warning (disable: 4100)
  8. #pragma warning (disable: 4706)
  9. ////////////////////
  10. //
  11. // Include
  12. //
  13. #include <nt.h>
  14. #include <ntrtl.h>
  15. #include <nturtl.h>
  16. #include <windows.h>
  17. #include <winwlx.h>
  18. #include <shobjidl.h>
  19. #include <shellapi.h>
  20. #include "debug.h"
  21. #include "wlballoon.rh"
  22. ////////////////////
  23. //
  24. // Defines
  25. //
  26. #define MAX_RESOURCE_STRING_SIZE 512
  27. #define IQUERY_CANCEL_INTERVAL (10 * 1000)
  28. #define BALLOON_SHOW_TIME (15 * 1000)
  29. #define BALLOON_SHOW_INTERVAL (2 * 60 * 1000)
  30. #define BALLOON_RESHOW_COUNT (0)
  31. #define LOGOFF_NOTIFICATION_EVENT_NAME L"Local\\WlballoonLogoffNotificationEventName"
  32. #define KERBEROS_NOTIFICATION_EVENT_NAME L"WlballoonKerberosNotificationEventName"
  33. ////////////////////
  34. //
  35. // Classes
  36. //
  37. class CBalloon : IQueryContinue
  38. {
  39. public:
  40. CBalloon(HANDLE hEvent);
  41. ~CBalloon();
  42. // IUnknown
  43. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  44. STDMETHODIMP_(ULONG) AddRef();
  45. STDMETHODIMP_(ULONG) Release();
  46. // IQueryContinue
  47. STDMETHODIMP QueryContinue(); // S_OK -> Continue, otherwise S_FALSE
  48. STDMETHODIMP ShowBalloon(HWND hWnd, HINSTANCE hInstance);
  49. private:
  50. LONG m_cRef;
  51. HANDLE m_hEvent;
  52. };
  53. class CNotify
  54. {
  55. public:
  56. CNotify();
  57. ~CNotify();
  58. DWORD RegisterNotification(LPWSTR pwszLogoffEventName, LPWSTR pwszKerberosEventName);
  59. DWORD UnregisterNotification();
  60. private:
  61. HANDLE m_hWait;
  62. HANDLE m_hThread;
  63. HANDLE m_hLogoffEvent;
  64. HANDLE m_hKerberosEvent;
  65. WCHAR m_wszKerberosEventName[MAX_PATH];
  66. static VOID CALLBACK RegisterWaitCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired);
  67. static DWORD WINAPI NotifyThreadProc(PVOID lpParameter);
  68. };
  69. ////////////////////
  70. //
  71. // Typedefs
  72. //
  73. typedef struct
  74. {
  75. HANDLE hWait;
  76. HANDLE hEvent;
  77. HMODULE hModule;
  78. CNotify * pNotify;
  79. } LOGOFFDATA, * PLOGOFFDATA;
  80. #if (0) //DSIE: Bug 407941
  81. //+----------------------------------------------------------------------------
  82. //
  83. // BalloonDialog
  84. //
  85. //-----------------------------------------------------------------------------
  86. INT_PTR CALLBACK BalloonDialog(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  87. {
  88. switch (uMsg)
  89. {
  90. case WM_INITDIALOG:
  91. {
  92. RECT rect;
  93. if (GetWindowRect(hwndDlg, &rect))
  94. {
  95. int wx = (GetSystemMetrics(SM_CXSCREEN) - (rect.right - rect.left)) / 2;
  96. int wy = (GetSystemMetrics(SM_CYSCREEN) - (rect.bottom - rect.top)) / 2;
  97. if (wx > 0 && wy > 0)
  98. {
  99. SetWindowPos(hwndDlg, NULL, wx, wy, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
  100. }
  101. }
  102. return TRUE;
  103. }
  104. case WM_CLOSE:
  105. {
  106. EndDialog(hwndDlg, IDOK);
  107. return TRUE;
  108. }
  109. case WM_COMMAND:
  110. {
  111. if (IDOK == LOWORD(wParam))
  112. {
  113. EndDialog(hwndDlg, IDOK);
  114. return TRUE;
  115. }
  116. break;
  117. }
  118. default:
  119. {
  120. break;
  121. }
  122. }
  123. return FALSE;
  124. }
  125. #endif
  126. //+----------------------------------------------------------------------------
  127. //
  128. // CBalloon
  129. //
  130. //-----------------------------------------------------------------------------
  131. CBalloon::CBalloon(HANDLE hEvent)
  132. {
  133. m_cRef = 1;
  134. m_hEvent = hEvent;
  135. }
  136. //+----------------------------------------------------------------------------
  137. //
  138. // ~CBalloon
  139. //
  140. //-----------------------------------------------------------------------------
  141. CBalloon::~CBalloon()
  142. {
  143. ASSERT(m_hEvent);
  144. CloseHandle(m_hEvent);
  145. return;
  146. }
  147. //+----------------------------------------------------------------------------
  148. //
  149. // QueryInterface
  150. //
  151. //-----------------------------------------------------------------------------
  152. HRESULT CBalloon::QueryInterface(REFIID riid, void **ppv)
  153. {
  154. HRESULT hr = S_OK;
  155. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IQueryContinue))
  156. {
  157. *ppv = static_cast<IQueryContinue *>(this);
  158. AddRef();
  159. }
  160. else
  161. {
  162. *ppv = NULL;
  163. hr = E_NOINTERFACE;
  164. }
  165. return hr;
  166. }
  167. //+----------------------------------------------------------------------------
  168. //
  169. // AddRef
  170. //
  171. //-----------------------------------------------------------------------------
  172. STDMETHODIMP_(ULONG) CBalloon::AddRef()
  173. {
  174. return InterlockedIncrement(&m_cRef);
  175. }
  176. //+----------------------------------------------------------------------------
  177. //
  178. // Release
  179. //
  180. //-----------------------------------------------------------------------------
  181. STDMETHODIMP_(ULONG) CBalloon::Release()
  182. {
  183. if (InterlockedDecrement(&m_cRef))
  184. {
  185. return m_cRef;
  186. }
  187. delete this;
  188. return 0;
  189. }
  190. //+----------------------------------------------------------------------------
  191. //
  192. // QueryContinue
  193. //
  194. //-----------------------------------------------------------------------------
  195. STDMETHODIMP CBalloon::QueryContinue()
  196. {
  197. ASSERT(m_hEvent);
  198. switch (WaitForSingleObject(m_hEvent, 0))
  199. {
  200. case WAIT_OBJECT_0:
  201. DebugTrace("Info: Kerberos event is still signaled, continue to show notification balloon.\n");
  202. return S_OK;
  203. case WAIT_TIMEOUT:
  204. DebugTrace("Info: Kerberos event has been reset, dismissing notification balloon.\n");
  205. return S_FALSE;
  206. case WAIT_FAILED:
  207. default:
  208. DebugTrace("Error [%#x]: WaitForSingleObject() failed, dismissing notification balloon.\n", GetLastError());
  209. break;
  210. }
  211. return E_FAIL;
  212. }
  213. //+----------------------------------------------------------------------------
  214. //
  215. // ShowBalloon
  216. //
  217. //-----------------------------------------------------------------------------
  218. STDMETHODIMP CBalloon::ShowBalloon(HWND hWnd, HINSTANCE hInstance)
  219. {
  220. HRESULT hr = S_OK;
  221. BOOL bCoInitialized = FALSE;
  222. HICON hIcon = NULL;
  223. WCHAR wszTitle[MAX_RESOURCE_STRING_SIZE] = L"";
  224. WCHAR wszText[MAX_RESOURCE_STRING_SIZE] = L"";
  225. IUserNotification * pIUserNotification = NULL;
  226. PrivateDebugTrace("Entering CBalloon::ShowBalloon.\n");
  227. ASSERT(m_hEvent);
  228. ASSERT(hInstance);
  229. if (FAILED(hr = CoInitialize(NULL)))
  230. {
  231. DebugTrace("Error [%#x]: CoInitialize() failed.\n", hr);
  232. goto ErrorReturn;
  233. }
  234. bCoInitialized = TRUE;
  235. if (FAILED(hr = CoCreateInstance(CLSID_UserNotification,
  236. NULL,
  237. CLSCTX_ALL,
  238. IID_IUserNotification,
  239. (void **) &pIUserNotification)))
  240. {
  241. DebugTrace("Error [%#x]: CoCreateInstance() failed.\n", hr);
  242. goto ErrorReturn;
  243. }
  244. if (FAILED(hr = pIUserNotification->SetBalloonRetry(BALLOON_SHOW_TIME,
  245. BALLOON_SHOW_INTERVAL,
  246. BALLOON_RESHOW_COUNT)))
  247. {
  248. DebugTrace("Error [%#x]: pIUserNotification->SetBalloonRetry() failed.\n", hr);
  249. goto ErrorReturn;
  250. }
  251. if (NULL == (hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_KERBEROS_TICKET))))
  252. {
  253. hr = HRESULT_FROM_WIN32(GetLastError());
  254. DebugTrace("Error [%#x]: LoadIcon() failed for IDI_KERBEROS_TICKET.\n", hr);
  255. goto ErrorReturn;
  256. }
  257. if (!LoadStringW(hInstance, IDS_BALLOON_TIP, wszText, MAX_RESOURCE_STRING_SIZE))
  258. {
  259. hr = HRESULT_FROM_WIN32(GetLastError());
  260. DebugTrace("Error [%#x]: LoadStringW() failed for IDS_BALLOON_TIP.\n", hr);
  261. goto ErrorReturn;
  262. }
  263. if (FAILED(hr = pIUserNotification->SetIconInfo(hIcon, wszText)))
  264. {
  265. DebugTrace("Error [%#x]: pIUserNotification->SetIconInfo() failed.\n", hr);
  266. goto ErrorReturn;
  267. }
  268. if (!LoadStringW(hInstance, IDS_BALLOON_TITLE, wszTitle, MAX_RESOURCE_STRING_SIZE))
  269. {
  270. hr = HRESULT_FROM_WIN32(GetLastError());
  271. DebugTrace("Error [%#x]: LoadStringW() failed for IDS_BALLOON_TITLE.\n", hr);
  272. goto ErrorReturn;
  273. }
  274. if (!LoadStringW(hInstance, IDS_BALLOON_TEXT, wszText, MAX_RESOURCE_STRING_SIZE))
  275. {
  276. hr = HRESULT_FROM_WIN32(GetLastError());
  277. DebugTrace("Error [%#x]: LoadStringW() failed for IDS_BALLOON_TEXT.\n", hr);
  278. goto ErrorReturn;
  279. }
  280. if (FAILED(hr = pIUserNotification->SetBalloonInfo(wszTitle, wszText, NIIF_ERROR)))
  281. {
  282. DebugTrace("Error [%#x]: pIUserNotification->SetBalloonInfo() failed.\n", hr);
  283. goto ErrorReturn;
  284. }
  285. if (FAILED(hr = pIUserNotification->Show(static_cast<IQueryContinue *>(this), IQUERY_CANCEL_INTERVAL)))
  286. {
  287. DebugTrace("Error [%#x]: pIUserNotification->Show() failed.\n", hr);
  288. goto ErrorReturn;
  289. }
  290. CommonReturn:
  291. if (hIcon)
  292. {
  293. DestroyIcon(hIcon);
  294. }
  295. if (pIUserNotification)
  296. {
  297. pIUserNotification->Release();
  298. }
  299. if (bCoInitialized)
  300. {
  301. CoUninitialize();
  302. }
  303. PrivateDebugTrace("Leaving CBalloon::ShowBalloon().\n");
  304. return hr;
  305. ErrorReturn:
  306. ASSERT(hr != S_OK);
  307. goto CommonReturn;
  308. }
  309. //+----------------------------------------------------------------------------
  310. //
  311. // Function : ShowNotificationBalloonW
  312. //
  313. // Synopsis : Display the notification balloon periodically until the specified
  314. // event is reset.
  315. //
  316. // Parameter: HWND hWnd
  317. // HINSTANCE hInstance
  318. // LPWSTR lpwszCommandLine - Event name
  319. // int nCmdShow
  320. //
  321. // Return : None.
  322. //
  323. // Remarks : This function is intended to be called through RunDll32 from
  324. // Winlogon. The reason we put these in wlnotify.dll is to save
  325. // distributing another EXE.
  326. //
  327. // Sample calling command line:
  328. //
  329. // RunDll32 wlnotify.dll,ShowNotificationBalloon EventName
  330. //
  331. //-----------------------------------------------------------------------------
  332. void CALLBACK ShowNotificationBalloonW(HWND hWnd,
  333. HINSTANCE hInstance,
  334. LPWSTR lpwszCommandLine,
  335. int nCmdShow)
  336. {
  337. HRESULT hr = S_OK;
  338. HANDLE hLogoffEvent = NULL;
  339. HANDLE hKerberosEvent = NULL;
  340. HMODULE hModule = NULL;
  341. CBalloon * pBalloon = NULL;
  342. PrivateDebugTrace("Entering ShowNotificationBalloonW().\n");
  343. if (NULL == (hModule = LoadLibraryW(L"wlnotify.dll")))
  344. {
  345. DebugTrace("Error [%#x]: LoadLibraryW() failed for wlnotify.dll.\n", GetLastError());
  346. goto ErrorReturn;
  347. }
  348. if (NULL == lpwszCommandLine)
  349. {
  350. DebugTrace("Error [%#x]: invalid argument, lpwszCommandLine is NULL.\n", E_INVALIDARG);
  351. goto ErrorReturn;
  352. }
  353. if (NULL == (hLogoffEvent = OpenEventW(SYNCHRONIZE, FALSE, LOGOFF_NOTIFICATION_EVENT_NAME)))
  354. {
  355. DebugTrace("Error [%#x]: OpenEventW() failed for event %S.\n", GetLastError(), LOGOFF_NOTIFICATION_EVENT_NAME);
  356. goto ErrorReturn;
  357. }
  358. if (NULL == (hKerberosEvent = OpenEventW(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, lpwszCommandLine)))
  359. {
  360. DebugTrace("Error [%#x]: OpenEventW() failed for event %S.\n", GetLastError(), lpwszCommandLine);
  361. goto ErrorReturn;
  362. }
  363. if (NULL == (pBalloon = new CBalloon(hKerberosEvent)))
  364. {
  365. DebugTrace("Error [%#x]: new CBalloon() failed.\n", E_OUTOFMEMORY);
  366. goto ErrorReturn;
  367. }
  368. if (WAIT_OBJECT_0 == WaitForSingleObject(hKerberosEvent, 0))
  369. {
  370. if (S_OK == (hr = pBalloon->ShowBalloon(NULL, hModule)))
  371. {
  372. WCHAR wszTitle[MAX_RESOURCE_STRING_SIZE] = L"";
  373. WCHAR wszText[MAX_RESOURCE_STRING_SIZE] = L"";
  374. DebugTrace("Info: User clicked on icon.\n");
  375. #if (0) //DSIE: Bug 407941
  376. DialogBoxParamW(hModule,
  377. (LPCWSTR) MAKEINTRESOURCEW(IDD_BALLOON_DIALOG),
  378. hWnd,
  379. BalloonDialog,
  380. (LPARAM) hModule);
  381. #else
  382. if (!LoadStringW(hModule, IDS_BALLOON_DIALOG_TITLE, wszTitle, sizeof(wszTitle) / sizeof(wszTitle[0])))
  383. {
  384. hr = HRESULT_FROM_WIN32(GetLastError());
  385. DebugTrace("Error [%#x]: LoadStringW() failed for IDS_BALLOON_DIALOG_TITLE.\n", hr);
  386. goto CommonReturn; // hKerberosEvent will be closed in CBalloon destructor
  387. }
  388. if (!LoadStringW(hModule,
  389. GetSystemMetrics(SM_REMOTESESSION) ? IDS_BALLOON_DIALOG_TS_TEXT : IDS_BALLOON_DIALOG_TEXT,
  390. wszText, sizeof(wszText) / sizeof(wszText[0])))
  391. {
  392. hr = HRESULT_FROM_WIN32(GetLastError());
  393. DebugTrace("Error [%#x]: LoadStringW() failed for IDS_BALLOON_DIALOG_TEXT.\n", hr);
  394. goto CommonReturn; // hKerberosEvent will be closed in CBalloon destructor
  395. }
  396. MessageBoxW(hWnd, wszText, wszTitle, MB_OK | MB_ICONERROR);
  397. #endif
  398. }
  399. else if (S_FALSE == hr)
  400. {
  401. DebugTrace("Info: IQueryContinue cancelled the notification.\n");
  402. }
  403. else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
  404. {
  405. DebugTrace("Info: Balloon icon timed out.\n");
  406. }
  407. else
  408. {
  409. SetEvent(hLogoffEvent);
  410. DebugTrace("Error [%#x]: pBalloon->ShowBalloon() failed.\n", hr);
  411. }
  412. ResetEvent(hKerberosEvent);
  413. }
  414. CommonReturn:
  415. if (hLogoffEvent)
  416. {
  417. CloseHandle(hLogoffEvent);
  418. }
  419. if (pBalloon)
  420. {
  421. pBalloon->Release();
  422. }
  423. if (hModule)
  424. {
  425. FreeLibrary(hModule);
  426. }
  427. PrivateDebugTrace("Leaving ShowNotificationBalloonW().\n");
  428. return;
  429. ErrorReturn:
  430. if (hKerberosEvent)
  431. {
  432. CloseHandle(hKerberosEvent);
  433. }
  434. goto CommonReturn;
  435. }
  436. //+----------------------------------------------------------------------------
  437. //
  438. // CNotify
  439. //
  440. //-----------------------------------------------------------------------------
  441. CNotify::CNotify()
  442. {
  443. m_hWait = NULL;
  444. m_hThread = NULL;
  445. m_hLogoffEvent = NULL;
  446. m_hKerberosEvent = NULL;
  447. m_wszKerberosEventName[0] = L'\0';
  448. }
  449. //+----------------------------------------------------------------------------
  450. //
  451. // ~CNotify
  452. //
  453. //-----------------------------------------------------------------------------
  454. CNotify::~CNotify()
  455. {
  456. if (m_hWait)
  457. {
  458. UnregisterWait(m_hWait);
  459. }
  460. if (m_hThread)
  461. {
  462. CloseHandle(m_hThread);
  463. }
  464. if (m_hLogoffEvent)
  465. {
  466. CloseHandle(m_hLogoffEvent);
  467. }
  468. if (m_hKerberosEvent)
  469. {
  470. CloseHandle(m_hKerberosEvent);
  471. }
  472. return;
  473. }
  474. //+----------------------------------------------------------------------------
  475. //
  476. // RegisterNotification
  477. //
  478. // To register and wait for the Kerberos notification event. When the event is
  479. // signaled, a ticket icon and balloon will appear in the systray to warn the
  480. // user about the problem, and suggest them to lock and then unlock the machine
  481. // with their new password.
  482. //
  483. //-----------------------------------------------------------------------------
  484. DWORD CNotify::RegisterNotification(LPWSTR pwszLogoffEventName, LPWSTR pwszKerberosEventName)
  485. {
  486. DWORD dwRetCode = 0;
  487. PrivateDebugTrace("Entering CNotify::RegisterNotification().\n");
  488. if (NULL == pwszLogoffEventName ||
  489. NULL == pwszKerberosEventName ||
  490. MAX_PATH < lstrlenW(pwszKerberosEventName))
  491. {
  492. dwRetCode = (DWORD) E_INVALIDARG;
  493. DebugTrace("Error [%#x]: invalid argument.\n", dwRetCode);
  494. goto ErrorReturn;
  495. }
  496. if (NULL == (m_hLogoffEvent = OpenEventW(SYNCHRONIZE, FALSE, pwszLogoffEventName)))
  497. {
  498. dwRetCode = GetLastError();
  499. DebugTrace("Error [%#x]: OpenEventW() failed for event %S.\n", dwRetCode, pwszLogoffEventName);
  500. goto ErrorReturn;
  501. }
  502. if (NULL == (m_hKerberosEvent = CreateEventW(NULL, TRUE, FALSE, pwszKerberosEventName)))
  503. {
  504. dwRetCode = GetLastError();
  505. DebugTrace("Error [%#x]: CreateEventW() failed for event %S.\n", dwRetCode, pwszKerberosEventName);
  506. goto ErrorReturn;
  507. }
  508. if (ERROR_ALREADY_EXISTS == GetLastError())
  509. {
  510. dwRetCode = ERROR_SINGLE_INSTANCE_APP;
  511. DebugTrace("Error [%#x]: cannot run more than one instance of this code per unique event.\n", dwRetCode);
  512. goto ErrorReturn;
  513. }
  514. lstrcpyW(&m_wszKerberosEventName[0], pwszKerberosEventName);
  515. if (!RegisterWaitForSingleObject(&m_hWait,
  516. m_hKerberosEvent,
  517. CNotify::RegisterWaitCallback,
  518. (PVOID) this,
  519. INFINITE,
  520. WT_EXECUTEONLYONCE))
  521. {
  522. dwRetCode = (DWORD) E_UNEXPECTED;
  523. DebugTrace("Unexpected error: RegisterWaitForSingleObject() failed.\n");
  524. goto ErrorReturn;
  525. }
  526. CommonReturn:
  527. PrivateDebugTrace("Leaving CNotify::RegisterNotification().\n");
  528. return dwRetCode;
  529. ErrorReturn:
  530. ASSERT(0 != dwRetCode);
  531. if (m_hWait)
  532. {
  533. UnregisterWait(m_hWait);
  534. m_hWait = NULL;
  535. }
  536. if (m_hLogoffEvent)
  537. {
  538. CloseHandle(m_hLogoffEvent);
  539. m_hLogoffEvent = NULL;
  540. }
  541. if (m_hKerberosEvent)
  542. {
  543. CloseHandle(m_hKerberosEvent);
  544. m_hKerberosEvent = NULL;
  545. }
  546. goto CommonReturn;
  547. }
  548. //+----------------------------------------------------------------------------
  549. //
  550. // UnregisterNotification
  551. //
  552. // Unregister the Kerberos notification wait event registered by
  553. // RegisterNotification().
  554. //
  555. //-----------------------------------------------------------------------------
  556. DWORD CNotify::UnregisterNotification()
  557. {
  558. DWORD dwRetCode = 0;
  559. PrivateDebugTrace("Entering CNotify::UnregisterNotification().\n");
  560. ResetEvent(m_hKerberosEvent);
  561. if (m_hThread)
  562. {
  563. WaitForSingleObject(m_hThread, INFINITE);
  564. }
  565. PrivateDebugTrace("Leaving CNotify::UnregisterNotification().\n");
  566. return dwRetCode;
  567. }
  568. //+----------------------------------------------------------------------------
  569. //
  570. // RegisterWaitCallback
  571. //
  572. //-----------------------------------------------------------------------------
  573. VOID CALLBACK CNotify::RegisterWaitCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
  574. {
  575. DWORD dwThreadId = 0;
  576. CNotify * pNotify = NULL;
  577. PrivateDebugTrace("Entering CNotify::RegisterWaitCallback().\n");
  578. (void) TimerOrWaitFired;
  579. ASSERT(lpParameter);
  580. pNotify = (CNotify *) lpParameter;
  581. if (NULL == (pNotify->m_hThread = CreateThread(NULL,
  582. 0,
  583. (LPTHREAD_START_ROUTINE) CNotify::NotifyThreadProc,
  584. lpParameter,
  585. 0,
  586. &dwThreadId)))
  587. {
  588. ResetEvent(pNotify->m_hKerberosEvent);
  589. DebugTrace("Error [%#x]: CreateThread() for NotifyThreadProc failed.\n", GetLastError());
  590. }
  591. PrivateDebugTrace("Leaving CNotify::RegisterWaitCallback().\n");
  592. return;
  593. }
  594. //+----------------------------------------------------------------------------
  595. //
  596. // NotifyThreadProc
  597. //
  598. //-----------------------------------------------------------------------------
  599. DWORD WINAPI CNotify::NotifyThreadProc(PVOID lpParameter)
  600. {
  601. DWORD dwRetCode = 0;
  602. WCHAR wszDllPath[MAX_PATH] = L"";
  603. WCHAR wszCommandLine[MAX_PATH * 2] = L"RunDll32.exe ";
  604. CNotify * pNotify = NULL;
  605. STARTUPINFOW si;
  606. PROCESS_INFORMATION pi;
  607. PrivateDebugTrace("Entering CNotify::NotifyThreadProc().\n");
  608. ASSERT(lpParameter);
  609. pNotify = (CNotify *) lpParameter;
  610. ASSERT(pNotify->m_hWait);
  611. ASSERT(pNotify->m_hLogoffEvent);
  612. ASSERT(pNotify->m_hKerberosEvent);
  613. ASSERT(lstrlenW(pNotify->m_wszKerberosEventName));
  614. ExpandEnvironmentStringsW(L"%SystemRoot%\\system32\\wlnotify.dll", wszDllPath, MAX_PATH);
  615. lstrcatW(wszCommandLine, wszDllPath);
  616. lstrcatW(wszCommandLine, L",ShowNotificationBalloon ");
  617. lstrcatW(wszCommandLine, pNotify->m_wszKerberosEventName);
  618. ZeroMemory(&pi, sizeof(pi));
  619. ZeroMemory(&si, sizeof(si));
  620. si.cb = sizeof(si);
  621. si.lpDesktop = L"WinSta0\\Default";
  622. if (!CreateProcessW(NULL,
  623. wszCommandLine,
  624. NULL,
  625. NULL,
  626. FALSE,
  627. 0,
  628. NULL,
  629. NULL,
  630. &si,
  631. &pi))
  632. {
  633. dwRetCode = GetLastError();
  634. DebugTrace( "Error [%#x]: CreateProcessW() failed.\n", dwRetCode);
  635. goto ErrorReturn;
  636. }
  637. WaitForSingleObject(pi.hProcess, INFINITE);
  638. CloseHandle(pi.hThread);
  639. CloseHandle(pi.hProcess);
  640. switch (WaitForSingleObject(pNotify->m_hLogoffEvent, 0))
  641. {
  642. case WAIT_OBJECT_0:
  643. {
  644. DebugTrace("Info: Logoff event is signaled, so skip register wait for callback.\n");
  645. break;
  646. }
  647. case WAIT_TIMEOUT:
  648. {
  649. HANDLE hThread = pNotify->m_hThread;
  650. pNotify->m_hThread = NULL;
  651. CloseHandle(hThread);
  652. if (!RegisterWaitForSingleObject(&pNotify->m_hWait,
  653. pNotify->m_hKerberosEvent,
  654. CNotify::RegisterWaitCallback,
  655. lpParameter,
  656. INFINITE,
  657. WT_EXECUTEONLYONCE))
  658. {
  659. dwRetCode = (DWORD) E_UNEXPECTED;
  660. DebugTrace("Error [%#x]: RegisterWaitForSingleObject() failed.\n", dwRetCode);
  661. goto ErrorReturn;
  662. }
  663. DebugTrace("Info: Logoff event is not signaled, so continue to register wait for callback.\n");
  664. break;
  665. }
  666. default:
  667. {
  668. dwRetCode = GetLastError();
  669. DebugTrace("Error [%#x]: WaitForSingleObject() failed.\n", dwRetCode);
  670. goto ErrorReturn;
  671. }
  672. }
  673. CommonReturn:
  674. PrivateDebugTrace("Leaving CNotify::NotifyThreadProc().\n");
  675. return dwRetCode;
  676. ErrorReturn:
  677. ASSERT(0 != dwRetCode);
  678. goto CommonReturn;
  679. }
  680. //+----------------------------------------------------------------------------
  681. //
  682. // CreateNotificationEventName
  683. //
  684. //-----------------------------------------------------------------------------
  685. LPWSTR CreateNotificationEventName(LPWSTR pwszSuffixName)
  686. {
  687. DWORD dwRetCode = 0;
  688. DWORD cb = 0;
  689. TOKEN_STATISTICS stats;
  690. HANDLE hThreadToken = NULL;
  691. LPWSTR pwszEventName = NULL;
  692. WCHAR wszPrefixName[] = L"Global\\";
  693. WCHAR wszLuid[20] = L"";
  694. WCHAR wszSeparator[] = L"_";
  695. PrivateDebugTrace("Entering CreateNotificationEventName().\n");
  696. if (NULL == pwszSuffixName)
  697. {
  698. dwRetCode = ERROR_INVALID_PARAMETER;
  699. goto CLEANUP;
  700. }
  701. if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken))
  702. {
  703. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThreadToken))
  704. {
  705. dwRetCode = GetLastError();
  706. DebugTrace("Error [%#x]: OpenProcessToken() failed.\n", dwRetCode);
  707. goto CLEANUP;
  708. }
  709. }
  710. if (!GetTokenInformation(hThreadToken, TokenStatistics, &stats, sizeof(stats), &cb))
  711. {
  712. dwRetCode = GetLastError();
  713. DebugTrace("Error [%#x]: GetTokenInformation() failed.\n", dwRetCode);
  714. goto CLEANUP;
  715. }
  716. wsprintfW(wszLuid, L"%08x%08x", stats.AuthenticationId.HighPart, stats.AuthenticationId.LowPart);
  717. if (NULL == (pwszEventName = (LPWSTR) malloc((lstrlenW(wszPrefixName) +
  718. lstrlenW(wszLuid) +
  719. lstrlenW(wszSeparator) +
  720. lstrlenW(pwszSuffixName) + 1) * sizeof(WCHAR))))
  721. {
  722. dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
  723. DebugTrace("Error: out of memory.\n");
  724. goto CLEANUP;
  725. }
  726. lstrcpyW(pwszEventName, wszPrefixName);
  727. lstrcatW(pwszEventName, wszLuid);
  728. lstrcatW(pwszEventName, wszSeparator);
  729. lstrcatW(pwszEventName, pwszSuffixName);
  730. CLEANUP:
  731. if (NULL != hThreadToken)
  732. {
  733. CloseHandle(hThreadToken);
  734. }
  735. PrivateDebugTrace("Leaving CreateNotificationEventName().\n");
  736. SetLastError(dwRetCode);
  737. return pwszEventName;
  738. }
  739. //+----------------------------------------------------------------------------
  740. //
  741. // LogoffThreadProc
  742. //
  743. //-----------------------------------------------------------------------------
  744. DWORD WINAPI LogoffThreadProc(PVOID lpParameter)
  745. {
  746. HMODULE hModule = NULL;
  747. PLOGOFFDATA pLogoffData;
  748. PrivateDebugTrace("Entering LogoffThreadProc().\n");
  749. ASSERT(lpParameter);
  750. if (pLogoffData = (PLOGOFFDATA) lpParameter)
  751. {
  752. if (pLogoffData->hWait)
  753. {
  754. UnregisterWait(pLogoffData->hWait);
  755. }
  756. if (pLogoffData->pNotify)
  757. {
  758. pLogoffData->pNotify->UnregisterNotification();
  759. delete pLogoffData->pNotify;
  760. }
  761. if (pLogoffData->hEvent)
  762. {
  763. CloseHandle(pLogoffData->hEvent);
  764. }
  765. if (pLogoffData->hModule)
  766. {
  767. hModule = pLogoffData->hModule;
  768. }
  769. LocalFree(pLogoffData);
  770. }
  771. PrivateDebugTrace("Leaving LogoffThreadProc().\n");
  772. if (hModule)
  773. {
  774. FreeLibraryAndExitThread(hModule, 0);
  775. }
  776. return 0;
  777. }
  778. //+----------------------------------------------------------------------------
  779. //
  780. // LogoffWaitCallback
  781. //
  782. //-----------------------------------------------------------------------------
  783. VOID CALLBACK LogoffWaitCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
  784. {
  785. PLOGOFFDATA pLogoffData;
  786. PrivateDebugTrace("Entering LogoffWaitCallback().\n");
  787. (void) TimerOrWaitFired;
  788. ASSERT(lpParameter);
  789. if (pLogoffData = (PLOGOFFDATA) lpParameter)
  790. {
  791. DWORD dwThreadId = 0;
  792. HANDLE hThread = NULL;
  793. if (hThread = CreateThread(NULL,
  794. 0,
  795. (LPTHREAD_START_ROUTINE) LogoffThreadProc,
  796. lpParameter,
  797. 0,
  798. &dwThreadId))
  799. {
  800. CloseHandle(hThread);
  801. }
  802. else
  803. {
  804. DebugTrace("Error [%#x]: CreateThread() for LogoffThreadProc failed.\n", GetLastError());
  805. }
  806. }
  807. PrivateDebugTrace("Leaving LogoffWaitCallback().\n");
  808. return;
  809. }
  810. //+----------------------------------------------------------------------------
  811. //
  812. // Public
  813. //
  814. //-----------------------------------------------------------------------------
  815. //+----------------------------------------------------------------------------
  816. //
  817. // Function : RegisterTicketExpiredNotificationEvent
  818. //
  819. // Synopsis : To register and wait for the Kerberos notification event. When the
  820. // event is signaled, a ticket icon and balloon will appear in the
  821. // systray to warn the user about the problem, and suggest them to
  822. // lock and then unlock the machine with their new password.
  823. //
  824. // Parameter: PWLX_NOTIFICATION_INFO pNotificationInfo
  825. //
  826. // Return : If the function succeeds, zero is returned.
  827. //
  828. // If the function fails, a non-zero error code is returned.
  829. //
  830. // Remarks : This function should only be called by Winlogon LOGON
  831. // notification mechanism with the Asynchronous and Impersonate
  832. // flags set to 1.
  833. //
  834. // Also for each RegisterKerberosNotificationEvent() call, a
  835. // pairing call by Winlogon LOGOFF notification mechanism to
  836. // UnregisterKerberosNotificationEvent() must be made at the
  837. // end of each logon session.
  838. //
  839. //-----------------------------------------------------------------------------
  840. DWORD WINAPI RegisterTicketExpiredNotificationEvent(PWLX_NOTIFICATION_INFO pNotificationInfo)
  841. {
  842. DWORD dwRetCode = 0;
  843. LPWSTR pwszLogoffEventName = LOGOFF_NOTIFICATION_EVENT_NAME;
  844. LPWSTR pwszKerberosEventName = NULL;
  845. PLOGOFFDATA pLogoffData = NULL;
  846. PrivateDebugTrace("Entering RegisterTicketExpiredNotificationEvent().\n");
  847. if (NULL == (pLogoffData = (PLOGOFFDATA) LocalAlloc(LPTR, sizeof(LOGOFFDATA))))
  848. {
  849. dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
  850. DebugTrace("Error [%#x]: out of memory.\n", dwRetCode);
  851. goto ErrorReturn;
  852. }
  853. ZeroMemory(pLogoffData, sizeof(LOGOFFDATA));
  854. if (NULL == (pLogoffData->hEvent = CreateEventW(NULL, TRUE, FALSE, pwszLogoffEventName)))
  855. {
  856. dwRetCode = GetLastError();
  857. DebugTrace("Error [%#x]: CreateEventW() failed for event %S.\n", dwRetCode, pwszLogoffEventName);
  858. goto ErrorReturn;
  859. }
  860. if (ERROR_ALREADY_EXISTS == GetLastError())
  861. {
  862. dwRetCode = ERROR_SINGLE_INSTANCE_APP;
  863. DebugTrace("Error [%#x]: cannot run more than one instance of this code per session.\n", dwRetCode);
  864. goto ErrorReturn;
  865. }
  866. DebugTrace("Info: Logoff event name = %S.\n", pwszLogoffEventName);
  867. if (NULL == (pwszKerberosEventName = CreateNotificationEventName(KERBEROS_NOTIFICATION_EVENT_NAME)))
  868. {
  869. dwRetCode = GetLastError();
  870. DebugTrace("Error [%#x]: CreateNotificationEventName() failed.\n", dwRetCode);
  871. goto ErrorReturn;
  872. }
  873. if (ERROR_ALREADY_EXISTS == GetLastError())
  874. {
  875. dwRetCode = ERROR_INTERNAL_ERROR;
  876. DebugTrace("Internal error [%#x]: Kerberos event already exists.\n", dwRetCode);
  877. goto ErrorReturn;
  878. }
  879. DebugTrace("Info: Kerberos event name = %S.\n", pwszKerberosEventName);
  880. if (NULL == (pLogoffData->hModule = LoadLibraryW(L"wlnotify.dll")))
  881. {
  882. dwRetCode = GetLastError();
  883. DebugTrace("Error [%#x]: LoadLibraryW() failed.\n", dwRetCode);
  884. goto ErrorReturn;
  885. }
  886. if (NULL == (pLogoffData->pNotify = new CNotify()))
  887. {
  888. dwRetCode = (DWORD) E_OUTOFMEMORY;
  889. DebugTrace("Error [%#x]: new CNotify() failed.\n", dwRetCode);
  890. goto ErrorReturn;
  891. }
  892. if (0 != (dwRetCode = pLogoffData->pNotify->RegisterNotification(pwszLogoffEventName,
  893. pwszKerberosEventName)))
  894. {
  895. goto ErrorReturn;
  896. }
  897. if (!RegisterWaitForSingleObject(&pLogoffData->hWait,
  898. pLogoffData->hEvent,
  899. LogoffWaitCallback,
  900. (PVOID) pLogoffData,
  901. INFINITE,
  902. WT_EXECUTEONLYONCE))
  903. {
  904. dwRetCode = (DWORD) E_UNEXPECTED;
  905. DebugTrace("Unexpected error: RegisterWaitForSingleObject() failed for LogoffWaitCallback().\n");
  906. goto ErrorReturn;
  907. }
  908. CommonReturn:
  909. if (pwszKerberosEventName)
  910. {
  911. free(pwszKerberosEventName);
  912. }
  913. PrivateDebugTrace("Leaving RegisterTicketExpiredNotificationEvent().\n");
  914. return dwRetCode;;
  915. ErrorReturn:
  916. ASSERT(0 != dwRetCode);
  917. if (pLogoffData)
  918. {
  919. if (pLogoffData->hWait)
  920. {
  921. UnregisterWait(pLogoffData->hWait);
  922. }
  923. if (pLogoffData->pNotify)
  924. {
  925. pLogoffData->pNotify->UnregisterNotification();
  926. delete pLogoffData->pNotify;
  927. }
  928. if (pLogoffData->hEvent)
  929. {
  930. CloseHandle(pLogoffData->hEvent);
  931. }
  932. if (pLogoffData->hModule)
  933. {
  934. FreeLibrary(pLogoffData->hModule);
  935. }
  936. LocalFree(pLogoffData);
  937. }
  938. goto CommonReturn;
  939. }
  940. //+----------------------------------------------------------------------------
  941. //
  942. // Function : UnregisterTicketExpiredNotificationEvent
  943. //
  944. // Synopsis : To unregister the Kerberos notification wait event registered by
  945. // RegisterKerberosNotificationEvent().
  946. //
  947. // Parameter: PWLX_NOTIFICATION_INFO pNotificationInfo
  948. //
  949. // Return : If the function succeeds, zero is returned.
  950. //
  951. // If the function fails, a non-zero error code is returned.
  952. //
  953. // Remarks : This function should only be called by Winlogon LOGON
  954. // notification mechanism with the Asynchronous and Impersonate
  955. // flags set to 1.
  956. //
  957. //-----------------------------------------------------------------------------
  958. DWORD WINAPI UnregisterTicketExpiredNotificationEvent(PWLX_NOTIFICATION_INFO pNotificationInfo)
  959. {
  960. DWORD dwRetCode = 0;
  961. HANDLE hLogoffEvent = NULL;
  962. PrivateDebugTrace("Entering UnregisterTicketExpiredNotificationEvent().\n");
  963. if (NULL == (hLogoffEvent = OpenEventW(EVENT_MODIFY_STATE, FALSE, LOGOFF_NOTIFICATION_EVENT_NAME)))
  964. {
  965. dwRetCode = GetLastError();
  966. DebugTrace("Error [%#x]: OpenEventW() failed for event %S.\n", dwRetCode, LOGOFF_NOTIFICATION_EVENT_NAME);
  967. goto ErrorReturn;
  968. }
  969. if (!SetEvent(hLogoffEvent))
  970. {
  971. dwRetCode = GetLastError();
  972. DebugTrace("Error [%#x]: SetEvent() failed for event %S.\n", dwRetCode, LOGOFF_NOTIFICATION_EVENT_NAME);
  973. goto ErrorReturn;
  974. }
  975. CommonReturn:
  976. if (hLogoffEvent)
  977. {
  978. CloseHandle(hLogoffEvent);
  979. }
  980. PrivateDebugTrace("Leaving UnregisterTicketExpiredNotificationEvent().\n");
  981. return dwRetCode;
  982. ErrorReturn:
  983. ASSERT(0 != dwRetCode);
  984. goto CommonReturn;
  985. }