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
1701 lines
50 KiB
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#define SECURITY_WIN32
|
|
|
|
#ifdef NTSDDEBUG
|
|
#define NTSDDBGPRINT(x) DbgPrint x
|
|
#else
|
|
#define NTSDDBGPRINT(x)
|
|
#endif
|
|
|
|
#include "winsvcp.h" // defines I_ScSendTSMessage
|
|
#include "conntfy.h"
|
|
|
|
|
|
|
|
|
|
BOOL IsBitSet(DWORD dwMask, WPARAM notifybit)
|
|
{
|
|
// why are you asking for bit 0?
|
|
ASSERT(notifybit != 0);
|
|
ASSERT(notifybit <= WTS_MAX_SESSION_NOTIFICATION);
|
|
return (CREATE_MASK(notifybit)) & dwMask;
|
|
}
|
|
|
|
|
|
|
|
//#ifdef MAKARANDS_HIGHER_WARNING_LEVEL
|
|
#pragma warning(push, 4)
|
|
#pragma warning(disable:4201) // nameless structure.
|
|
//#endif
|
|
|
|
#define INVALID_SESSIONSERIAL 0xffffffff
|
|
|
|
|
|
// 0x1 fConnected
|
|
// 0x2 fLoggedOn
|
|
// 0x3 fRemote
|
|
// 0x4 fWelcome
|
|
typedef struct _WTSSESSION_STATE
|
|
{
|
|
unsigned int bConnected: 1;
|
|
unsigned int bLoggedOn: 1;
|
|
unsigned int bConsole: 1;
|
|
unsigned int bRemote: 1;
|
|
unsigned int bLocked: 1;
|
|
|
|
} WTSSESSION_STATE, *PWTSSESSION_STATE;
|
|
|
|
/*
|
|
WTS_CONSOLE_CONNECT bConnected, bConsole, !bRemote,
|
|
WTS_CONSOLE_DISCONNECT !bConnected, !bConsole, !bRemote
|
|
WTS_REMOTE_CONNECT bConnected, !bConsole, bremote
|
|
WTS_REMOTE_DISCONNECT !bConnected, !bConsole, !bRemote
|
|
WTS_SESSION_LOGON bLoggedOn
|
|
WTS_SESSION_LOGOFF !bLoggedOn
|
|
WTS_SESSION_LOCK bLocked
|
|
WTS_SESSION_UNLOCK !bLocked
|
|
*/
|
|
|
|
//
|
|
// this is head for hwnds list.
|
|
// this links NOTIFY_ENTRY or NOTIFY_ENTRY_GLOBAL together.
|
|
//
|
|
typedef struct _NOTIFY_LIST
|
|
{
|
|
LIST_ENTRY Links; // links to other NOTIFY_LISTs. not used in case of global notification list.
|
|
LIST_ENTRY ListHead; // head of notification entries. links NOTIFY_ENTRYs (or NOTIFY_ENTRY_GLOBAL) together
|
|
RTL_CRITICAL_SECTION ListLock; // lock to travel the entries.
|
|
ULONG SessionId; // session id ( not used in case of global list)
|
|
ULONG SessonSerialNumber; // serial number ( not used in case of global list)
|
|
WTSSESSION_STATE SessionState; // state of the session.
|
|
|
|
} NOTIFY_LIST, *PNOTIFY_LIST;
|
|
|
|
//
|
|
// entry in notification list per winstation.
|
|
//
|
|
typedef struct _NOTIFY_ENTRY
|
|
{
|
|
LIST_ENTRY Links; // links to other entries
|
|
ULONG_PTR hWnd; // window or event handle.
|
|
ULONG RefCount; // how many times was this hwnd registered ?
|
|
DWORD dwMask; // mask tell us the event to be notified for.
|
|
DWORD dwFlags; // flags.
|
|
|
|
} NOTIFY_ENTRY, *PNOTIFY_ENTRY;
|
|
|
|
//
|
|
// Entry in Notification list for all sessions Notifications.
|
|
//
|
|
typedef struct _NOTIFY_ENTRY_GLOBAL
|
|
{
|
|
struct _NOTIFY_ENTRY; // above structure +
|
|
ULONG SessionId; // since this is global entry, it needs to keep session id per hwnd.
|
|
|
|
} NOTIFY_ENTRY_GLOBAL, *PNOTIFY_ENTRY_GLOBAL;
|
|
|
|
//
|
|
// The notification Queue.
|
|
//
|
|
typedef struct _NOTIFICATION_QUEUE
|
|
{
|
|
LIST_ENTRY ListHead; // head of queue reuests. links NOTIFICATION_REQUESTs together
|
|
RTL_CRITICAL_SECTION ListLock; // lock to travel the queue
|
|
HANDLE hNotificationEvent; // syncronization between woker and caller of queue.
|
|
|
|
} NOTIFICATION_QUEUE, *PNOTIFICATION_QUEUE;
|
|
|
|
//
|
|
// Entry in Notification Queue.
|
|
//
|
|
typedef struct _NOTIFICATION_REQUEST
|
|
{
|
|
LIST_ENTRY Links; // links to other entries.
|
|
ULONG SessionId; // session id for the session this notificaiton is to be sent.
|
|
ULONG SessonSerialNumber; // serial number for the session this notificaiton is to be sent.
|
|
WPARAM NotificationCode; // notificaiton code
|
|
|
|
} NOTIFICATION_REQUEST, *PNOTIFICATION_REQUEST;
|
|
|
|
//
|
|
// our main data structure.
|
|
//
|
|
typedef struct _NOTIFY_LLIST
|
|
{
|
|
LIST_ENTRY ListHead; // head of notification lists. links NOTIFY_LISTs together.
|
|
RTL_CRITICAL_SECTION ListLock; // lock to travel the head list.
|
|
NOTIFY_LIST GlobalList; // global notification list.
|
|
NOTIFICATION_QUEUE RequestQueue; // notification queue.
|
|
NOTIFY_LIST InvlidHwndList; // invalid window list
|
|
|
|
} NOTIFY_LLIST, PNOTIFY_LLIST;
|
|
|
|
//
|
|
// File Globals.
|
|
//
|
|
NOTIFY_LLIST gNotifyLList;
|
|
|
|
|
|
//
|
|
// private functions
|
|
//
|
|
BOOL DoesHWndExists (
|
|
PNOTIFY_LIST pNotifyList,
|
|
ULONG_PTR hWnd
|
|
);
|
|
|
|
PNOTIFY_ENTRY GetHWndEntryFromSessionList (
|
|
PNOTIFY_LIST pNotifyList,
|
|
ULONG_PTR hWnd,
|
|
DWORD dwFlags
|
|
);
|
|
|
|
PNOTIFY_ENTRY_GLOBAL GetHWndEntryFromGlobalList (
|
|
PNOTIFY_LIST pNotifyList,
|
|
ULONG_PTR hWnd,
|
|
ULONG SessionId,
|
|
DWORD dwFlags
|
|
);
|
|
|
|
NTSTATUS GetNoficationListFromSessionId (
|
|
ULONG SessionId,
|
|
PNOTIFY_LIST *ppNofificationList,
|
|
BOOL bKeepLListLocked
|
|
);
|
|
|
|
NTSTATUS GetGlobalNotificationList (
|
|
PNOTIFY_LIST *ppConChgNtfy
|
|
);
|
|
|
|
|
|
NTSTATUS GetInvlidHwndList(PNOTIFY_LIST *ppConChgNtfy);
|
|
NTSTATUS NotifyConsole (
|
|
ULONG SessionId,
|
|
ULONG SessionSerialNumber,
|
|
WPARAM wParam
|
|
);
|
|
|
|
NTSTATUS SendConsoleNotification (
|
|
ULONG SessionId,
|
|
ULONG_PTR hWnd,
|
|
ULONG Msg,
|
|
WPARAM wParam,
|
|
WTSSESSION_NOTIFICATION wtsConsoleNotification
|
|
);
|
|
|
|
BOOL IsGlobalList(PNOTIFY_LIST pNtfyList);
|
|
|
|
int GetListCount (
|
|
LIST_ENTRY *pListHead
|
|
);
|
|
|
|
|
|
NTSTATUS DestroyLock ( PNOTIFY_LIST pNtfyList);
|
|
NTSTATUS CreateLock ( PNOTIFY_LIST pNtfyList);
|
|
|
|
|
|
|
|
NTSTATUS
|
|
InitializeNotificationQueue ();
|
|
|
|
NTSTATUS
|
|
QueueNotificationRequest (
|
|
ULONG SessionSerialNumber,
|
|
ULONG SessionId,
|
|
WPARAM notification
|
|
);
|
|
|
|
PNOTIFICATION_REQUEST
|
|
UnQueueNotificationRequest ();
|
|
|
|
DWORD NotificationQueueWorker (
|
|
LPVOID
|
|
);
|
|
|
|
NTSTATUS RemoveGlobalNotification (ULONG SessionId);
|
|
NTSTATUS RemoveInvalidWindowsFromLists ();
|
|
NTSTATUS RemoveBadEvents(ULONG SessionId);
|
|
NTSTATUS UnRegisterConsoleNotificationInternal (ULONG_PTR hWnd, ULONG SessionId, BOOL bDcrRef, DWORD dwFlags);
|
|
|
|
void ReleaseNotificationList (PNOTIFY_LIST pNotifyList);
|
|
|
|
void UpdateSessionState(PNOTIFY_LIST pNotifyList, WPARAM wNotification)
|
|
{
|
|
/*
|
|
WTS_CONSOLE_CONNECT bConnected, bConsole, !bRemote,
|
|
WTS_CONSOLE_DISCONNECT !bConnected, !bConsole, !bRemote
|
|
WTS_REMOTE_CONNECT bConnected, !bConsole, bremote
|
|
WTS_REMOTE_DISCONNECT !bConnected, !bConsole, !bRemote
|
|
WTS_SESSION_LOGON bLoggedOn
|
|
WTS_SESSION_LOGOFF !bLoggedOn
|
|
WTS_SESSION_LOCK bLocked
|
|
WTS_SESSION_UNLOCK !bLocked
|
|
*/
|
|
|
|
ASSERT(!IsGlobalList(pNotifyList));
|
|
|
|
|
|
ASSERT(!pNotifyList->SessionState.bConsole || !pNotifyList->SessionState.bRemote);
|
|
ASSERT(!pNotifyList->SessionState.bConnected || pNotifyList->SessionState.bConsole || pNotifyList->SessionState.bRemote);
|
|
|
|
switch (wNotification)
|
|
{
|
|
case WTS_CONSOLE_CONNECT:
|
|
|
|
ASSERT(!pNotifyList->SessionState.bConsole);
|
|
ASSERT(!pNotifyList->SessionState.bRemote);
|
|
|
|
pNotifyList->SessionState.bConnected = 1;
|
|
pNotifyList->SessionState.bConsole = 1;
|
|
break;
|
|
|
|
case WTS_CONSOLE_DISCONNECT:
|
|
|
|
ASSERT(pNotifyList->SessionState.bConsole);
|
|
ASSERT(pNotifyList->SessionState.bConnected);
|
|
ASSERT(!pNotifyList->SessionState.bRemote);
|
|
|
|
pNotifyList->SessionState.bConnected = 0;
|
|
pNotifyList->SessionState.bConsole = 0;
|
|
break;
|
|
|
|
case WTS_REMOTE_DISCONNECT:
|
|
|
|
ASSERT(pNotifyList->SessionState.bRemote);
|
|
ASSERT(pNotifyList->SessionState.bConnected);
|
|
ASSERT(!pNotifyList->SessionState.bConsole);
|
|
|
|
pNotifyList->SessionState.bConnected = 0;
|
|
pNotifyList->SessionState.bRemote = 0;
|
|
break;
|
|
|
|
case WTS_REMOTE_CONNECT:
|
|
|
|
ASSERT(!pNotifyList->SessionState.bRemote);
|
|
ASSERT(!pNotifyList->SessionState.bConnected);
|
|
ASSERT(!pNotifyList->SessionState.bConsole);
|
|
|
|
pNotifyList->SessionState.bConnected = 1;
|
|
pNotifyList->SessionState.bRemote = 1;
|
|
break;
|
|
|
|
case WTS_SESSION_LOGON:
|
|
|
|
ASSERT(pNotifyList->SessionState.bLoggedOn == 0);
|
|
|
|
pNotifyList->SessionState.bLoggedOn = 1;
|
|
break;
|
|
|
|
case WTS_SESSION_LOGOFF:
|
|
|
|
ASSERT(pNotifyList->SessionState.bLoggedOn == 1);
|
|
|
|
pNotifyList->SessionState.bLoggedOn = 0;
|
|
break;
|
|
|
|
case WTS_SESSION_LOCK:
|
|
|
|
ASSERT(pNotifyList->SessionState.bLocked == 0);
|
|
|
|
pNotifyList->SessionState.bLocked = 1;
|
|
break;
|
|
|
|
case WTS_SESSION_UNLOCK:
|
|
|
|
ASSERT(pNotifyList->SessionState.bLocked == 1);
|
|
|
|
pNotifyList->SessionState.bLocked = 0;
|
|
break;
|
|
|
|
case WTS_SESSION_REMOTE_CONTROL:
|
|
|
|
NOTHING;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
ASSERT(!pNotifyList->SessionState.bConsole || !pNotifyList->SessionState.bRemote);
|
|
ASSERT(!pNotifyList->SessionState.bConnected || pNotifyList->SessionState.bConsole || pNotifyList->SessionState.bRemote);
|
|
}
|
|
|
|
//
|
|
// Global initialization.
|
|
//
|
|
NTSTATUS InitializeConsoleNotification ()
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
InitializeListHead( &gNotifyLList.ListHead );
|
|
Status = RtlInitializeCriticalSection( &gNotifyLList.ListLock );
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return (Status);
|
|
}
|
|
|
|
//
|
|
// following members are unused in for global list.
|
|
//
|
|
gNotifyLList.GlobalList.Links.Blink = NULL;
|
|
gNotifyLList.GlobalList.Links.Flink = NULL;
|
|
gNotifyLList.GlobalList.SessionId = INVALID_SESSIONID;
|
|
gNotifyLList.GlobalList.SessonSerialNumber = INVALID_SESSIONSERIAL;
|
|
|
|
|
|
InitializeListHead( &gNotifyLList.GlobalList.ListHead);
|
|
Status = RtlInitializeCriticalSection( &gNotifyLList.GlobalList.ListLock );
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
RtlDeleteCriticalSection( &gNotifyLList.ListLock );
|
|
return (Status);
|
|
}
|
|
|
|
|
|
gNotifyLList.InvlidHwndList.Links.Blink = NULL;
|
|
gNotifyLList.InvlidHwndList.Links.Flink = NULL;
|
|
gNotifyLList.InvlidHwndList.SessionId = INVALID_SESSIONID;
|
|
gNotifyLList.InvlidHwndList.SessonSerialNumber = INVALID_SESSIONSERIAL;
|
|
|
|
InitializeListHead(&gNotifyLList.InvlidHwndList.ListHead) ;
|
|
Status = RtlInitializeCriticalSection( &gNotifyLList.InvlidHwndList.ListLock );
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
RtlDeleteCriticalSection( &gNotifyLList.ListLock );
|
|
RtlDeleteCriticalSection( &gNotifyLList.GlobalList.ListLock );
|
|
return (Status);
|
|
}
|
|
|
|
|
|
Status = InitializeNotificationQueue ();
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
RtlDeleteCriticalSection( &gNotifyLList.ListLock );
|
|
RtlDeleteCriticalSection( &gNotifyLList.GlobalList.ListLock );
|
|
RtlDeleteCriticalSection( &gNotifyLList.InvlidHwndList.ListLock );
|
|
}
|
|
|
|
return (Status);
|
|
}
|
|
|
|
|
|
//
|
|
// per winstation initialization.
|
|
//
|
|
NTSTATUS InitializeSessionNotification (PWINSTATION pWinStation)
|
|
{
|
|
NTSTATUS Status;
|
|
PNOTIFY_LIST pNewNotifyList;
|
|
|
|
ASSERT(pWinStation);
|
|
|
|
if (pWinStation->Terminating)
|
|
{
|
|
// dont create notification list if this winstation is already terminating.
|
|
// its possible that a winstation is being terminated before getting completely created,
|
|
// in such case we might end up calling RemoveSessionNotification before InitializeSessionNotification.
|
|
// so essentially leaving this session never to deleted. (Bug #414330)
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#ifdef DBG
|
|
|
|
// BUGBUG - is it possible that a old session with the same session id is still there?
|
|
Status = GetNoficationListFromSessionId(pWinStation->LogonId, &pNewNotifyList, FALSE);
|
|
|
|
//
|
|
// we are just being asked to initialize notification
|
|
// we must not find list for this session in our LList.
|
|
//
|
|
ASSERT( STATUS_NO_SUCH_LOGON_SESSION == Status );
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// create a new hwnd list for this session
|
|
//
|
|
pNewNotifyList = MemAlloc(sizeof(NOTIFY_LIST));
|
|
if (!pNewNotifyList)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
pNewNotifyList->SessionId = pWinStation->LogonId;
|
|
pNewNotifyList->SessonSerialNumber = pWinStation->SessionSerialNumber;
|
|
|
|
//
|
|
// initialize session state.
|
|
//
|
|
{
|
|
pNewNotifyList->SessionState.bConnected = 0;
|
|
pNewNotifyList->SessionState.bConsole = 0;
|
|
pNewNotifyList->SessionState.bLoggedOn = 0;
|
|
pNewNotifyList->SessionState.bRemote = 0;
|
|
pNewNotifyList->SessionState.bLocked = 0; // bugbug we dont know the real welcome state ;(
|
|
}
|
|
|
|
InitializeListHead( &pNewNotifyList->ListHead);
|
|
|
|
Status = RtlInitializeCriticalSection( &pNewNotifyList->ListLock );
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
MemFree(pNewNotifyList);
|
|
pNewNotifyList = NULL;
|
|
return Status;
|
|
}
|
|
|
|
// now link this new list into our main list of lists.
|
|
ENTERCRIT(&gNotifyLList.ListLock);
|
|
InsertTailList( &gNotifyLList.ListHead, &pNewNotifyList->Links);
|
|
LEAVECRIT(&gNotifyLList.ListLock);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
//
|
|
// must be called when a session ends.
|
|
//
|
|
NTSTATUS RemoveSessionNotification(ULONG SessionId, ULONG SessionSerialNumber)
|
|
{
|
|
NTSTATUS Status;
|
|
PNOTIFY_LIST pListTobeRemoved;
|
|
UNREFERENCED_PARAMETER(SessionSerialNumber); // it's referenced only for Chk builds.
|
|
|
|
|
|
// BUGBUG - is it possible that a new session with the same session id was created while we are here ?
|
|
Status = GetNoficationListFromSessionId( SessionId, &pListTobeRemoved, TRUE);
|
|
|
|
if (!NT_SUCCESS( Status ))
|
|
{
|
|
//
|
|
// we are being asked to remove session notification
|
|
// but its possible that we dont have session notification list created for this session.
|
|
// This can happen if the session is being terminate during session creation process.
|
|
//
|
|
ASSERT( !pListTobeRemoved );
|
|
return Status;
|
|
|
|
}
|
|
|
|
ASSERT( pListTobeRemoved );
|
|
ASSERT( SessionSerialNumber == pListTobeRemoved->SessonSerialNumber );
|
|
|
|
RemoveEntryList( &pListTobeRemoved->Links );
|
|
LEAVECRIT(&gNotifyLList.ListLock);
|
|
|
|
//
|
|
// walk throught this list and free all the nodes.
|
|
//
|
|
while (!IsListEmpty(&pListTobeRemoved->ListHead))
|
|
{
|
|
PNOTIFY_ENTRY pEntry;
|
|
PLIST_ENTRY Next;
|
|
|
|
Next = pListTobeRemoved->ListHead.Flink;
|
|
|
|
ASSERT(Next);
|
|
|
|
pEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY, Links );
|
|
ASSERT(pEntry);
|
|
|
|
RemoveEntryList( &pEntry->Links );
|
|
|
|
if (pEntry->dwFlags & WTS_EVENT_NOTIFICATION)
|
|
{
|
|
CloseHandle((HANDLE)pEntry->hWnd);
|
|
}
|
|
MemFree(pEntry);
|
|
pEntry = NULL;
|
|
}
|
|
|
|
// we are no more going to use this list lock.
|
|
RtlDeleteCriticalSection( &pListTobeRemoved->ListLock );
|
|
MemFree(pListTobeRemoved);
|
|
pListTobeRemoved = NULL;
|
|
|
|
|
|
return RemoveGlobalNotification (SessionId);
|
|
// return QueueNotificationRequest(pWinStation->SessionSerialNumber, pWinStation->LogonId, 0);
|
|
|
|
}
|
|
|
|
NTSTATUS RemoveGlobalNotification (ULONG SessionId)
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PNOTIFY_LIST pListTobeRemoved = NULL;
|
|
NTSTATUS Status = GetGlobalNotificationList(&pListTobeRemoved);
|
|
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return (Status);
|
|
}
|
|
|
|
ASSERT(pListTobeRemoved);
|
|
|
|
|
|
Head = &pListTobeRemoved->ListHead;
|
|
Next = Head->Flink;
|
|
while (Head != Next)
|
|
{
|
|
PNOTIFY_ENTRY_GLOBAL pEntryGlobal = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
|
|
Next = Next->Flink;
|
|
ASSERT(pEntryGlobal);
|
|
if (pEntryGlobal->SessionId == SessionId)
|
|
{
|
|
RemoveEntryList( &pEntryGlobal->Links );
|
|
if (pEntryGlobal->dwFlags & WTS_EVENT_NOTIFICATION)
|
|
{
|
|
CloseHandle((HANDLE)pEntryGlobal->hWnd);
|
|
}
|
|
|
|
MemFree(pEntryGlobal);
|
|
pEntryGlobal = NULL;
|
|
}
|
|
}
|
|
|
|
ReleaseNotificationList( pListTobeRemoved );
|
|
pListTobeRemoved = NULL;
|
|
|
|
// now lets remove the invalid Windows associated with this session.
|
|
// from the list if there is any.
|
|
|
|
pListTobeRemoved = NULL;
|
|
Status = GetInvlidHwndList(&pListTobeRemoved);
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return (Status);
|
|
}
|
|
|
|
ASSERT(pListTobeRemoved);
|
|
|
|
Head = &pListTobeRemoved->ListHead;
|
|
Next = Head->Flink;
|
|
while (Head != Next)
|
|
{
|
|
PNOTIFY_ENTRY_GLOBAL pEntryGlobal = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
|
|
Next = Next->Flink;
|
|
ASSERT(pEntryGlobal);
|
|
if (pEntryGlobal->SessionId == SessionId)
|
|
{
|
|
RemoveEntryList( &pEntryGlobal->Links );
|
|
MemFree(pEntryGlobal);
|
|
pEntryGlobal = NULL;
|
|
}
|
|
}
|
|
|
|
ReleaseNotificationList(pListTobeRemoved);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//NTSTATUS RegisterNotificationEvent(HANDLE hEvent, DWORD dwMaskFlags, BOOL bThisSessionOnly)
|
|
//{
|
|
//}
|
|
NTSTATUS RegisterConsoleNotification ( ULONG_PTR hWnd, ULONG SessionId, DWORD dwFlags, DWORD dwMask)
|
|
{
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
PNOTIFY_LIST pNotifyList = NULL;
|
|
PNOTIFY_LIST pNotifyListGlobal = NULL;
|
|
PNOTIFY_ENTRY pEntry = NULL;
|
|
PNOTIFY_ENTRY_GLOBAL pEntryGlobal = NULL;
|
|
|
|
// WTS_EVENT_NOTIFICATION & WTS_WINDOW_NOTIFICATION are mutually exclusive.
|
|
ASSERT(!(dwFlags & WTS_EVENT_NOTIFICATION && dwFlags & WTS_WINDOW_NOTIFICATION));
|
|
|
|
|
|
if (dwFlags & WTS_EVENT_NOTIFICATION)
|
|
{
|
|
//
|
|
// lets clean up our event list before we register this new event.
|
|
//
|
|
RemoveBadEvents(SessionId);
|
|
}
|
|
/*
|
|
if (dwFlags != NOTIFY_FOR_THIS_SESSION && dwFlags != NOTIFY_FOR_ALL_SESSIONS)
|
|
{
|
|
//
|
|
// invalid flag value
|
|
//
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
}
|
|
*/
|
|
|
|
//
|
|
// get the global session notificaiton list
|
|
//
|
|
Status = GetGlobalNotificationList(&pNotifyListGlobal);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// get the session specific list for this window
|
|
//
|
|
Status = GetNoficationListFromSessionId( SessionId, &pNotifyList, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ReleaseNotificationList (pNotifyListGlobal);
|
|
return Status;
|
|
}
|
|
|
|
|
|
ASSERT( pNotifyList );
|
|
ASSERT( pNotifyListGlobal );
|
|
|
|
|
|
pEntry = GetHWndEntryFromSessionList(pNotifyList, hWnd, dwFlags & (WTS_EVENT_NOTIFICATION | WTS_WINDOW_NOTIFICATION));
|
|
pEntryGlobal = GetHWndEntryFromGlobalList(pNotifyListGlobal, hWnd, SessionId, dwFlags & (WTS_EVENT_NOTIFICATION | WTS_WINDOW_NOTIFICATION));
|
|
|
|
//
|
|
// entry must not exist in both the lists.
|
|
//
|
|
ASSERT(!(pEntry && pEntryGlobal));
|
|
|
|
|
|
// in case of event notifications, return here if entry already exists.
|
|
if ((pEntry || pEntryGlobal) && (dwFlags & WTS_EVENT_NOTIFICATION))
|
|
{
|
|
ReleaseNotificationList( pNotifyListGlobal );
|
|
ReleaseNotificationList( pNotifyList );
|
|
|
|
return STATUS_INVALID_PARAMETER_1; // BUGBUG : get better status;
|
|
}
|
|
|
|
|
|
if (pEntry)
|
|
{
|
|
//
|
|
// Let other list go
|
|
//
|
|
ReleaseNotificationList( pNotifyListGlobal );
|
|
|
|
ASSERT( pEntry );
|
|
ASSERT( pEntry->RefCount > 0 );
|
|
|
|
//
|
|
// entry already exists, just increment its reference count.
|
|
//
|
|
pEntry->RefCount++;
|
|
|
|
ReleaseNotificationList( pNotifyList );
|
|
|
|
}
|
|
else if (pEntryGlobal)
|
|
{
|
|
ReleaseNotificationList (pNotifyList);
|
|
|
|
ASSERT( pEntryGlobal );
|
|
ASSERT( pEntryGlobal->RefCount > 0 );
|
|
|
|
//
|
|
// entry already exists, just increment its reference count.
|
|
//
|
|
pEntryGlobal->RefCount++;
|
|
|
|
ReleaseNotificationList( pNotifyListGlobal );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// the entry does not exists in either of the lists.
|
|
// so we need to create a new entry
|
|
//
|
|
if (dwFlags & NOTIFY_FOR_ALL_SESSIONS)
|
|
{
|
|
ReleaseNotificationList (pNotifyList);
|
|
|
|
pEntryGlobal = MemAlloc( sizeof(NOTIFY_ENTRY_GLOBAL) );
|
|
if (pEntryGlobal == NULL )
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
pEntryGlobal->hWnd = hWnd;
|
|
pEntryGlobal->SessionId = SessionId;
|
|
pEntryGlobal->RefCount = 1;
|
|
pEntryGlobal->dwMask = dwMask;
|
|
pEntryGlobal->dwFlags = dwFlags;
|
|
InsertTailList( &(pNotifyListGlobal->ListHead), &(pEntryGlobal->Links) );
|
|
}
|
|
|
|
ReleaseNotificationList( pNotifyListGlobal );
|
|
}
|
|
else
|
|
{
|
|
ReleaseNotificationList( pNotifyListGlobal );
|
|
|
|
pEntry = MemAlloc( sizeof(NOTIFY_ENTRY) );
|
|
if (pEntry == NULL )
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
pEntry->hWnd = hWnd;
|
|
pEntry->RefCount = 1;
|
|
pEntry->dwMask = dwMask;
|
|
pEntry->dwFlags = dwFlags;
|
|
|
|
InsertTailList( &(pNotifyList->ListHead), &(pEntry->Links) );
|
|
}
|
|
|
|
ReleaseNotificationList (pNotifyList);
|
|
}
|
|
}
|
|
|
|
return (Status);
|
|
}
|
|
|
|
NTSTATUS UnRegisterConsoleNotification (ULONG_PTR hWnd, ULONG SessionId, DWORD dwFlags)
|
|
{
|
|
return UnRegisterConsoleNotificationInternal (hWnd, SessionId, TRUE, dwFlags);
|
|
}
|
|
|
|
|
|
NTSTATUS UnRegisterConsoleNotificationInternal (ULONG_PTR hWnd, ULONG SessionId, BOOL bDcrRef, DWORD dwFlags)
|
|
{
|
|
NTSTATUS Status;
|
|
PNOTIFY_LIST pNotifyList;
|
|
PNOTIFY_ENTRY pEntry;
|
|
|
|
//
|
|
// get the notification list for the Session
|
|
//
|
|
Status = GetNoficationListFromSessionId( SessionId, &pNotifyList, FALSE);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
ASSERT(pNotifyList);
|
|
|
|
pEntry = GetHWndEntryFromSessionList(pNotifyList,hWnd, dwFlags);
|
|
|
|
if (pEntry)
|
|
{
|
|
ASSERT( pEntry->RefCount > 0 );
|
|
ASSERT( !(pEntry->dwFlags & WTS_EVENT_NOTIFICATION) || pEntry->RefCount == 1);
|
|
|
|
// decrement ref count
|
|
pEntry->RefCount--;
|
|
|
|
if (pEntry->RefCount == 0 || !bDcrRef)
|
|
{
|
|
RemoveEntryList( &pEntry->Links );
|
|
if (pEntry->dwFlags & WTS_EVENT_NOTIFICATION)
|
|
{
|
|
CloseHandle((HANDLE)pEntry->hWnd);
|
|
}
|
|
|
|
MemFree(pEntry);
|
|
pEntry = NULL;
|
|
}
|
|
|
|
ReleaseNotificationList (pNotifyList);
|
|
}
|
|
else
|
|
{
|
|
PNOTIFY_LIST pNotifyListGlobal = NULL;
|
|
PNOTIFY_ENTRY_GLOBAL pEntryGlobal = NULL;
|
|
|
|
ReleaseNotificationList (pNotifyList);
|
|
|
|
//
|
|
// now check the global session notificaiton entry
|
|
//
|
|
Status = GetGlobalNotificationList(&pNotifyListGlobal);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
pEntryGlobal = GetHWndEntryFromGlobalList(pNotifyListGlobal, hWnd, SessionId, dwFlags);
|
|
if (pEntryGlobal)
|
|
{
|
|
ASSERT(pEntryGlobal->RefCount > 0);
|
|
ASSERT( !(pEntryGlobal->dwFlags & WTS_EVENT_NOTIFICATION) || pEntryGlobal->RefCount == 1);
|
|
|
|
pEntryGlobal->RefCount--;
|
|
if (pEntryGlobal->RefCount == 0 || !bDcrRef)
|
|
{
|
|
RemoveEntryList( &pEntryGlobal->Links );
|
|
if (pEntryGlobal->dwFlags & WTS_EVENT_NOTIFICATION)
|
|
{
|
|
CloseHandle((HANDLE)pEntryGlobal->hWnd);
|
|
}
|
|
|
|
MemFree(pEntryGlobal);
|
|
pEntryGlobal = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NOT_FOUND;
|
|
}
|
|
|
|
ReleaseNotificationList( pNotifyListGlobal );
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (Status);
|
|
}
|
|
|
|
NTSTATUS NotifySessionChange (PWINSTATION pWinStation, WPARAM wNotification)
|
|
{
|
|
return QueueNotificationRequest(pWinStation->SessionSerialNumber, pWinStation->LogonId, wNotification);
|
|
}
|
|
|
|
NTSTATUS NotifyLogon(PWINSTATION pWinStation)
|
|
{
|
|
return NotifySessionChange(pWinStation, WTS_SESSION_LOGON);
|
|
}
|
|
|
|
NTSTATUS NotifyLogoff(PWINSTATION pWinStation)
|
|
{
|
|
return NotifySessionChange(pWinStation, WTS_SESSION_LOGOFF);
|
|
}
|
|
|
|
NTSTATUS NotifyConnect (PWINSTATION pWinStation, BOOL bConsole)
|
|
{
|
|
return NotifySessionChange(pWinStation, bConsole ? WTS_CONSOLE_CONNECT : WTS_REMOTE_CONNECT);
|
|
}
|
|
|
|
NTSTATUS NotifyDisconnect (PWINSTATION pWinStation, BOOL bConsole)
|
|
{
|
|
return NotifySessionChange(pWinStation, bConsole ? WTS_CONSOLE_DISCONNECT : WTS_REMOTE_DISCONNECT);
|
|
}
|
|
|
|
NTSTATUS NofifyWelcomeOn (PWINSTATION pWinStation)
|
|
{
|
|
return NotifySessionChange(pWinStation, WTS_SESSION_LOCK);
|
|
}
|
|
|
|
NTSTATUS NotifyWelcomeOff (PWINSTATION pWinStation)
|
|
{
|
|
return NotifySessionChange(pWinStation, WTS_SESSION_UNLOCK);
|
|
}
|
|
|
|
NTSTATUS NotifyShadowChange (PWINSTATION pWinStation, BOOL bIsHelpAssistant)
|
|
{
|
|
UNREFERENCED_PARAMETER(bIsHelpAssistant); // for a new event later?
|
|
|
|
return NotifySessionChange(pWinStation, WTS_SESSION_REMOTE_CONTROL);
|
|
}
|
|
|
|
|
|
NTSTATUS SendNotificationToHwnd(PWINSTATION pWinstation, ULONG_PTR hWnd, ULONG SessionId, WPARAM wParam)
|
|
{
|
|
WINSTATION_APIMSG WMsg;
|
|
//
|
|
// now pupulate the WMSG for delievery.
|
|
//
|
|
WMsg.u.sMsg.Msg = WM_WTSSESSION_CHANGE;
|
|
WMsg.u.sMsg.wParam = wParam;
|
|
WMsg.ApiNumber = SMWinStationSendWindowMessage ;
|
|
WMsg.WaitForReply = FALSE;
|
|
WMsg.u.sMsg.dataBuffer = NULL;
|
|
WMsg.u.sMsg.bufferSize = 0;
|
|
WMsg.u.sMsg.lParam = SessionId;
|
|
WMsg.u.sMsg.hWnd = (HWND) hWnd ;
|
|
|
|
return SendWinStationCommand( pWinstation, &WMsg, 0);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS NotifyConsole (ULONG SessionId, ULONG SessionSerialNumber, WPARAM wParam)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
|
|
DWORD dwError;
|
|
PWINSTATION pWinStation=NULL;
|
|
|
|
|
|
Status = RemoveInvalidWindowsFromLists();
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
|
|
pWinStation = FindWinStationById(SessionId, FALSE);
|
|
|
|
//
|
|
// if we find the session we were looking for
|
|
// note: we must check for the serialnumber, as the session id is not unique.
|
|
//
|
|
if (pWinStation)
|
|
{
|
|
if (SessionSerialNumber == pWinStation->SessionSerialNumber)
|
|
{
|
|
PNOTIFY_LIST pConsoleList;
|
|
|
|
Status = GetNoficationListFromSessionId(pWinStation->LogonId, &pConsoleList, FALSE);
|
|
if (NT_SUCCESS(Status) && pConsoleList)
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
Head = &pConsoleList->ListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
|
|
{
|
|
PNOTIFY_ENTRY pEntry;
|
|
pEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY, Links );
|
|
ASSERT(pEntry);
|
|
ASSERT(!(pEntry->dwFlags & WTS_ALL_SESSION_NOTIFICATION));
|
|
|
|
if (IsBitSet(pEntry->dwMask, wParam))
|
|
{
|
|
if (pEntry->dwFlags & WTS_EVENT_NOTIFICATION)
|
|
{
|
|
SetEvent((HANDLE)pEntry->hWnd);
|
|
}
|
|
else
|
|
{
|
|
Status = SendNotificationToHwnd(pWinStation, pEntry->hWnd, SessionId, wParam);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
NTSDDBGPRINT(("conntfy.c - SendWinStationCommand failed, Status = %d.\n", Status));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateSessionState(pConsoleList, wParam);
|
|
|
|
ReleaseNotificationList( pConsoleList );
|
|
}
|
|
}
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
}
|
|
|
|
//
|
|
// now send notifications to windows registered for all session notificaitons.
|
|
//
|
|
{
|
|
PNOTIFY_LIST pNotifyListGlobal = NULL;
|
|
|
|
Status = GetGlobalNotificationList(&pNotifyListGlobal);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
Head = &pNotifyListGlobal->ListHead;
|
|
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
|
|
{
|
|
PNOTIFY_ENTRY_GLOBAL pEntryGlobal = NULL;
|
|
pEntryGlobal = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
|
|
ASSERT(pEntryGlobal);
|
|
ASSERT(pEntryGlobal->dwFlags & WTS_ALL_SESSION_NOTIFICATION);
|
|
|
|
if (IsBitSet(pEntryGlobal->dwMask, wParam))
|
|
{
|
|
if (pEntryGlobal->dwFlags & WTS_EVENT_NOTIFICATION)
|
|
{
|
|
SetEvent((HANDLE)pEntryGlobal->hWnd);
|
|
}
|
|
else
|
|
{
|
|
pWinStation = FindWinStationById(pEntryGlobal->SessionId, FALSE);
|
|
if (pWinStation)
|
|
{
|
|
if (!pWinStation->Terminating)
|
|
{
|
|
Status = SendNotificationToHwnd(pWinStation, pEntryGlobal->hWnd, SessionId, wParam);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
NTSDDBGPRINT(("conntfy.c - SendWinStationCommand failed, Status = %d.\n", Status));
|
|
}
|
|
}
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseNotificationList(pNotifyListGlobal);
|
|
}
|
|
else
|
|
{
|
|
NTSDDBGPRINT(("conntfy.c - Failed to get all session notification list - status = 0x%x.\n", Status));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// now lets notify SCM which will notify all the services registered for SERVICE_ACCEPT_SESSIONCHANGE
|
|
//
|
|
|
|
//
|
|
// logon logoff notifications for session 0 are sent by winlogon. rest are handled here.
|
|
//
|
|
if (SessionId != 0 || ( wParam != WTS_SESSION_LOGON && wParam != WTS_SESSION_LOGOFF))
|
|
{
|
|
|
|
WTSSESSION_NOTIFICATION wtsConsoleNotification;
|
|
wtsConsoleNotification.cbSize = sizeof(WTSSESSION_NOTIFICATION);
|
|
wtsConsoleNotification.dwSessionId = SessionId;
|
|
|
|
dwError = I_ScSendTSMessage(
|
|
SERVICE_CONTROL_SESSIONCHANGE, // op code
|
|
(DWORD)wParam, // event code,
|
|
wtsConsoleNotification.cbSize, // data size
|
|
(LPBYTE)&wtsConsoleNotification // data.
|
|
);
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS DestroyLock( PNOTIFY_LIST pNtfyList)
|
|
{
|
|
return RtlDeleteCriticalSection( &pNtfyList->ListLock );
|
|
}
|
|
|
|
NTSTATUS CreateLock (PNOTIFY_LIST pNtfyList)
|
|
{
|
|
return RtlInitializeCriticalSection( &pNtfyList->ListLock );
|
|
}
|
|
|
|
|
|
BOOL IsInvalidHWndList (PNOTIFY_LIST pNtfyList)
|
|
{
|
|
return (pNtfyList == &gNotifyLList.InvlidHwndList);
|
|
}
|
|
BOOL IsGlobalList(PNOTIFY_LIST pNtfyList)
|
|
{
|
|
return (pNtfyList == &gNotifyLList.GlobalList);
|
|
}
|
|
|
|
int GetListCount (LIST_ENTRY *pListHead)
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
int iCount = 0;
|
|
|
|
ASSERT(pListHead);
|
|
|
|
Head = pListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
|
|
{
|
|
iCount++;
|
|
}
|
|
|
|
return iCount;
|
|
}
|
|
|
|
PNOTIFY_ENTRY GetHWndEntryFromSessionList(PNOTIFY_LIST pNotifyList, ULONG_PTR hWnd, DWORD dwFlags)
|
|
{
|
|
|
|
PLIST_ENTRY Head = NULL;
|
|
PLIST_ENTRY Next = NULL;
|
|
PNOTIFY_ENTRY pEntry = NULL;
|
|
|
|
Head = &pNotifyList->ListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
|
|
{
|
|
pEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY, Links );
|
|
|
|
ASSERT(pEntry);
|
|
ASSERT(!(pEntry->dwFlags & WTS_EVENT_NOTIFICATION && pEntry->dwFlags & WTS_WINDOW_NOTIFICATION));
|
|
|
|
if (pEntry->hWnd == hWnd && pEntry->dwFlags & dwFlags)
|
|
{
|
|
return pEntry;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PNOTIFY_ENTRY_GLOBAL GetHWndEntryFromGlobalList(PNOTIFY_LIST pNotifyList, ULONG_PTR hWnd, ULONG SessionId, DWORD dwFlags)
|
|
{
|
|
PLIST_ENTRY Head = NULL;
|
|
PLIST_ENTRY Next = NULL;
|
|
PNOTIFY_ENTRY_GLOBAL pEntry = NULL;
|
|
|
|
Head = &pNotifyList->ListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
|
|
{
|
|
pEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
|
|
|
|
ASSERT(pEntry);
|
|
ASSERT(!(pEntry->dwFlags & WTS_EVENT_NOTIFICATION && pEntry->dwFlags & WTS_WINDOW_NOTIFICATION));
|
|
|
|
if (pEntry->hWnd == hWnd && SessionId == pEntry->SessionId && pEntry->dwFlags & dwFlags)
|
|
{
|
|
return pEntry;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// returns PNOTIFY_LIST list for the given session.
|
|
//
|
|
NTSTATUS GetNoficationListFromSessionId (ULONG SessionId, PNOTIFY_LIST *ppNotifyList, BOOL bKeepLListLocked)
|
|
{
|
|
PLIST_ENTRY Next, Head;
|
|
|
|
ASSERT(ppNotifyList);
|
|
|
|
*ppNotifyList = NULL;
|
|
|
|
// lock our list of lists.
|
|
ENTERCRIT(&gNotifyLList.ListLock);
|
|
|
|
|
|
|
|
Head = &gNotifyLList.ListHead;
|
|
Next = Head->Flink;
|
|
while (Head != Next)
|
|
{
|
|
PNOTIFY_LIST pNotifyList = CONTAINING_RECORD( Next, NOTIFY_LIST, Links );
|
|
|
|
ASSERT( pNotifyList );
|
|
|
|
//
|
|
// we always take gNotifyLList.ListLock first and then the Listlock
|
|
// therefore we must never have PNOTIFY_LIST.ListLock at this time.
|
|
//
|
|
ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != pNotifyList->ListLock.OwningThread );
|
|
|
|
|
|
if (pNotifyList->SessionId == SessionId)
|
|
{
|
|
//
|
|
// did we find more that 1 matching notify list ???, should never happen!
|
|
//
|
|
ASSERT(*ppNotifyList == NULL);
|
|
|
|
//
|
|
// ok we found the session list we were looking for
|
|
//
|
|
*ppNotifyList = pNotifyList;
|
|
|
|
#ifndef DBG
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
//
|
|
// if we have found the list we were looking for
|
|
//
|
|
if (*ppNotifyList)
|
|
{
|
|
//
|
|
// lock the list before returning.
|
|
//
|
|
ENTERCRIT(&(*ppNotifyList)->ListLock);
|
|
}
|
|
|
|
if (!(*ppNotifyList) || !bKeepLListLocked)
|
|
{
|
|
//
|
|
// unlock llist lock.
|
|
//
|
|
LEAVECRIT(&gNotifyLList.ListLock);
|
|
}
|
|
|
|
if (*ppNotifyList)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return STATUS_NO_SUCH_LOGON_SESSION;
|
|
}
|
|
}
|
|
|
|
void ReleaseNotificationList (PNOTIFY_LIST pNotifyList)
|
|
{
|
|
ASSERT(pNotifyList);
|
|
if (IsInvalidHWndList(pNotifyList))
|
|
{
|
|
// we must take invalid hwnd list before taking global list.
|
|
ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.GlobalList.ListLock).OwningThread );
|
|
// we must take invalid hwnd list before taking LList.
|
|
ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.ListLock).OwningThread );
|
|
}
|
|
else if (IsGlobalList(pNotifyList))
|
|
{
|
|
// we must take invalid hwnd list before taking LList.
|
|
ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.ListLock).OwningThread );
|
|
}
|
|
|
|
LEAVECRIT(&pNotifyList->ListLock);
|
|
}
|
|
|
|
NTSTATUS GetInvlidHwndList(PNOTIFY_LIST *ppConChgNtfy)
|
|
{
|
|
ASSERT(ppConChgNtfy);
|
|
|
|
// we must take invalid hwnd list before taking global list.
|
|
ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.GlobalList.ListLock).OwningThread );
|
|
|
|
// we must take invalid hwnd list before taking LList.
|
|
ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.ListLock).OwningThread );
|
|
|
|
*ppConChgNtfy = &gNotifyLList.InvlidHwndList;
|
|
ENTERCRIT(&(*ppConChgNtfy)->ListLock);
|
|
|
|
ASSERT(gNotifyLList.InvlidHwndList.Links.Blink == NULL);
|
|
ASSERT(gNotifyLList.InvlidHwndList.Links.Flink == NULL);
|
|
ASSERT(gNotifyLList.InvlidHwndList.SessionId == INVALID_SESSIONID);
|
|
ASSERT(gNotifyLList.InvlidHwndList.SessonSerialNumber == INVALID_SESSIONSERIAL);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS GetGlobalNotificationList(PNOTIFY_LIST *ppConChgNtfy)
|
|
{
|
|
ASSERT(ppConChgNtfy);
|
|
|
|
// we must LLIst after global list.
|
|
ASSERT( (HANDLE)LongToHandle( GetCurrentThreadId() ) != (gNotifyLList.ListLock).OwningThread );
|
|
|
|
*ppConChgNtfy = &gNotifyLList.GlobalList;
|
|
|
|
ENTERCRIT(&(*ppConChgNtfy)->ListLock);
|
|
|
|
ASSERT(gNotifyLList.GlobalList.Links.Blink == NULL);
|
|
ASSERT(gNotifyLList.GlobalList.Links.Flink == NULL);
|
|
ASSERT(gNotifyLList.GlobalList.SessionId == INVALID_SESSIONID);
|
|
ASSERT(gNotifyLList.GlobalList.SessonSerialNumber == INVALID_SESSIONSERIAL);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
NTSTATUS InitializeNotificationQueue ()
|
|
{
|
|
DWORD ThreadId;
|
|
NTSTATUS Status;
|
|
HANDLE hSessionNotifyThread;
|
|
|
|
InitializeListHead( &gNotifyLList.RequestQueue.ListHead);
|
|
|
|
gNotifyLList.RequestQueue.hNotificationEvent = CreateEvent(
|
|
NULL, // SD
|
|
FALSE, // reset type
|
|
FALSE, // initial state
|
|
NULL // object name
|
|
);
|
|
|
|
if (gNotifyLList.RequestQueue.hNotificationEvent == NULL)
|
|
{
|
|
// we failed to create event.
|
|
// return GetLastError()
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
Status = RtlInitializeCriticalSection( &gNotifyLList.RequestQueue.ListLock );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
CloseHandle(gNotifyLList.RequestQueue.hNotificationEvent);
|
|
gNotifyLList.RequestQueue.hNotificationEvent = NULL;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// now create thread for notifications.
|
|
//
|
|
hSessionNotifyThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)NotificationQueueWorker,
|
|
NULL,
|
|
0,
|
|
&ThreadId);
|
|
|
|
//
|
|
// Just close it, we can do without this handle.
|
|
//
|
|
if( hSessionNotifyThread )
|
|
{
|
|
CloseHandle( hSessionNotifyThread );
|
|
}
|
|
else
|
|
{
|
|
|
|
RtlDeleteCriticalSection( &gNotifyLList.RequestQueue.ListLock );
|
|
|
|
CloseHandle(gNotifyLList.RequestQueue.hNotificationEvent);
|
|
gNotifyLList.RequestQueue.hNotificationEvent = NULL;
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
void LockNotificationQueue()
|
|
{
|
|
ENTERCRIT(&gNotifyLList.RequestQueue.ListLock);
|
|
}
|
|
|
|
void UnLockNotificationQueue()
|
|
{
|
|
LEAVECRIT(&gNotifyLList.RequestQueue.ListLock);
|
|
}
|
|
|
|
|
|
//
|
|
// Queues a notification entry
|
|
//
|
|
NTSTATUS QueueNotificationRequest(ULONG SessionSerialNumber, ULONG SessionId, WPARAM notification)
|
|
{
|
|
PNOTIFICATION_REQUEST pRequest = NULL;
|
|
pRequest = MemAlloc( sizeof(NOTIFICATION_REQUEST) );
|
|
|
|
if (!pRequest)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
pRequest->SessonSerialNumber = SessionSerialNumber;
|
|
pRequest->SessionId = SessionId;
|
|
pRequest->NotificationCode = notification;
|
|
|
|
// now lock the queue
|
|
LockNotificationQueue();
|
|
InsertHeadList(&gNotifyLList.RequestQueue.ListHead, &pRequest->Links);
|
|
UnLockNotificationQueue();
|
|
|
|
// let the waiting thread process this notification.
|
|
PulseEvent(gNotifyLList.RequestQueue.hNotificationEvent);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// takes out a notification entry from queue.
|
|
//
|
|
PNOTIFICATION_REQUEST UnQueueNotificationRequest()
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
PNOTIFICATION_REQUEST pRequest = NULL;
|
|
|
|
//
|
|
// Remove a request from the list.
|
|
//
|
|
LockNotificationQueue();
|
|
if (!IsListEmpty(&gNotifyLList.RequestQueue.ListHead))
|
|
{
|
|
pEntry = RemoveTailList(&gNotifyLList.RequestQueue.ListHead);
|
|
pRequest = CONTAINING_RECORD(pEntry, NOTIFICATION_REQUEST, Links);
|
|
}
|
|
|
|
UnLockNotificationQueue();
|
|
|
|
return pRequest;
|
|
}
|
|
|
|
|
|
// This thread is a helper for the next function. We do this because the
|
|
// compiler defies reason by insisting not all control paths return a value.
|
|
VOID NotificationQueueWorkerEx()
|
|
{
|
|
PNOTIFICATION_REQUEST pRequest = NULL;
|
|
|
|
for(;;)
|
|
{
|
|
WaitForSingleObject(gNotifyLList.RequestQueue.hNotificationEvent, INFINITE); // wait for the event to be signaled.
|
|
|
|
while ((pRequest = UnQueueNotificationRequest()) != NULL)
|
|
{
|
|
if (!pRequest->NotificationCode)
|
|
{
|
|
ASSERT(FALSE);
|
|
// this is not a real notificaiton request.
|
|
// this request is for session removal.
|
|
// RemoveGlobalNotification(pRequest->SessionId, pRequest->SessonSerialNumber);
|
|
}
|
|
else
|
|
{
|
|
NotifyConsole (pRequest->SessionId, pRequest->SessonSerialNumber, pRequest->NotificationCode);
|
|
}
|
|
|
|
MemFree(pRequest);
|
|
pRequest = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// this thread takes a notification request from queue and executes it.
|
|
// this thread gets signaled when a new item is added to the queue.
|
|
//
|
|
DWORD NotificationQueueWorker(LPVOID ThreadParameter)
|
|
{
|
|
UNREFERENCED_PARAMETER(ThreadParameter);
|
|
|
|
NotificationQueueWorkerEx();
|
|
|
|
return 0;
|
|
}
|
|
|
|
NTSTATUS SetLockedState (PWINSTATION pWinStation, BOOL bLocked)
|
|
{
|
|
ASSERT(pWinStation);
|
|
|
|
if (bLocked)
|
|
{
|
|
return NofifyWelcomeOn (pWinStation);
|
|
}
|
|
else
|
|
{
|
|
return NotifyWelcomeOff (pWinStation);
|
|
}
|
|
}
|
|
|
|
NTSTATUS GetLockedState (PWINSTATION pWinStation, BOOL *pbLocked)
|
|
{
|
|
NTSTATUS Status;
|
|
PNOTIFY_LIST pNotifyList;
|
|
|
|
ASSERT(pbLocked);
|
|
ASSERT(pWinStation);
|
|
|
|
Status = GetNoficationListFromSessionId(pWinStation->LogonId, &pNotifyList, FALSE);
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return (Status);
|
|
}
|
|
|
|
*pbLocked = pNotifyList->SessionState.bLocked;
|
|
ReleaseNotificationList(pNotifyList);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
/*
|
|
NTSTATUS GetSessionState (PWINSTATION pWinStation, WTSSESSION_STATE *pSessionState)
|
|
{
|
|
NTSTATUS Status;
|
|
PNOTIFY_LIST pNotifyList;
|
|
|
|
ASSERT(pSessionState);
|
|
ASSERT(pWinStation);
|
|
|
|
Status = GetNoficationListFromSessionId(pWinStation->LogonId, &pNotifyList, FALSE);
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return (Status);
|
|
}
|
|
|
|
*pSessionState = pNotifyList->SessionState;
|
|
ReleaseNotificationList(pNotifyList);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
*/
|
|
|
|
NTSTATUS RemoveBadHwnd(ULONG_PTR hWnd, ULONG SessionId)
|
|
{
|
|
PNOTIFY_ENTRY_GLOBAL pInvalidHwndEntry;
|
|
PNOTIFY_LIST pInvalidHwndList;
|
|
NTSTATUS Status;
|
|
|
|
Status = GetInvlidHwndList(&pInvalidHwndList);
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return (Status);
|
|
}
|
|
|
|
pInvalidHwndEntry = GetHWndEntryFromGlobalList(pInvalidHwndList, hWnd, SessionId, WTS_WINDOW_NOTIFICATION);
|
|
//
|
|
// this entry must not already exist in invalid list.
|
|
//
|
|
if(!pInvalidHwndEntry)
|
|
{
|
|
// it alreay exists in our list.
|
|
pInvalidHwndEntry = MemAlloc(sizeof(NOTIFY_ENTRY_GLOBAL));
|
|
if (pInvalidHwndEntry)
|
|
{
|
|
pInvalidHwndEntry->hWnd = hWnd;
|
|
pInvalidHwndEntry->SessionId = SessionId;
|
|
pInvalidHwndEntry->dwFlags = WTS_WINDOW_NOTIFICATION;
|
|
pInvalidHwndEntry->RefCount = 0xFFFFFFFF;
|
|
pInvalidHwndEntry->dwMask = 0xFFFFFFFF;
|
|
|
|
InsertHeadList(&pInvalidHwndList->ListHead, &pInvalidHwndEntry->Links);
|
|
}
|
|
}
|
|
|
|
ReleaseNotificationList( pInvalidHwndList );
|
|
|
|
if (pInvalidHwndEntry)
|
|
return STATUS_SUCCESS;
|
|
else
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
NTSTATUS RemoveBadEvents(DWORD SessionId)
|
|
{
|
|
PNOTIFY_LIST pListGlobal = NULL;
|
|
PLIST_ENTRY Next, Head;
|
|
PNOTIFY_LIST pNotifyList = NULL;
|
|
|
|
NTSTATUS Status = GetGlobalNotificationList(&pListGlobal);
|
|
|
|
if (NT_SUCCESS( Status ))
|
|
{
|
|
Head = &pListGlobal->ListHead;
|
|
Next = Head->Flink;
|
|
while (Head != Next)
|
|
{
|
|
PNOTIFY_ENTRY_GLOBAL pEntryGlobal = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
|
|
Next = Next->Flink;
|
|
ASSERT(pEntryGlobal);
|
|
if ((pEntryGlobal->SessionId == SessionId) && (pEntryGlobal->dwFlags & WTS_EVENT_NOTIFICATION))
|
|
{
|
|
OBJECT_BASIC_INFORMATION Obi;
|
|
Status = NtQueryObject(
|
|
(HANDLE)pEntryGlobal->hWnd,
|
|
ObjectBasicInformation,
|
|
&Obi,
|
|
sizeof (OBJECT_BASIC_INFORMATION),
|
|
NULL
|
|
);
|
|
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
ASSERT(Obi.HandleCount >= 1);
|
|
if (Obi.HandleCount == 1)
|
|
{
|
|
//
|
|
// its just us referencing this event.
|
|
// let it go.
|
|
//
|
|
RemoveEntryList( &pEntryGlobal->Links );
|
|
CloseHandle((HANDLE)pEntryGlobal->hWnd);
|
|
MemFree(pEntryGlobal);
|
|
pEntryGlobal = NULL;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NTSDDBGPRINT(("conntfy.c - NtQueryObject failed, Status = %d.\n", Status));
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseNotificationList(pListGlobal);
|
|
pListGlobal = NULL;
|
|
|
|
}
|
|
else
|
|
{
|
|
NTSDDBGPRINT(("conntfy.c - GetGlobalNotificationList failed, Status = %d.\n", Status));
|
|
}
|
|
|
|
|
|
Status = GetNoficationListFromSessionId( SessionId, &pNotifyList, FALSE);
|
|
if (NT_SUCCESS( Status ))
|
|
{
|
|
Head = &pNotifyList->ListHead;
|
|
Next = Head->Flink;
|
|
while (Head != Next)
|
|
{
|
|
PNOTIFY_ENTRY pEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY, Links );
|
|
Next = Next->Flink;
|
|
ASSERT(pEntry);
|
|
if (pEntry->dwFlags & WTS_EVENT_NOTIFICATION)
|
|
{
|
|
OBJECT_BASIC_INFORMATION Obi;
|
|
Status = NtQueryObject(
|
|
(HANDLE)pEntry->hWnd,
|
|
ObjectBasicInformation,
|
|
&Obi,
|
|
sizeof (OBJECT_BASIC_INFORMATION),
|
|
NULL
|
|
);
|
|
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
ASSERT(Obi.HandleCount >= 1);
|
|
if (Obi.HandleCount == 1)
|
|
{
|
|
//
|
|
// its just us referencing this event.
|
|
// let it go.
|
|
//
|
|
RemoveEntryList( &pEntry->Links );
|
|
CloseHandle((HANDLE)pEntry->hWnd);
|
|
MemFree(pEntry);
|
|
pEntry = NULL;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseNotificationList(pNotifyList);
|
|
pNotifyList = NULL;
|
|
}
|
|
else
|
|
{
|
|
NTSDDBGPRINT(("conntfy.c - GetNoficationListFromSessionId failed, Status = %d.\n", Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS RemoveInvalidWindowsFromLists()
|
|
{
|
|
PNOTIFY_LIST pInvalidHwndList;
|
|
PLIST_ENTRY Next, Head;
|
|
NTSTATUS Status;
|
|
|
|
Status = GetInvlidHwndList(&pInvalidHwndList);
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return (Status);
|
|
}
|
|
|
|
Head = &pInvalidHwndList->ListHead;
|
|
Next = Head->Flink;
|
|
while (Head != Next)
|
|
{
|
|
PNOTIFY_ENTRY_GLOBAL pInvalidHwndEntry = CONTAINING_RECORD( Next, NOTIFY_ENTRY_GLOBAL, Links );
|
|
Next = Next->Flink;
|
|
ASSERT(pInvalidHwndEntry);
|
|
Status = UnRegisterConsoleNotificationInternal (pInvalidHwndEntry->hWnd, pInvalidHwndEntry->SessionId, FALSE, WTS_WINDOW_NOTIFICATION);
|
|
|
|
// we are done removing this invalid hwnd entry from our lists.
|
|
RemoveEntryList( &pInvalidHwndEntry->Links );
|
|
MemFree(pInvalidHwndEntry);
|
|
pInvalidHwndEntry = NULL;
|
|
}
|
|
|
|
ReleaseNotificationList(pInvalidHwndList);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
our order of locks is
|
|
|
|
0. Invalid Hwnd List.
|
|
1. Global Notification List
|
|
2. Winstation
|
|
3. List of Lists lock.
|
|
4. Session Notification List
|
|
*/
|
|
|
|
//#ifdef MAKARANDS_HIGHER_WARNING_LEVEL
|
|
#pragma warning(pop)
|
|
//#endif
|