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.

1701 lines
50 KiB

  1. #include "precomp.h"
  2. #pragma hdrstop
  3. #define SECURITY_WIN32
  4. #ifdef NTSDDEBUG
  5. #define NTSDDBGPRINT(x) DbgPrint x
  6. #else
  7. #define NTSDDBGPRINT(x)
  8. #endif
  9. #include "winsvcp.h" // defines I_ScSendTSMessage
  10. #include "conntfy.h"
  11. BOOL IsBitSet(DWORD dwMask, WPARAM notifybit)
  12. {
  13. // why are you asking for bit 0?
  14. ASSERT(notifybit != 0);
  15. ASSERT(notifybit <= WTS_MAX_SESSION_NOTIFICATION);
  16. return (CREATE_MASK(notifybit)) & dwMask;
  17. }
  18. //#ifdef MAKARANDS_HIGHER_WARNING_LEVEL
  19. #pragma warning(push, 4)
  20. #pragma warning(disable:4201) // nameless structure.
  21. //#endif
  22. #define INVALID_SESSIONSERIAL 0xffffffff
  23. // 0x1 fConnected
  24. // 0x2 fLoggedOn
  25. // 0x3 fRemote
  26. // 0x4 fWelcome
  27. typedef struct _WTSSESSION_STATE
  28. {
  29. unsigned int bConnected: 1;
  30. unsigned int bLoggedOn: 1;
  31. unsigned int bConsole: 1;
  32. unsigned int bRemote: 1;
  33. unsigned int bLocked: 1;
  34. } WTSSESSION_STATE, *PWTSSESSION_STATE;
  35. /*
  36. WTS_CONSOLE_CONNECT bConnected, bConsole, !bRemote,
  37. WTS_CONSOLE_DISCONNECT !bConnected, !bConsole, !bRemote
  38. WTS_REMOTE_CONNECT bConnected, !bConsole, bremote
  39. WTS_REMOTE_DISCONNECT !bConnected, !bConsole, !bRemote
  40. WTS_SESSION_LOGON bLoggedOn
  41. WTS_SESSION_LOGOFF !bLoggedOn
  42. WTS_SESSION_LOCK bLocked
  43. WTS_SESSION_UNLOCK !bLocked
  44. */
  45. //
  46. // this is head for hwnds list.
  47. // this links NOTIFY_ENTRY or NOTIFY_ENTRY_GLOBAL together.
  48. //
  49. typedef struct _NOTIFY_LIST
  50. {
  51. LIST_ENTRY Links; // links to other NOTIFY_LISTs. not used in case of global notification list.
  52. LIST_ENTRY ListHead; // head of notification entries. links NOTIFY_ENTRYs (or NOTIFY_ENTRY_GLOBAL) together
  53. RTL_CRITICAL_SECTION ListLock; // lock to travel the entries.
  54. ULONG SessionId; // session id ( not used in case of global list)
  55. ULONG SessonSerialNumber; // serial number ( not used in case of global list)
  56. WTSSESSION_STATE SessionState; // state of the session.
  57. } NOTIFY_LIST, *PNOTIFY_LIST;
  58. //
  59. // entry in notification list per winstation.
  60. //
  61. typedef struct _NOTIFY_ENTRY
  62. {
  63. LIST_ENTRY Links; // links to other entries
  64. ULONG_PTR hWnd; // window or event handle.
  65. ULONG RefCount; // how many times was this hwnd registered ?
  66. DWORD dwMask; // mask tell us the event to be notified for.
  67. DWORD dwFlags; // flags.
  68. } NOTIFY_ENTRY, *PNOTIFY_ENTRY;
  69. //
  70. // Entry in Notification list for all sessions Notifications.
  71. //
  72. typedef struct _NOTIFY_ENTRY_GLOBAL
  73. {
  74. struct _NOTIFY_ENTRY; // above structure +
  75. ULONG SessionId; // since this is global entry, it needs to keep session id per hwnd.
  76. } NOTIFY_ENTRY_GLOBAL, *PNOTIFY_ENTRY_GLOBAL;
  77. //
  78. // The notification Queue.
  79. //
  80. typedef struct _NOTIFICATION_QUEUE
  81. {
  82. LIST_ENTRY ListHead; // head of queue reuests. links NOTIFICATION_REQUESTs together
  83. RTL_CRITICAL_SECTION ListLock; // lock to travel the queue
  84. HANDLE hNotificationEvent; // syncronization between woker and caller of queue.
  85. } NOTIFICATION_QUEUE, *PNOTIFICATION_QUEUE;
  86. //
  87. // Entry in Notification Queue.
  88. //
  89. typedef struct _NOTIFICATION_REQUEST
  90. {
  91. LIST_ENTRY Links; // links to other entries.
  92. ULONG SessionId; // session id for the session this notificaiton is to be sent.
  93. ULONG SessonSerialNumber; // serial number for the session this notificaiton is to be sent.
  94. WPARAM NotificationCode; // notificaiton code
  95. } NOTIFICATION_REQUEST, *PNOTIFICATION_REQUEST;
  96. //
  97. // our main data structure.
  98. //
  99. typedef struct _NOTIFY_LLIST
  100. {
  101. LIST_ENTRY ListHead; // head of notification lists. links NOTIFY_LISTs together.
  102. RTL_CRITICAL_SECTION ListLock; // lock to travel the head list.
  103. NOTIFY_LIST GlobalList; // global notification list.
  104. NOTIFICATION_QUEUE RequestQueue; // notification queue.
  105. NOTIFY_LIST InvlidHwndList; // invalid window list
  106. } NOTIFY_LLIST, PNOTIFY_LLIST;
  107. //
  108. // File Globals.
  109. //
  110. NOTIFY_LLIST gNotifyLList;
  111. //
  112. // private functions
  113. //
  114. BOOL DoesHWndExists (
  115. PNOTIFY_LIST pNotifyList,
  116. ULONG_PTR hWnd
  117. );
  118. PNOTIFY_ENTRY GetHWndEntryFromSessionList (
  119. PNOTIFY_LIST pNotifyList,
  120. ULONG_PTR hWnd,
  121. DWORD dwFlags
  122. );
  123. PNOTIFY_ENTRY_GLOBAL GetHWndEntryFromGlobalList (
  124. PNOTIFY_LIST pNotifyList,
  125. ULONG_PTR hWnd,
  126. ULONG SessionId,
  127. DWORD dwFlags
  128. );
  129. NTSTATUS GetNoficationListFromSessionId (
  130. ULONG SessionId,
  131. PNOTIFY_LIST *ppNofificationList,
  132. BOOL bKeepLListLocked
  133. );
  134. NTSTATUS GetGlobalNotificationList (
  135. PNOTIFY_LIST *ppConChgNtfy
  136. );
  137. NTSTATUS GetInvlidHwndList(PNOTIFY_LIST *ppConChgNtfy);
  138. NTSTATUS NotifyConsole (
  139. ULONG SessionId,
  140. ULONG SessionSerialNumber,
  141. WPARAM wParam
  142. );
  143. NTSTATUS SendConsoleNotification (
  144. ULONG SessionId,
  145. ULONG_PTR hWnd,
  146. ULONG Msg,
  147. WPARAM wParam,
  148. WTSSESSION_NOTIFICATION wtsConsoleNotification
  149. );
  150. BOOL IsGlobalList(PNOTIFY_LIST pNtfyList);
  151. int GetListCount (
  152. LIST_ENTRY *pListHead
  153. );
  154. NTSTATUS DestroyLock ( PNOTIFY_LIST pNtfyList);
  155. NTSTATUS CreateLock ( PNOTIFY_LIST pNtfyList);
  156. NTSTATUS
  157. InitializeNotificationQueue ();
  158. NTSTATUS
  159. QueueNotificationRequest (
  160. ULONG SessionSerialNumber,
  161. ULONG SessionId,
  162. WPARAM notification
  163. );
  164. PNOTIFICATION_REQUEST
  165. UnQueueNotificationRequest ();
  166. DWORD NotificationQueueWorker (
  167. LPVOID
  168. );
  169. NTSTATUS RemoveGlobalNotification (ULONG SessionId);
  170. NTSTATUS RemoveInvalidWindowsFromLists ();
  171. NTSTATUS RemoveBadEvents(ULONG SessionId);
  172. NTSTATUS UnRegisterConsoleNotificationInternal (ULONG_PTR hWnd, ULONG SessionId, BOOL bDcrRef, DWORD dwFlags);
  173. void ReleaseNotificationList (PNOTIFY_LIST pNotifyList);
  174. void UpdateSessionState(PNOTIFY_LIST pNotifyList, WPARAM wNotification)
  175. {
  176. /*
  177. WTS_CONSOLE_CONNECT bConnected, bConsole, !bRemote,
  178. WTS_CONSOLE_DISCONNECT !bConnected, !bConsole, !bRemote
  179. WTS_REMOTE_CONNECT bConnected, !bConsole, bremote
  180. WTS_REMOTE_DISCONNECT !bConnected, !bConsole, !bRemote
  181. WTS_SESSION_LOGON bLoggedOn
  182. WTS_SESSION_LOGOFF !bLoggedOn
  183. WTS_SESSION_LOCK bLocked
  184. WTS_SESSION_UNLOCK !bLocked
  185. */
  186. ASSERT(!IsGlobalList(pNotifyList));
  187. ASSERT(!pNotifyList->SessionState.bConsole || !pNotifyList->SessionState.bRemote);
  188. ASSERT(!pNotifyList->SessionState.bConnected || pNotifyList->SessionState.bConsole || pNotifyList->SessionState.bRemote);
  189. switch (wNotification)
  190. {
  191. case WTS_CONSOLE_CONNECT:
  192. ASSERT(!pNotifyList->SessionState.bConsole);
  193. ASSERT(!pNotifyList->SessionState.bRemote);
  194. pNotifyList->SessionState.bConnected = 1;
  195. pNotifyList->SessionState.bConsole = 1;
  196. break;
  197. case WTS_CONSOLE_DISCONNECT:
  198. ASSERT(pNotifyList->SessionState.bConsole);
  199. ASSERT(pNotifyList->SessionState.bConnected);
  200. ASSERT(!pNotifyList->SessionState.bRemote);
  201. pNotifyList->SessionState.bConnected = 0;
  202. pNotifyList->SessionState.bConsole = 0;
  203. break;
  204. case WTS_REMOTE_DISCONNECT:
  205. ASSERT(pNotifyList->SessionState.bRemote);
  206. ASSERT(pNotifyList->SessionState.bConnected);
  207. ASSERT(!pNotifyList->SessionState.bConsole);
  208. pNotifyList->SessionState.bConnected = 0;
  209. pNotifyList->SessionState.bRemote = 0;
  210. break;
  211. case WTS_REMOTE_CONNECT:
  212. ASSERT(!pNotifyList->SessionState.bRemote);
  213. ASSERT(!pNotifyList->SessionState.bConnected);
  214. ASSERT(!pNotifyList->SessionState.bConsole);
  215. pNotifyList->SessionState.bConnected = 1;
  216. pNotifyList->SessionState.bRemote = 1;
  217. break;
  218. case WTS_SESSION_LOGON:
  219. ASSERT(pNotifyList->SessionState.bLoggedOn == 0);
  220. pNotifyList->SessionState.bLoggedOn = 1;
  221. break;
  222. case WTS_SESSION_LOGOFF:
  223. ASSERT(pNotifyList->SessionState.bLoggedOn == 1);
  224. pNotifyList->SessionState.bLoggedOn = 0;
  225. break;
  226. case WTS_SESSION_LOCK:
  227. ASSERT(pNotifyList->SessionState.bLocked == 0);
  228. pNotifyList->SessionState.bLocked = 1;
  229. break;
  230. case WTS_SESSION_UNLOCK:
  231. ASSERT(pNotifyList->SessionState.bLocked == 1);
  232. pNotifyList->SessionState.bLocked = 0;
  233. break;
  234. case WTS_SESSION_REMOTE_CONTROL:
  235. NOTHING;
  236. break;
  237. default:
  238. ASSERT(FALSE);
  239. }
  240. ASSERT(!pNotifyList->SessionState.bConsole || !pNotifyList->SessionState.bRemote);
  241. ASSERT(!pNotifyList->SessionState.bConnected || pNotifyList->SessionState.bConsole || pNotifyList->SessionState.bRemote);
  242. }
  243. //
  244. // Global initialization.
  245. //
  246. NTSTATUS InitializeConsoleNotification ()
  247. {
  248. NTSTATUS Status;
  249. InitializeListHead( &gNotifyLList.ListHead );
  250. Status = RtlInitializeCriticalSection( &gNotifyLList.ListLock );
  251. if ( !NT_SUCCESS( Status ) )
  252. {
  253. return (Status);
  254. }
  255. //
  256. // following members are unused in for global list.
  257. //
  258. gNotifyLList.GlobalList.Links.Blink = NULL;
  259. gNotifyLList.GlobalList.Links.Flink = NULL;
  260. gNotifyLList.GlobalList.SessionId = INVALID_SESSIONID;
  261. gNotifyLList.GlobalList.SessonSerialNumber = INVALID_SESSIONSERIAL;
  262. InitializeListHead( &gNotifyLList.GlobalList.ListHead);
  263. Status = RtlInitializeCriticalSection( &gNotifyLList.GlobalList.ListLock );
  264. if ( !NT_SUCCESS( Status ) )
  265. {
  266. RtlDeleteCriticalSection( &gNotifyLList.ListLock );
  267. return (Status);
  268. }
  269. gNotifyLList.InvlidHwndList.Links.Blink = NULL;
  270. gNotifyLList.InvlidHwndList.Links.Flink = NULL;
  271. gNotifyLList.InvlidHwndList.SessionId = INVALID_SESSIONID;
  272. gNotifyLList.InvlidHwndList.SessonSerialNumber = INVALID_SESSIONSERIAL;
  273. InitializeListHead(&gNotifyLList.InvlidHwndList.ListHead) ;
  274. Status = RtlInitializeCriticalSection( &gNotifyLList.InvlidHwndList.ListLock );
  275. if ( !NT_SUCCESS( Status ) )
  276. {
  277. RtlDeleteCriticalSection( &gNotifyLList.ListLock );
  278. RtlDeleteCriticalSection( &gNotifyLList.GlobalList.ListLock );
  279. return (Status);
  280. }
  281. Status = InitializeNotificationQueue ();
  282. if ( !NT_SUCCESS( Status ) )
  283. {
  284. RtlDeleteCriticalSection( &gNotifyLList.ListLock );
  285. RtlDeleteCriticalSection( &gNotifyLList.GlobalList.ListLock );
  286. RtlDeleteCriticalSection( &gNotifyLList.InvlidHwndList.ListLock );
  287. }
  288. return (Status);
  289. }
  290. //
  291. // per winstation initialization.
  292. //
  293. NTSTATUS InitializeSessionNotification (PWINSTATION pWinStation)
  294. {
  295. NTSTATUS Status;
  296. PNOTIFY_LIST pNewNotifyList;
  297. ASSERT(pWinStation);
  298. if (pWinStation->Terminating)
  299. {
  300. // dont create notification list if this winstation is already terminating.
  301. // its possible that a winstation is being terminated before getting completely created,
  302. // in such case we might end up calling RemoveSessionNotification before InitializeSessionNotification.
  303. // so essentially leaving this session never to deleted. (Bug #414330)
  304. return STATUS_SUCCESS;
  305. }
  306. #ifdef DBG
  307. // BUGBUG - is it possible that a old session with the same session id is still there?
  308. Status = GetNoficationListFromSessionId(pWinStation->LogonId, &pNewNotifyList, FALSE);
  309. //
  310. // we are just being asked to initialize notification
  311. // we must not find list for this session in our LList.
  312. //
  313. ASSERT( STATUS_NO_SUCH_LOGON_SESSION == Status );
  314. #endif
  315. //
  316. // create a new hwnd list for this session
  317. //
  318. pNewNotifyList = MemAlloc(sizeof(NOTIFY_LIST));
  319. if (!pNewNotifyList)
  320. {
  321. return STATUS_NO_MEMORY;
  322. }
  323. pNewNotifyList->SessionId = pWinStation->LogonId;
  324. pNewNotifyList->SessonSerialNumber = pWinStation->SessionSerialNumber;
  325. //
  326. // initialize session state.
  327. //
  328. {
  329. pNewNotifyList->SessionState.bConnected = 0;
  330. pNewNotifyList->SessionState.bConsole = 0;
  331. pNewNotifyList->SessionState.bLoggedOn = 0;
  332. pNewNotifyList->SessionState.bRemote = 0;
  333. pNewNotifyList->SessionState.bLocked = 0; // bugbug we dont know the real welcome state ;(
  334. }
  335. InitializeListHead( &pNewNotifyList->ListHead);
  336. Status = RtlInitializeCriticalSection( &pNewNotifyList->ListLock );
  337. if ( !NT_SUCCESS( Status ) )
  338. {
  339. MemFree(pNewNotifyList);
  340. pNewNotifyList = NULL;
  341. return Status;
  342. }
  343. // now link this new list into our main list of lists.
  344. ENTERCRIT(&gNotifyLList.ListLock);
  345. InsertTailList( &gNotifyLList.ListHead, &pNewNotifyList->Links);
  346. LEAVECRIT(&gNotifyLList.ListLock);
  347. return STATUS_SUCCESS;
  348. }
  349. //
  350. // must be called when a session ends.
  351. //
  352. NTSTATUS RemoveSessionNotification(ULONG SessionId, ULONG SessionSerialNumber)
  353. {
  354. NTSTATUS Status;
  355. PNOTIFY_LIST pListTobeRemoved;
  356. UNREFERENCED_PARAMETER(SessionSerialNumber); // it's referenced only for Chk builds.
  357. // BUGBUG - is it possible that a new session with the same session id was created while we are here ?
  358. Status = GetNoficationListFromSessionId( SessionId, &pListTobeRemoved, TRUE);
  359. if (!NT_SUCCESS( Status ))
  360. {
  361. //
  362. // we are being asked to remove session notification
  363. // but its possible that we dont have session notification list created for this session.
  364. // This can happen if the session is being terminate during session creation process.
  365. //
  366. ASSERT( !pListTobeRemoved );
  367. return Status;
  368. }
  369. ASSERT( pListTobeRemoved );
  370. ASSERT( SessionSerialNumber == pListTobeRemoved->SessonSerialNumber );
  371. RemoveEntryList( &pListTobeRemoved->Links );
  372. LEAVECRIT(&gNotifyLList.ListLock);
  373. //
  374. // walk throught this list and free all the nodes.
  375. //
  376. while (!IsListEmpty(&pListTobeRemoved->ListHead))
  377. {
  378. PNOTIFY_ENTRY pEntry;
  379. PLIST_ENTRY Next;
  380. Next = pListTobeRemoved->ListHead.Flink;
  381. ASSERT(Next);
  382. pEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY, Links );
  383. ASSERT(pEntry);
  384. RemoveEntryList( &pEntry->Links );
  385. if (pEntry->dwFlags & WTS_EVENT_NOTIFICATION)
  386. {
  387. CloseHandle((HANDLE)pEntry->hWnd);
  388. }
  389. MemFree(pEntry);
  390. pEntry = NULL;
  391. }
  392. // we are no more going to use this list lock.
  393. RtlDeleteCriticalSection( &pListTobeRemoved->ListLock );
  394. MemFree(pListTobeRemoved);
  395. pListTobeRemoved = NULL;
  396. return RemoveGlobalNotification (SessionId);
  397. // return QueueNotificationRequest(pWinStation->SessionSerialNumber, pWinStation->LogonId, 0);
  398. }
  399. NTSTATUS RemoveGlobalNotification (ULONG SessionId)
  400. {
  401. PLIST_ENTRY Head, Next;
  402. PNOTIFY_LIST pListTobeRemoved = NULL;
  403. NTSTATUS Status = GetGlobalNotificationList(&pListTobeRemoved);
  404. if ( !NT_SUCCESS( Status ) )
  405. {
  406. return (Status);
  407. }
  408. ASSERT(pListTobeRemoved);
  409. Head = &pListTobeRemoved->ListHead;
  410. Next = Head->Flink;
  411. while (Head != Next)
  412. {
  413. PNOTIFY_ENTRY_GLOBAL pEntryGlobal = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
  414. Next = Next->Flink;
  415. ASSERT(pEntryGlobal);
  416. if (pEntryGlobal->SessionId == SessionId)
  417. {
  418. RemoveEntryList( &pEntryGlobal->Links );
  419. if (pEntryGlobal->dwFlags & WTS_EVENT_NOTIFICATION)
  420. {
  421. CloseHandle((HANDLE)pEntryGlobal->hWnd);
  422. }
  423. MemFree(pEntryGlobal);
  424. pEntryGlobal = NULL;
  425. }
  426. }
  427. ReleaseNotificationList( pListTobeRemoved );
  428. pListTobeRemoved = NULL;
  429. // now lets remove the invalid Windows associated with this session.
  430. // from the list if there is any.
  431. pListTobeRemoved = NULL;
  432. Status = GetInvlidHwndList(&pListTobeRemoved);
  433. if ( !NT_SUCCESS( Status ) )
  434. {
  435. return (Status);
  436. }
  437. ASSERT(pListTobeRemoved);
  438. Head = &pListTobeRemoved->ListHead;
  439. Next = Head->Flink;
  440. while (Head != Next)
  441. {
  442. PNOTIFY_ENTRY_GLOBAL pEntryGlobal = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
  443. Next = Next->Flink;
  444. ASSERT(pEntryGlobal);
  445. if (pEntryGlobal->SessionId == SessionId)
  446. {
  447. RemoveEntryList( &pEntryGlobal->Links );
  448. MemFree(pEntryGlobal);
  449. pEntryGlobal = NULL;
  450. }
  451. }
  452. ReleaseNotificationList(pListTobeRemoved);
  453. return STATUS_SUCCESS;
  454. }
  455. //NTSTATUS RegisterNotificationEvent(HANDLE hEvent, DWORD dwMaskFlags, BOOL bThisSessionOnly)
  456. //{
  457. //}
  458. NTSTATUS RegisterConsoleNotification ( ULONG_PTR hWnd, ULONG SessionId, DWORD dwFlags, DWORD dwMask)
  459. {
  460. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  461. PNOTIFY_LIST pNotifyList = NULL;
  462. PNOTIFY_LIST pNotifyListGlobal = NULL;
  463. PNOTIFY_ENTRY pEntry = NULL;
  464. PNOTIFY_ENTRY_GLOBAL pEntryGlobal = NULL;
  465. // WTS_EVENT_NOTIFICATION & WTS_WINDOW_NOTIFICATION are mutually exclusive.
  466. ASSERT(!(dwFlags & WTS_EVENT_NOTIFICATION && dwFlags & WTS_WINDOW_NOTIFICATION));
  467. if (dwFlags & WTS_EVENT_NOTIFICATION)
  468. {
  469. //
  470. // lets clean up our event list before we register this new event.
  471. //
  472. RemoveBadEvents(SessionId);
  473. }
  474. /*
  475. if (dwFlags != NOTIFY_FOR_THIS_SESSION && dwFlags != NOTIFY_FOR_ALL_SESSIONS)
  476. {
  477. //
  478. // invalid flag value
  479. //
  480. return STATUS_INVALID_PARAMETER_3;
  481. }
  482. */
  483. //
  484. // get the global session notificaiton list
  485. //
  486. Status = GetGlobalNotificationList(&pNotifyListGlobal);
  487. if (!NT_SUCCESS(Status))
  488. {
  489. return Status;
  490. }
  491. //
  492. // get the session specific list for this window
  493. //
  494. Status = GetNoficationListFromSessionId( SessionId, &pNotifyList, FALSE);
  495. if (!NT_SUCCESS(Status))
  496. {
  497. ReleaseNotificationList (pNotifyListGlobal);
  498. return Status;
  499. }
  500. ASSERT( pNotifyList );
  501. ASSERT( pNotifyListGlobal );
  502. pEntry = GetHWndEntryFromSessionList(pNotifyList, hWnd, dwFlags & (WTS_EVENT_NOTIFICATION | WTS_WINDOW_NOTIFICATION));
  503. pEntryGlobal = GetHWndEntryFromGlobalList(pNotifyListGlobal, hWnd, SessionId, dwFlags & (WTS_EVENT_NOTIFICATION | WTS_WINDOW_NOTIFICATION));
  504. //
  505. // entry must not exist in both the lists.
  506. //
  507. ASSERT(!(pEntry && pEntryGlobal));
  508. // in case of event notifications, return here if entry already exists.
  509. if ((pEntry || pEntryGlobal) && (dwFlags & WTS_EVENT_NOTIFICATION))
  510. {
  511. ReleaseNotificationList( pNotifyListGlobal );
  512. ReleaseNotificationList( pNotifyList );
  513. return STATUS_INVALID_PARAMETER_1; // BUGBUG : get better status;
  514. }
  515. if (pEntry)
  516. {
  517. //
  518. // Let other list go
  519. //
  520. ReleaseNotificationList( pNotifyListGlobal );
  521. ASSERT( pEntry );
  522. ASSERT( pEntry->RefCount > 0 );
  523. //
  524. // entry already exists, just increment its reference count.
  525. //
  526. pEntry->RefCount++;
  527. ReleaseNotificationList( pNotifyList );
  528. }
  529. else if (pEntryGlobal)
  530. {
  531. ReleaseNotificationList (pNotifyList);
  532. ASSERT( pEntryGlobal );
  533. ASSERT( pEntryGlobal->RefCount > 0 );
  534. //
  535. // entry already exists, just increment its reference count.
  536. //
  537. pEntryGlobal->RefCount++;
  538. ReleaseNotificationList( pNotifyListGlobal );
  539. }
  540. else
  541. {
  542. //
  543. // the entry does not exists in either of the lists.
  544. // so we need to create a new entry
  545. //
  546. if (dwFlags & NOTIFY_FOR_ALL_SESSIONS)
  547. {
  548. ReleaseNotificationList (pNotifyList);
  549. pEntryGlobal = MemAlloc( sizeof(NOTIFY_ENTRY_GLOBAL) );
  550. if (pEntryGlobal == NULL )
  551. {
  552. Status = STATUS_NO_MEMORY;
  553. }
  554. else
  555. {
  556. pEntryGlobal->hWnd = hWnd;
  557. pEntryGlobal->SessionId = SessionId;
  558. pEntryGlobal->RefCount = 1;
  559. pEntryGlobal->dwMask = dwMask;
  560. pEntryGlobal->dwFlags = dwFlags;
  561. InsertTailList( &(pNotifyListGlobal->ListHead), &(pEntryGlobal->Links) );
  562. }
  563. ReleaseNotificationList( pNotifyListGlobal );
  564. }
  565. else
  566. {
  567. ReleaseNotificationList( pNotifyListGlobal );
  568. pEntry = MemAlloc( sizeof(NOTIFY_ENTRY) );
  569. if (pEntry == NULL )
  570. {
  571. Status = STATUS_NO_MEMORY;
  572. }
  573. else
  574. {
  575. pEntry->hWnd = hWnd;
  576. pEntry->RefCount = 1;
  577. pEntry->dwMask = dwMask;
  578. pEntry->dwFlags = dwFlags;
  579. InsertTailList( &(pNotifyList->ListHead), &(pEntry->Links) );
  580. }
  581. ReleaseNotificationList (pNotifyList);
  582. }
  583. }
  584. return (Status);
  585. }
  586. NTSTATUS UnRegisterConsoleNotification (ULONG_PTR hWnd, ULONG SessionId, DWORD dwFlags)
  587. {
  588. return UnRegisterConsoleNotificationInternal (hWnd, SessionId, TRUE, dwFlags);
  589. }
  590. NTSTATUS UnRegisterConsoleNotificationInternal (ULONG_PTR hWnd, ULONG SessionId, BOOL bDcrRef, DWORD dwFlags)
  591. {
  592. NTSTATUS Status;
  593. PNOTIFY_LIST pNotifyList;
  594. PNOTIFY_ENTRY pEntry;
  595. //
  596. // get the notification list for the Session
  597. //
  598. Status = GetNoficationListFromSessionId( SessionId, &pNotifyList, FALSE);
  599. if (NT_SUCCESS(Status))
  600. {
  601. ASSERT(pNotifyList);
  602. pEntry = GetHWndEntryFromSessionList(pNotifyList,hWnd, dwFlags);
  603. if (pEntry)
  604. {
  605. ASSERT( pEntry->RefCount > 0 );
  606. ASSERT( !(pEntry->dwFlags & WTS_EVENT_NOTIFICATION) || pEntry->RefCount == 1);
  607. // decrement ref count
  608. pEntry->RefCount--;
  609. if (pEntry->RefCount == 0 || !bDcrRef)
  610. {
  611. RemoveEntryList( &pEntry->Links );
  612. if (pEntry->dwFlags & WTS_EVENT_NOTIFICATION)
  613. {
  614. CloseHandle((HANDLE)pEntry->hWnd);
  615. }
  616. MemFree(pEntry);
  617. pEntry = NULL;
  618. }
  619. ReleaseNotificationList (pNotifyList);
  620. }
  621. else
  622. {
  623. PNOTIFY_LIST pNotifyListGlobal = NULL;
  624. PNOTIFY_ENTRY_GLOBAL pEntryGlobal = NULL;
  625. ReleaseNotificationList (pNotifyList);
  626. //
  627. // now check the global session notificaiton entry
  628. //
  629. Status = GetGlobalNotificationList(&pNotifyListGlobal);
  630. if (NT_SUCCESS(Status))
  631. {
  632. pEntryGlobal = GetHWndEntryFromGlobalList(pNotifyListGlobal, hWnd, SessionId, dwFlags);
  633. if (pEntryGlobal)
  634. {
  635. ASSERT(pEntryGlobal->RefCount > 0);
  636. ASSERT( !(pEntryGlobal->dwFlags & WTS_EVENT_NOTIFICATION) || pEntryGlobal->RefCount == 1);
  637. pEntryGlobal->RefCount--;
  638. if (pEntryGlobal->RefCount == 0 || !bDcrRef)
  639. {
  640. RemoveEntryList( &pEntryGlobal->Links );
  641. if (pEntryGlobal->dwFlags & WTS_EVENT_NOTIFICATION)
  642. {
  643. CloseHandle((HANDLE)pEntryGlobal->hWnd);
  644. }
  645. MemFree(pEntryGlobal);
  646. pEntryGlobal = NULL;
  647. }
  648. }
  649. else
  650. {
  651. Status = STATUS_NOT_FOUND;
  652. }
  653. ReleaseNotificationList( pNotifyListGlobal );
  654. }
  655. }
  656. }
  657. return (Status);
  658. }
  659. NTSTATUS NotifySessionChange (PWINSTATION pWinStation, WPARAM wNotification)
  660. {
  661. return QueueNotificationRequest(pWinStation->SessionSerialNumber, pWinStation->LogonId, wNotification);
  662. }
  663. NTSTATUS NotifyLogon(PWINSTATION pWinStation)
  664. {
  665. return NotifySessionChange(pWinStation, WTS_SESSION_LOGON);
  666. }
  667. NTSTATUS NotifyLogoff(PWINSTATION pWinStation)
  668. {
  669. return NotifySessionChange(pWinStation, WTS_SESSION_LOGOFF);
  670. }
  671. NTSTATUS NotifyConnect (PWINSTATION pWinStation, BOOL bConsole)
  672. {
  673. return NotifySessionChange(pWinStation, bConsole ? WTS_CONSOLE_CONNECT : WTS_REMOTE_CONNECT);
  674. }
  675. NTSTATUS NotifyDisconnect (PWINSTATION pWinStation, BOOL bConsole)
  676. {
  677. return NotifySessionChange(pWinStation, bConsole ? WTS_CONSOLE_DISCONNECT : WTS_REMOTE_DISCONNECT);
  678. }
  679. NTSTATUS NofifyWelcomeOn (PWINSTATION pWinStation)
  680. {
  681. return NotifySessionChange(pWinStation, WTS_SESSION_LOCK);
  682. }
  683. NTSTATUS NotifyWelcomeOff (PWINSTATION pWinStation)
  684. {
  685. return NotifySessionChange(pWinStation, WTS_SESSION_UNLOCK);
  686. }
  687. NTSTATUS NotifyShadowChange (PWINSTATION pWinStation, BOOL bIsHelpAssistant)
  688. {
  689. UNREFERENCED_PARAMETER(bIsHelpAssistant); // for a new event later?
  690. return NotifySessionChange(pWinStation, WTS_SESSION_REMOTE_CONTROL);
  691. }
  692. NTSTATUS SendNotificationToHwnd(PWINSTATION pWinstation, ULONG_PTR hWnd, ULONG SessionId, WPARAM wParam)
  693. {
  694. WINSTATION_APIMSG WMsg;
  695. //
  696. // now pupulate the WMSG for delievery.
  697. //
  698. WMsg.u.sMsg.Msg = WM_WTSSESSION_CHANGE;
  699. WMsg.u.sMsg.wParam = wParam;
  700. WMsg.ApiNumber = SMWinStationSendWindowMessage ;
  701. WMsg.WaitForReply = FALSE;
  702. WMsg.u.sMsg.dataBuffer = NULL;
  703. WMsg.u.sMsg.bufferSize = 0;
  704. WMsg.u.sMsg.lParam = SessionId;
  705. WMsg.u.sMsg.hWnd = (HWND) hWnd ;
  706. return SendWinStationCommand( pWinstation, &WMsg, 0);
  707. }
  708. NTSTATUS NotifyConsole (ULONG SessionId, ULONG SessionSerialNumber, WPARAM wParam)
  709. {
  710. NTSTATUS Status = STATUS_SUCCESS;
  711. DWORD dwError;
  712. PWINSTATION pWinStation=NULL;
  713. Status = RemoveInvalidWindowsFromLists();
  714. ASSERT(NT_SUCCESS(Status));
  715. pWinStation = FindWinStationById(SessionId, FALSE);
  716. //
  717. // if we find the session we were looking for
  718. // note: we must check for the serialnumber, as the session id is not unique.
  719. //
  720. if (pWinStation)
  721. {
  722. if (SessionSerialNumber == pWinStation->SessionSerialNumber)
  723. {
  724. PNOTIFY_LIST pConsoleList;
  725. Status = GetNoficationListFromSessionId(pWinStation->LogonId, &pConsoleList, FALSE);
  726. if (NT_SUCCESS(Status) && pConsoleList)
  727. {
  728. PLIST_ENTRY Head, Next;
  729. Head = &pConsoleList->ListHead;
  730. for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
  731. {
  732. PNOTIFY_ENTRY pEntry;
  733. pEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY, Links );
  734. ASSERT(pEntry);
  735. ASSERT(!(pEntry->dwFlags & WTS_ALL_SESSION_NOTIFICATION));
  736. if (IsBitSet(pEntry->dwMask, wParam))
  737. {
  738. if (pEntry->dwFlags & WTS_EVENT_NOTIFICATION)
  739. {
  740. SetEvent((HANDLE)pEntry->hWnd);
  741. }
  742. else
  743. {
  744. Status = SendNotificationToHwnd(pWinStation, pEntry->hWnd, SessionId, wParam);
  745. if (!NT_SUCCESS(Status))
  746. {
  747. NTSDDBGPRINT(("conntfy.c - SendWinStationCommand failed, Status = %d.\n", Status));
  748. }
  749. }
  750. }
  751. }
  752. UpdateSessionState(pConsoleList, wParam);
  753. ReleaseNotificationList( pConsoleList );
  754. }
  755. }
  756. ReleaseWinStation( pWinStation );
  757. }
  758. //
  759. // now send notifications to windows registered for all session notificaitons.
  760. //
  761. {
  762. PNOTIFY_LIST pNotifyListGlobal = NULL;
  763. Status = GetGlobalNotificationList(&pNotifyListGlobal);
  764. if (NT_SUCCESS(Status))
  765. {
  766. PLIST_ENTRY Head, Next;
  767. Head = &pNotifyListGlobal->ListHead;
  768. for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
  769. {
  770. PNOTIFY_ENTRY_GLOBAL pEntryGlobal = NULL;
  771. pEntryGlobal = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
  772. ASSERT(pEntryGlobal);
  773. ASSERT(pEntryGlobal->dwFlags & WTS_ALL_SESSION_NOTIFICATION);
  774. if (IsBitSet(pEntryGlobal->dwMask, wParam))
  775. {
  776. if (pEntryGlobal->dwFlags & WTS_EVENT_NOTIFICATION)
  777. {
  778. SetEvent((HANDLE)pEntryGlobal->hWnd);
  779. }
  780. else
  781. {
  782. pWinStation = FindWinStationById(pEntryGlobal->SessionId, FALSE);
  783. if (pWinStation)
  784. {
  785. if (!pWinStation->Terminating)
  786. {
  787. Status = SendNotificationToHwnd(pWinStation, pEntryGlobal->hWnd, SessionId, wParam);
  788. if (!NT_SUCCESS(Status))
  789. {
  790. NTSDDBGPRINT(("conntfy.c - SendWinStationCommand failed, Status = %d.\n", Status));
  791. }
  792. }
  793. ReleaseWinStation( pWinStation );
  794. }
  795. }
  796. }
  797. }
  798. ReleaseNotificationList(pNotifyListGlobal);
  799. }
  800. else
  801. {
  802. NTSDDBGPRINT(("conntfy.c - Failed to get all session notification list - status = 0x%x.\n", Status));
  803. }
  804. }
  805. //
  806. // now lets notify SCM which will notify all the services registered for SERVICE_ACCEPT_SESSIONCHANGE
  807. //
  808. //
  809. // logon logoff notifications for session 0 are sent by winlogon. rest are handled here.
  810. //
  811. if (SessionId != 0 || ( wParam != WTS_SESSION_LOGON && wParam != WTS_SESSION_LOGOFF))
  812. {
  813. WTSSESSION_NOTIFICATION wtsConsoleNotification;
  814. wtsConsoleNotification.cbSize = sizeof(WTSSESSION_NOTIFICATION);
  815. wtsConsoleNotification.dwSessionId = SessionId;
  816. dwError = I_ScSendTSMessage(
  817. SERVICE_CONTROL_SESSIONCHANGE, // op code
  818. (DWORD)wParam, // event code,
  819. wtsConsoleNotification.cbSize, // data size
  820. (LPBYTE)&wtsConsoleNotification // data.
  821. );
  822. }
  823. return Status;
  824. }
  825. NTSTATUS DestroyLock( PNOTIFY_LIST pNtfyList)
  826. {
  827. return RtlDeleteCriticalSection( &pNtfyList->ListLock );
  828. }
  829. NTSTATUS CreateLock (PNOTIFY_LIST pNtfyList)
  830. {
  831. return RtlInitializeCriticalSection( &pNtfyList->ListLock );
  832. }
  833. BOOL IsInvalidHWndList (PNOTIFY_LIST pNtfyList)
  834. {
  835. return (pNtfyList == &gNotifyLList.InvlidHwndList);
  836. }
  837. BOOL IsGlobalList(PNOTIFY_LIST pNtfyList)
  838. {
  839. return (pNtfyList == &gNotifyLList.GlobalList);
  840. }
  841. int GetListCount (LIST_ENTRY *pListHead)
  842. {
  843. PLIST_ENTRY Head, Next;
  844. int iCount = 0;
  845. ASSERT(pListHead);
  846. Head = pListHead;
  847. for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
  848. {
  849. iCount++;
  850. }
  851. return iCount;
  852. }
  853. PNOTIFY_ENTRY GetHWndEntryFromSessionList(PNOTIFY_LIST pNotifyList, ULONG_PTR hWnd, DWORD dwFlags)
  854. {
  855. PLIST_ENTRY Head = NULL;
  856. PLIST_ENTRY Next = NULL;
  857. PNOTIFY_ENTRY pEntry = NULL;
  858. Head = &pNotifyList->ListHead;
  859. for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
  860. {
  861. pEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY, Links );
  862. ASSERT(pEntry);
  863. ASSERT(!(pEntry->dwFlags & WTS_EVENT_NOTIFICATION && pEntry->dwFlags & WTS_WINDOW_NOTIFICATION));
  864. if (pEntry->hWnd == hWnd && pEntry->dwFlags & dwFlags)
  865. {
  866. return pEntry;
  867. }
  868. }
  869. return NULL;
  870. }
  871. PNOTIFY_ENTRY_GLOBAL GetHWndEntryFromGlobalList(PNOTIFY_LIST pNotifyList, ULONG_PTR hWnd, ULONG SessionId, DWORD dwFlags)
  872. {
  873. PLIST_ENTRY Head = NULL;
  874. PLIST_ENTRY Next = NULL;
  875. PNOTIFY_ENTRY_GLOBAL pEntry = NULL;
  876. Head = &pNotifyList->ListHead;
  877. for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
  878. {
  879. pEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
  880. ASSERT(pEntry);
  881. ASSERT(!(pEntry->dwFlags & WTS_EVENT_NOTIFICATION && pEntry->dwFlags & WTS_WINDOW_NOTIFICATION));
  882. if (pEntry->hWnd == hWnd && SessionId == pEntry->SessionId && pEntry->dwFlags & dwFlags)
  883. {
  884. return pEntry;
  885. }
  886. }
  887. return NULL;
  888. }
  889. //
  890. // returns PNOTIFY_LIST list for the given session.
  891. //
  892. NTSTATUS GetNoficationListFromSessionId (ULONG SessionId, PNOTIFY_LIST *ppNotifyList, BOOL bKeepLListLocked)
  893. {
  894. PLIST_ENTRY Next, Head;
  895. ASSERT(ppNotifyList);
  896. *ppNotifyList = NULL;
  897. // lock our list of lists.
  898. ENTERCRIT(&gNotifyLList.ListLock);
  899. Head = &gNotifyLList.ListHead;
  900. Next = Head->Flink;
  901. while (Head != Next)
  902. {
  903. PNOTIFY_LIST pNotifyList = CONTAINING_RECORD( Next, NOTIFY_LIST, Links );
  904. ASSERT( pNotifyList );
  905. //
  906. // we always take gNotifyLList.ListLock first and then the Listlock
  907. // therefore we must never have PNOTIFY_LIST.ListLock at this time.
  908. //
  909. ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != pNotifyList->ListLock.OwningThread );
  910. if (pNotifyList->SessionId == SessionId)
  911. {
  912. //
  913. // did we find more that 1 matching notify list ???, should never happen!
  914. //
  915. ASSERT(*ppNotifyList == NULL);
  916. //
  917. // ok we found the session list we were looking for
  918. //
  919. *ppNotifyList = pNotifyList;
  920. #ifndef DBG
  921. break;
  922. #endif
  923. }
  924. Next = Next->Flink;
  925. }
  926. //
  927. // if we have found the list we were looking for
  928. //
  929. if (*ppNotifyList)
  930. {
  931. //
  932. // lock the list before returning.
  933. //
  934. ENTERCRIT(&(*ppNotifyList)->ListLock);
  935. }
  936. if (!(*ppNotifyList) || !bKeepLListLocked)
  937. {
  938. //
  939. // unlock llist lock.
  940. //
  941. LEAVECRIT(&gNotifyLList.ListLock);
  942. }
  943. if (*ppNotifyList)
  944. {
  945. return STATUS_SUCCESS;
  946. }
  947. else
  948. {
  949. return STATUS_NO_SUCH_LOGON_SESSION;
  950. }
  951. }
  952. void ReleaseNotificationList (PNOTIFY_LIST pNotifyList)
  953. {
  954. ASSERT(pNotifyList);
  955. if (IsInvalidHWndList(pNotifyList))
  956. {
  957. // we must take invalid hwnd list before taking global list.
  958. ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.GlobalList.ListLock).OwningThread );
  959. // we must take invalid hwnd list before taking LList.
  960. ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.ListLock).OwningThread );
  961. }
  962. else if (IsGlobalList(pNotifyList))
  963. {
  964. // we must take invalid hwnd list before taking LList.
  965. ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.ListLock).OwningThread );
  966. }
  967. LEAVECRIT(&pNotifyList->ListLock);
  968. }
  969. NTSTATUS GetInvlidHwndList(PNOTIFY_LIST *ppConChgNtfy)
  970. {
  971. ASSERT(ppConChgNtfy);
  972. // we must take invalid hwnd list before taking global list.
  973. ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.GlobalList.ListLock).OwningThread );
  974. // we must take invalid hwnd list before taking LList.
  975. ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.ListLock).OwningThread );
  976. *ppConChgNtfy = &gNotifyLList.InvlidHwndList;
  977. ENTERCRIT(&(*ppConChgNtfy)->ListLock);
  978. ASSERT(gNotifyLList.InvlidHwndList.Links.Blink == NULL);
  979. ASSERT(gNotifyLList.InvlidHwndList.Links.Flink == NULL);
  980. ASSERT(gNotifyLList.InvlidHwndList.SessionId == INVALID_SESSIONID);
  981. ASSERT(gNotifyLList.InvlidHwndList.SessonSerialNumber == INVALID_SESSIONSERIAL);
  982. return STATUS_SUCCESS;
  983. }
  984. NTSTATUS GetGlobalNotificationList(PNOTIFY_LIST *ppConChgNtfy)
  985. {
  986. ASSERT(ppConChgNtfy);
  987. // we must LLIst after global list.
  988. ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.ListLock).OwningThread );
  989. *ppConChgNtfy = &gNotifyLList.GlobalList;
  990. ENTERCRIT(&(*ppConChgNtfy)->ListLock);
  991. ASSERT(gNotifyLList.GlobalList.Links.Blink == NULL);
  992. ASSERT(gNotifyLList.GlobalList.Links.Flink == NULL);
  993. ASSERT(gNotifyLList.GlobalList.SessionId == INVALID_SESSIONID);
  994. ASSERT(gNotifyLList.GlobalList.SessonSerialNumber == INVALID_SESSIONSERIAL);
  995. return (STATUS_SUCCESS);
  996. }
  997. NTSTATUS InitializeNotificationQueue ()
  998. {
  999. DWORD ThreadId;
  1000. NTSTATUS Status;
  1001. HANDLE hSessionNotifyThread;
  1002. InitializeListHead( &gNotifyLList.RequestQueue.ListHead);
  1003. gNotifyLList.RequestQueue.hNotificationEvent = CreateEvent(
  1004. NULL, // SD
  1005. FALSE, // reset type
  1006. FALSE, // initial state
  1007. NULL // object name
  1008. );
  1009. if (gNotifyLList.RequestQueue.hNotificationEvent == NULL)
  1010. {
  1011. // we failed to create event.
  1012. // return GetLastError()
  1013. return STATUS_UNSUCCESSFUL;
  1014. }
  1015. Status = RtlInitializeCriticalSection( &gNotifyLList.RequestQueue.ListLock );
  1016. if (!NT_SUCCESS(Status))
  1017. {
  1018. CloseHandle(gNotifyLList.RequestQueue.hNotificationEvent);
  1019. gNotifyLList.RequestQueue.hNotificationEvent = NULL;
  1020. return Status;
  1021. }
  1022. //
  1023. // now create thread for notifications.
  1024. //
  1025. hSessionNotifyThread = CreateThread(
  1026. NULL,
  1027. 0,
  1028. (LPTHREAD_START_ROUTINE)NotificationQueueWorker,
  1029. NULL,
  1030. 0,
  1031. &ThreadId);
  1032. //
  1033. // Just close it, we can do without this handle.
  1034. //
  1035. if( hSessionNotifyThread )
  1036. {
  1037. CloseHandle( hSessionNotifyThread );
  1038. }
  1039. else
  1040. {
  1041. RtlDeleteCriticalSection( &gNotifyLList.RequestQueue.ListLock );
  1042. CloseHandle(gNotifyLList.RequestQueue.hNotificationEvent);
  1043. gNotifyLList.RequestQueue.hNotificationEvent = NULL;
  1044. return STATUS_UNSUCCESSFUL;
  1045. }
  1046. return STATUS_SUCCESS;
  1047. }
  1048. void LockNotificationQueue()
  1049. {
  1050. ENTERCRIT(&gNotifyLList.RequestQueue.ListLock);
  1051. }
  1052. void UnLockNotificationQueue()
  1053. {
  1054. LEAVECRIT(&gNotifyLList.RequestQueue.ListLock);
  1055. }
  1056. //
  1057. // Queues a notification entry
  1058. //
  1059. NTSTATUS QueueNotificationRequest(ULONG SessionSerialNumber, ULONG SessionId, WPARAM notification)
  1060. {
  1061. PNOTIFICATION_REQUEST pRequest = NULL;
  1062. pRequest = MemAlloc( sizeof(NOTIFICATION_REQUEST) );
  1063. if (!pRequest)
  1064. {
  1065. return STATUS_NO_MEMORY;
  1066. }
  1067. pRequest->SessonSerialNumber = SessionSerialNumber;
  1068. pRequest->SessionId = SessionId;
  1069. pRequest->NotificationCode = notification;
  1070. // now lock the queue
  1071. LockNotificationQueue();
  1072. InsertHeadList(&gNotifyLList.RequestQueue.ListHead, &pRequest->Links);
  1073. UnLockNotificationQueue();
  1074. // let the waiting thread process this notification.
  1075. PulseEvent(gNotifyLList.RequestQueue.hNotificationEvent);
  1076. return STATUS_SUCCESS;
  1077. }
  1078. //
  1079. // takes out a notification entry from queue.
  1080. //
  1081. PNOTIFICATION_REQUEST UnQueueNotificationRequest()
  1082. {
  1083. PLIST_ENTRY pEntry;
  1084. PNOTIFICATION_REQUEST pRequest = NULL;
  1085. //
  1086. // Remove a request from the list.
  1087. //
  1088. LockNotificationQueue();
  1089. if (!IsListEmpty(&gNotifyLList.RequestQueue.ListHead))
  1090. {
  1091. pEntry = RemoveTailList(&gNotifyLList.RequestQueue.ListHead);
  1092. pRequest = CONTAINING_RECORD(pEntry, NOTIFICATION_REQUEST, Links);
  1093. }
  1094. UnLockNotificationQueue();
  1095. return pRequest;
  1096. }
  1097. // This thread is a helper for the next function. We do this because the
  1098. // compiler defies reason by insisting not all control paths return a value.
  1099. VOID NotificationQueueWorkerEx()
  1100. {
  1101. PNOTIFICATION_REQUEST pRequest = NULL;
  1102. for(;;)
  1103. {
  1104. WaitForSingleObject(gNotifyLList.RequestQueue.hNotificationEvent, INFINITE); // wait for the event to be signaled.
  1105. while ((pRequest = UnQueueNotificationRequest()) != NULL)
  1106. {
  1107. if (!pRequest->NotificationCode)
  1108. {
  1109. ASSERT(FALSE);
  1110. // this is not a real notificaiton request.
  1111. // this request is for session removal.
  1112. // RemoveGlobalNotification(pRequest->SessionId, pRequest->SessonSerialNumber);
  1113. }
  1114. else
  1115. {
  1116. NotifyConsole (pRequest->SessionId, pRequest->SessonSerialNumber, pRequest->NotificationCode);
  1117. }
  1118. MemFree(pRequest);
  1119. pRequest = NULL;
  1120. }
  1121. }
  1122. }
  1123. //
  1124. // this thread takes a notification request from queue and executes it.
  1125. // this thread gets signaled when a new item is added to the queue.
  1126. //
  1127. DWORD NotificationQueueWorker(LPVOID ThreadParameter)
  1128. {
  1129. UNREFERENCED_PARAMETER(ThreadParameter);
  1130. NotificationQueueWorkerEx();
  1131. return 0;
  1132. }
  1133. NTSTATUS SetLockedState (PWINSTATION pWinStation, BOOL bLocked)
  1134. {
  1135. ASSERT(pWinStation);
  1136. if (bLocked)
  1137. {
  1138. return NofifyWelcomeOn (pWinStation);
  1139. }
  1140. else
  1141. {
  1142. return NotifyWelcomeOff (pWinStation);
  1143. }
  1144. }
  1145. NTSTATUS GetLockedState (PWINSTATION pWinStation, BOOL *pbLocked)
  1146. {
  1147. NTSTATUS Status;
  1148. PNOTIFY_LIST pNotifyList;
  1149. ASSERT(pbLocked);
  1150. ASSERT(pWinStation);
  1151. Status = GetNoficationListFromSessionId(pWinStation->LogonId, &pNotifyList, FALSE);
  1152. if ( !NT_SUCCESS( Status ) )
  1153. {
  1154. return (Status);
  1155. }
  1156. *pbLocked = pNotifyList->SessionState.bLocked;
  1157. ReleaseNotificationList(pNotifyList);
  1158. return STATUS_SUCCESS;
  1159. }
  1160. /*
  1161. NTSTATUS GetSessionState (PWINSTATION pWinStation, WTSSESSION_STATE *pSessionState)
  1162. {
  1163. NTSTATUS Status;
  1164. PNOTIFY_LIST pNotifyList;
  1165. ASSERT(pSessionState);
  1166. ASSERT(pWinStation);
  1167. Status = GetNoficationListFromSessionId(pWinStation->LogonId, &pNotifyList, FALSE);
  1168. if ( !NT_SUCCESS( Status ) )
  1169. {
  1170. return (Status);
  1171. }
  1172. *pSessionState = pNotifyList->SessionState;
  1173. ReleaseNotificationList(pNotifyList);
  1174. return STATUS_SUCCESS;
  1175. }
  1176. */
  1177. NTSTATUS RemoveBadHwnd(ULONG_PTR hWnd, ULONG SessionId)
  1178. {
  1179. PNOTIFY_ENTRY_GLOBAL pInvalidHwndEntry;
  1180. PNOTIFY_LIST pInvalidHwndList;
  1181. NTSTATUS Status;
  1182. Status = GetInvlidHwndList(&pInvalidHwndList);
  1183. if ( !NT_SUCCESS( Status ) )
  1184. {
  1185. return (Status);
  1186. }
  1187. pInvalidHwndEntry = GetHWndEntryFromGlobalList(pInvalidHwndList, hWnd, SessionId, WTS_WINDOW_NOTIFICATION);
  1188. //
  1189. // this entry must not already exist in invalid list.
  1190. //
  1191. if(!pInvalidHwndEntry)
  1192. {
  1193. // it alreay exists in our list.
  1194. pInvalidHwndEntry = MemAlloc(sizeof(NOTIFY_ENTRY_GLOBAL));
  1195. if (pInvalidHwndEntry)
  1196. {
  1197. pInvalidHwndEntry->hWnd = hWnd;
  1198. pInvalidHwndEntry->SessionId = SessionId;
  1199. pInvalidHwndEntry->dwFlags = WTS_WINDOW_NOTIFICATION;
  1200. pInvalidHwndEntry->RefCount = 0xFFFFFFFF;
  1201. pInvalidHwndEntry->dwMask = 0xFFFFFFFF;
  1202. InsertHeadList(&pInvalidHwndList->ListHead, &pInvalidHwndEntry->Links);
  1203. }
  1204. }
  1205. ReleaseNotificationList( pInvalidHwndList );
  1206. if (pInvalidHwndEntry)
  1207. return STATUS_SUCCESS;
  1208. else
  1209. return STATUS_NO_MEMORY;
  1210. }
  1211. NTSTATUS RemoveBadEvents(DWORD SessionId)
  1212. {
  1213. PNOTIFY_LIST pListGlobal = NULL;
  1214. PLIST_ENTRY Next, Head;
  1215. PNOTIFY_LIST pNotifyList = NULL;
  1216. NTSTATUS Status = GetGlobalNotificationList(&pListGlobal);
  1217. if (NT_SUCCESS( Status ))
  1218. {
  1219. Head = &pListGlobal->ListHead;
  1220. Next = Head->Flink;
  1221. while (Head != Next)
  1222. {
  1223. PNOTIFY_ENTRY_GLOBAL pEntryGlobal = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
  1224. Next = Next->Flink;
  1225. ASSERT(pEntryGlobal);
  1226. if ((pEntryGlobal->SessionId == SessionId) && (pEntryGlobal->dwFlags & WTS_EVENT_NOTIFICATION))
  1227. {
  1228. OBJECT_BASIC_INFORMATION Obi;
  1229. Status = NtQueryObject(
  1230. (HANDLE)pEntryGlobal->hWnd,
  1231. ObjectBasicInformation,
  1232. &Obi,
  1233. sizeof (OBJECT_BASIC_INFORMATION),
  1234. NULL
  1235. );
  1236. if (Status == STATUS_SUCCESS)
  1237. {
  1238. ASSERT(Obi.HandleCount >= 1);
  1239. if (Obi.HandleCount == 1)
  1240. {
  1241. //
  1242. // its just us referencing this event.
  1243. // let it go.
  1244. //
  1245. RemoveEntryList( &pEntryGlobal->Links );
  1246. CloseHandle((HANDLE)pEntryGlobal->hWnd);
  1247. MemFree(pEntryGlobal);
  1248. pEntryGlobal = NULL;
  1249. }
  1250. }
  1251. else
  1252. {
  1253. NTSDDBGPRINT(("conntfy.c - NtQueryObject failed, Status = %d.\n", Status));
  1254. }
  1255. }
  1256. }
  1257. ReleaseNotificationList(pListGlobal);
  1258. pListGlobal = NULL;
  1259. }
  1260. else
  1261. {
  1262. NTSDDBGPRINT(("conntfy.c - GetGlobalNotificationList failed, Status = %d.\n", Status));
  1263. }
  1264. Status = GetNoficationListFromSessionId( SessionId, &pNotifyList, FALSE);
  1265. if (NT_SUCCESS( Status ))
  1266. {
  1267. Head = &pNotifyList->ListHead;
  1268. Next = Head->Flink;
  1269. while (Head != Next)
  1270. {
  1271. PNOTIFY_ENTRY pEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY, Links );
  1272. Next = Next->Flink;
  1273. ASSERT(pEntry);
  1274. if (pEntry->dwFlags & WTS_EVENT_NOTIFICATION)
  1275. {
  1276. OBJECT_BASIC_INFORMATION Obi;
  1277. Status = NtQueryObject(
  1278. (HANDLE)pEntry->hWnd,
  1279. ObjectBasicInformation,
  1280. &Obi,
  1281. sizeof (OBJECT_BASIC_INFORMATION),
  1282. NULL
  1283. );
  1284. if (Status == STATUS_SUCCESS)
  1285. {
  1286. ASSERT(Obi.HandleCount >= 1);
  1287. if (Obi.HandleCount == 1)
  1288. {
  1289. //
  1290. // its just us referencing this event.
  1291. // let it go.
  1292. //
  1293. RemoveEntryList( &pEntry->Links );
  1294. CloseHandle((HANDLE)pEntry->hWnd);
  1295. MemFree(pEntry);
  1296. pEntry = NULL;
  1297. }
  1298. }
  1299. }
  1300. }
  1301. ReleaseNotificationList(pNotifyList);
  1302. pNotifyList = NULL;
  1303. }
  1304. else
  1305. {
  1306. NTSDDBGPRINT(("conntfy.c - GetNoficationListFromSessionId failed, Status = %d.\n", Status));
  1307. }
  1308. return Status;
  1309. }
  1310. NTSTATUS RemoveInvalidWindowsFromLists()
  1311. {
  1312. PNOTIFY_LIST pInvalidHwndList;
  1313. PLIST_ENTRY Next, Head;
  1314. NTSTATUS Status;
  1315. Status = GetInvlidHwndList(&pInvalidHwndList);
  1316. if ( !NT_SUCCESS( Status ) )
  1317. {
  1318. return (Status);
  1319. }
  1320. Head = &pInvalidHwndList->ListHead;
  1321. Next = Head->Flink;
  1322. while (Head != Next)
  1323. {
  1324. PNOTIFY_ENTRY_GLOBAL pInvalidHwndEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
  1325. Next = Next->Flink;
  1326. ASSERT(pInvalidHwndEntry);
  1327. Status = UnRegisterConsoleNotificationInternal (pInvalidHwndEntry->hWnd, pInvalidHwndEntry->SessionId, FALSE, WTS_WINDOW_NOTIFICATION);
  1328. // we are done removing this invalid hwnd entry from our lists.
  1329. RemoveEntryList( &pInvalidHwndEntry->Links );
  1330. MemFree(pInvalidHwndEntry);
  1331. pInvalidHwndEntry = NULL;
  1332. }
  1333. ReleaseNotificationList(pInvalidHwndList);
  1334. return STATUS_SUCCESS;
  1335. }
  1336. /*
  1337. our order of locks is
  1338. 0. Invalid Hwnd List.
  1339. 1. Global Notification List
  1340. 2. Winstation
  1341. 3. List of Lists lock.
  1342. 4. Session Notification List
  1343. */
  1344. //#ifdef MAKARANDS_HIGHER_WARNING_LEVEL
  1345. #pragma warning(pop)
  1346. //#endif