reactos/win32ss/user/ntuser/main.c
Hermès Bélusca-Maïto a8e7defb01
[WIN32SS:USER] Some minimal work and fixes concerning message queues timeouts.
CORE-15147

- Rename CLIENTTHREADINFO::tickLastMsgChecked into timeLastRead as
  documented in https://reactos.org/wiki/Techwiki:Win32k/CLIENTTHREADINFO .
  This is the last time the message queue was read.

- This is the structure member one must compare against the current tick
  count timestamp in order to heuristically determine whether a message
  queue thread is hung!! Fix MsqIsHung() in accordance, add extra debug
  logging in order to help us determining which of our code present
  regular GUI hangs, and add as well an extra "TimeOut" parameter so as
  not to hardcode a fixed value within that function but instead
  allowing its caller to specify possible different values.

- THREADINFO::timeLast is on the contrary the last message time stamp,
  and will definitively differ from CLIENTTHREADINFO::timeLastRead .
  It should only be used for information purposes!

- Accordingly, in NtUserGetThreadState()::THREADSTATE_UPTIMELASTREAD
  and in InitThreadCallback(), only (re-)initialize the timeLastRead
  member of the CLIENTTHREADINFO structure of the THREADINFO of interest.

- In co_IntPeekMessage(), update more often the timeLastRead timestamp
  whenever the current message queue has been read (but NOT timeLast!!
  That one will be updated ONLY WHEN a message is found!).

- In co_IntSendMessageTimeoutSingle() first check whether the window to
  which we send the message is being destroyed, before checking for
  queue hangs etc. Collapse the logic checks for queue hang and increase
  the hang timeout check to 4 times MSQ_HUNG (== 4 * 5 seconds) and
  display a debug trace.
2019-12-29 16:14:15 +01:00

1007 lines
29 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Win32k subsystem
* PURPOSE: Driver entry and initialization of win32k
* FILE: win32ss/user/ntuser/main.c
* PROGRAMER:
*/
#include <win32k.h>
#include <napi.h>
#define NDEBUG
#include <debug.h>
#include <kdros.h>
HANDLE hModuleWin;
NTSTATUS ExitProcessCallback(PEPROCESS Process);
NTSTATUS NTAPI ExitThreadCallback(PETHREAD Thread);
// TODO: Should be moved to some GDI header
NTSTATUS GdiProcessCreate(PEPROCESS Process);
NTSTATUS GdiProcessDestroy(PEPROCESS Process);
NTSTATUS GdiThreadCreate(PETHREAD Thread);
NTSTATUS GdiThreadDestroy(PETHREAD Thread);
PSERVERINFO gpsi = NULL; // Global User Server Information.
USHORT gusLanguageID;
PPROCESSINFO ppiScrnSaver;
PPROCESSINFO gppiList = NULL;
extern ULONG_PTR Win32kSSDT[];
extern UCHAR Win32kSSPT[];
extern ULONG Win32kNumberOfSysCalls;
#if DBG
void
NTAPI
DbgPreServiceHook(ULONG ulSyscallId, PULONG_PTR pulArguments)
{
GdiDbgPreServiceHook(ulSyscallId, pulArguments);
UserDbgPreServiceHook(ulSyscallId, pulArguments);
}
ULONG_PTR
NTAPI
DbgPostServiceHook(ULONG ulSyscallId, ULONG_PTR ulResult)
{
ulResult = GdiDbgPostServiceHook(ulSyscallId, ulResult);
ulResult = UserDbgPostServiceHook(ulSyscallId, ulResult);
return ulResult;
}
#endif
NTSTATUS
AllocW32Process(IN PEPROCESS Process,
OUT PPROCESSINFO* W32Process)
{
PPROCESSINFO ppiCurrent;
TRACE_CH(UserProcess, "In AllocW32Process(0x%p)\n", Process);
/* Check that we were not called with an already existing Win32 process info */
ppiCurrent = PsGetProcessWin32Process(Process);
if (ppiCurrent) return STATUS_SUCCESS;
/* Allocate a new Win32 process info */
ppiCurrent = ExAllocatePoolWithTag(NonPagedPool,
sizeof(*ppiCurrent),
USERTAG_PROCESSINFO);
if (ppiCurrent == NULL)
{
ERR_CH(UserProcess, "Failed to allocate ppi for PID:0x%lx\n",
HandleToUlong(Process->UniqueProcessId));
return STATUS_NO_MEMORY;
}
TRACE_CH(UserProcess, "Allocated ppi 0x%p for PID:0x%lx\n",
ppiCurrent, HandleToUlong(Process->UniqueProcessId));
RtlZeroMemory(ppiCurrent, sizeof(*ppiCurrent));
PsSetProcessWin32Process(Process, ppiCurrent, NULL);
IntReferenceProcessInfo(ppiCurrent);
*W32Process = ppiCurrent;
return STATUS_SUCCESS;
}
/*
* Called from IntDereferenceProcessInfo
*/
VOID
UserDeleteW32Process(
_Pre_notnull_ __drv_freesMem(Mem) PPROCESSINFO ppiCurrent)
{
if (ppiCurrent->InputIdleEvent)
{
/* Free the allocated memory */
ExFreePoolWithTag(ppiCurrent->InputIdleEvent, USERTAG_EVENT);
}
/* Close the startup desktop */
if (ppiCurrent->rpdeskStartup)
ObDereferenceObject(ppiCurrent->rpdeskStartup);
#if DBG
if (DBG_IS_CHANNEL_ENABLED(ppiCurrent, DbgChUserObj, WARN_LEVEL))
{
TRACE_PPI(ppiCurrent, UserObj, "Dumping user handles now that process info %p is gets freed.\n", ppiCurrent);
DbgUserDumpHandleTable();
}
#endif
/* Free the PROCESSINFO */
ExFreePoolWithTag(ppiCurrent, USERTAG_PROCESSINFO);
}
NTSTATUS
UserProcessCreate(PEPROCESS Process)
{
PPROCESSINFO ppiCurrent = PsGetProcessWin32Process(Process);
ASSERT(ppiCurrent);
InitializeListHead(&ppiCurrent->DriverObjListHead);
ExInitializeFastMutex(&ppiCurrent->DriverObjListLock);
{
PKEVENT Event;
/* Allocate memory for the event structure */
Event = ExAllocatePoolWithTag(NonPagedPool,
sizeof(*Event),
USERTAG_EVENT);
if (Event)
{
/* Initialize the kernel event */
KeInitializeEvent(Event,
SynchronizationEvent,
FALSE);
}
else
{
/* Out of memory */
DPRINT("CreateEvent() failed\n");
KeBugCheck(0);
}
/* Set the event */
ppiCurrent->InputIdleEvent = Event;
KeInitializeEvent(ppiCurrent->InputIdleEvent, NotificationEvent, FALSE);
}
ppiCurrent->peProcess = Process;
ppiCurrent->W32Pid = HandleToUlong(PsGetProcessId(Process));
/* Setup process flags */
ppiCurrent->W32PF_flags |= W32PF_PROCESSCONNECTED;
if (Process->Peb->ProcessParameters &&
(Process->Peb->ProcessParameters->WindowFlags & STARTF_SCREENSAVER))
{
ppiScrnSaver = ppiCurrent;
ppiCurrent->W32PF_flags |= W32PF_SCREENSAVER;
}
// FIXME: check if this process is allowed.
ppiCurrent->W32PF_flags |= W32PF_ALLOWFOREGROUNDACTIVATE; // Starting application will get it toggled off.
return STATUS_SUCCESS;
}
NTSTATUS
UserProcessDestroy(PEPROCESS Process)
{
PPROCESSINFO ppiCurrent = PsGetProcessWin32Process(Process);
ASSERT(ppiCurrent);
if (ppiScrnSaver == ppiCurrent)
ppiScrnSaver = NULL;
/* Destroy user objects */
UserDestroyObjectsForOwner(gHandleTable, ppiCurrent);
TRACE_CH(UserProcess, "Freeing ppi 0x%p\n", ppiCurrent);
#if DBG
if (DBG_IS_CHANNEL_ENABLED(ppiCurrent, DbgChUserObj, WARN_LEVEL))
{
TRACE_CH(UserObj, "Dumping user handles at the end of the process %s (Info %p).\n",
ppiCurrent->peProcess->ImageFileName, ppiCurrent);
DbgUserDumpHandleTable();
}
#endif
/* Remove it from the list of GUI apps */
co_IntGraphicsCheck(FALSE);
/*
* Deregister logon application automatically
*/
if (gpidLogon == ppiCurrent->peProcess->UniqueProcessId)
gpidLogon = 0;
/* Close the current window station */
UserSetProcessWindowStation(NULL);
if (gppiInputProvider == ppiCurrent) gppiInputProvider = NULL;
if (ppiCurrent->hdeskStartup)
{
ZwClose(ppiCurrent->hdeskStartup);
ppiCurrent->hdeskStartup = NULL;
}
/* Clean up the process icon cache */
IntCleanupCurIconCache(ppiCurrent);
return STATUS_SUCCESS;
}
NTSTATUS
InitProcessCallback(PEPROCESS Process)
{
NTSTATUS Status;
PPROCESSINFO ppiCurrent;
PVOID KernelMapping = NULL, UserMapping = NULL;
/* We might be called with an already allocated win32 process */
ppiCurrent = PsGetProcessWin32Process(Process);
if (ppiCurrent != NULL)
{
/* There is no more to do for us (this is a success code!) */
return STATUS_ALREADY_WIN32;
}
// if (ppiCurrent->W32PF_flags & W32PF_PROCESSCONNECTED)
// return STATUS_ALREADY_WIN32;
/* Allocate a new Win32 process info */
Status = AllocW32Process(Process, &ppiCurrent);
if (!NT_SUCCESS(Status))
{
ERR_CH(UserProcess, "Failed to allocate ppi for PID:0x%lx\n",
HandleToUlong(Process->UniqueProcessId));
return Status;
}
#if DBG
DbgInitDebugChannels();
#if defined(KDBG)
KdRosRegisterCliCallback(DbgGdiKdbgCliCallback);
#endif
#endif
/* Map the global user heap into the process */
Status = MapGlobalUserHeap(Process, &KernelMapping, &UserMapping);
if (!NT_SUCCESS(Status))
{
TRACE_CH(UserProcess, "Failed to map the global heap! 0x%x\n", Status);
goto error;
}
TRACE_CH(UserProcess, "InitProcessCallback -- We have KernelMapping 0x%p and UserMapping 0x%p with delta = 0x%x\n",
KernelMapping, UserMapping, (ULONG_PTR)KernelMapping - (ULONG_PTR)UserMapping);
/* Initialize USER process info */
Status = UserProcessCreate(Process);
if (!NT_SUCCESS(Status))
{
ERR_CH(UserProcess, "UserProcessCreate failed, Status 0x%08lx\n", Status);
goto error;
}
/* Initialize GDI process info */
Status = GdiProcessCreate(Process);
if (!NT_SUCCESS(Status))
{
ERR_CH(UserProcess, "GdiProcessCreate failed, Status 0x%08lx\n", Status);
goto error;
}
/* Add the process to the global list */
ppiCurrent->ppiNext = gppiList;
gppiList = ppiCurrent;
return STATUS_SUCCESS;
error:
ERR_CH(UserProcess, "InitProcessCallback failed! Freeing ppi 0x%p for PID:0x%lx\n",
ppiCurrent, HandleToUlong(Process->UniqueProcessId));
ExitProcessCallback(Process);
return Status;
}
NTSTATUS
ExitProcessCallback(PEPROCESS Process)
{
PPROCESSINFO ppiCurrent, *pppi;
/* Get the Win32 Process */
ppiCurrent = PsGetProcessWin32Process(Process);
ASSERT(ppiCurrent);
ASSERT(ppiCurrent->peProcess == Process);
TRACE_CH(UserProcess, "Destroying ppi 0x%p\n", ppiCurrent);
ppiCurrent->W32PF_flags |= W32PF_TERMINATED;
/* Remove it from the list */
pppi = &gppiList;
while (*pppi != NULL && *pppi != ppiCurrent)
{
pppi = &(*pppi)->ppiNext;
}
ASSERT(*pppi == ppiCurrent);
*pppi = ppiCurrent->ppiNext;
/* Cleanup GDI info */
GdiProcessDestroy(Process);
/* Cleanup USER info */
UserProcessDestroy(Process);
/* The process is dying */
PsSetProcessWin32Process(Process, NULL, ppiCurrent);
ppiCurrent->peProcess = NULL;
/* Finally, dereference */
IntDereferenceProcessInfo(ppiCurrent);
return STATUS_SUCCESS;
}
NTSTATUS
APIENTRY
Win32kProcessCallback(PEPROCESS Process,
BOOLEAN Initialize)
{
NTSTATUS Status;
ASSERT(Process->Peb);
TRACE_CH(UserProcess, "Win32kProcessCallback -->\n");
UserEnterExclusive();
if (Initialize)
{
Status = InitProcessCallback(Process);
}
else
{
Status = ExitProcessCallback(Process);
}
UserLeave();
TRACE_CH(UserProcess, "<-- Win32kProcessCallback\n");
return Status;
}
NTSTATUS
AllocW32Thread(IN PETHREAD Thread,
OUT PTHREADINFO* W32Thread)
{
PTHREADINFO ptiCurrent;
TRACE_CH(UserThread, "In AllocW32Thread(0x%p)\n", Thread);
/* Check that we were not called with an already existing Win32 thread info */
ptiCurrent = PsGetThreadWin32Thread(Thread);
NT_ASSERT(ptiCurrent == NULL);
/* Allocate a new Win32 thread info */
ptiCurrent = ExAllocatePoolWithTag(NonPagedPool,
sizeof(*ptiCurrent),
USERTAG_THREADINFO);
if (ptiCurrent == NULL)
{
ERR_CH(UserThread, "Failed to allocate pti for TID:0x%lx\n",
HandleToUlong(Thread->Cid.UniqueThread));
return STATUS_NO_MEMORY;
}
TRACE_CH(UserThread, "Allocated pti 0x%p for TID:0x%lx\n",
ptiCurrent, HandleToUlong(Thread->Cid.UniqueThread));
RtlZeroMemory(ptiCurrent, sizeof(*ptiCurrent));
PsSetThreadWin32Thread(Thread, ptiCurrent, NULL);
ObReferenceObject(Thread);
IntReferenceThreadInfo(ptiCurrent);
*W32Thread = ptiCurrent;
return STATUS_SUCCESS;
}
/*
* Called from IntDereferenceThreadInfo
*/
VOID
UserDeleteW32Thread(PTHREADINFO pti)
{
PPROCESSINFO ppi = pti->ppi;
TRACE_CH(UserThread, "UserDeleteW32Thread pti 0x%p\n",pti);
/* Free the message queue */
if (pti->MessageQueue)
{
MsqDestroyMessageQueue(pti);
}
MsqCleanupThreadMsgs(pti);
ObDereferenceObject(pti->pEThread);
ExFreePoolWithTag(pti, USERTAG_THREADINFO);
IntDereferenceProcessInfo(ppi);
}
NTSTATUS
UserThreadCreate(PETHREAD Thread)
{
return STATUS_SUCCESS;
}
NTSTATUS
UserThreadDestroy(PETHREAD Thread)
{
return STATUS_SUCCESS;
}
NTSTATUS NTAPI
InitThreadCallback(PETHREAD Thread)
{
PEPROCESS Process;
PCLIENTINFO pci;
PTHREADINFO ptiCurrent;
int i;
NTSTATUS Status = STATUS_SUCCESS;
PTEB pTeb;
PRTL_USER_PROCESS_PARAMETERS ProcessParams;
Process = Thread->ThreadsProcess;
pTeb = NtCurrentTeb();
ASSERT(pTeb);
ProcessParams = pTeb->ProcessEnvironmentBlock->ProcessParameters;
/* Allocate a new Win32 thread info */
Status = AllocW32Thread(Thread, &ptiCurrent);
if (!NT_SUCCESS(Status))
{
ERR_CH(UserThread, "Failed to allocate pti for TID:0x%lx\n",
HandleToUlong(Thread->Cid.UniqueThread));
return Status;
}
/* Initialize the THREADINFO */
ptiCurrent->pEThread = Thread;
ptiCurrent->ppi = PsGetProcessWin32Process(Process);
IntReferenceProcessInfo(ptiCurrent->ppi);
pTeb->Win32ThreadInfo = ptiCurrent;
ptiCurrent->pClientInfo = (PCLIENTINFO)pTeb->Win32ClientInfo;
ptiCurrent->pcti = &ptiCurrent->cti;
/* Mark the process as having threads */
ptiCurrent->ppi->W32PF_flags |= W32PF_THREADCONNECTED;
InitializeListHead(&ptiCurrent->WindowListHead);
InitializeListHead(&ptiCurrent->W32CallbackListHead);
InitializeListHead(&ptiCurrent->PostedMessagesListHead);
InitializeListHead(&ptiCurrent->SentMessagesListHead);
InitializeListHead(&ptiCurrent->PtiLink);
for (i = 0; i < NB_HOOKS; i++)
{
InitializeListHead(&ptiCurrent->aphkStart[i]);
}
ptiCurrent->ptiSibling = ptiCurrent->ppi->ptiList;
ptiCurrent->ppi->ptiList = ptiCurrent;
ptiCurrent->ppi->cThreads++;
ptiCurrent->hEventQueueClient = NULL;
Status = ZwCreateEvent(&ptiCurrent->hEventQueueClient, EVENT_ALL_ACCESS,
NULL, SynchronizationEvent, FALSE);
if (!NT_SUCCESS(Status))
{
ERR_CH(UserThread, "Event creation failed, Status 0x%08x.\n", Status);
goto error;
}
Status = ObReferenceObjectByHandle(ptiCurrent->hEventQueueClient, 0,
*ExEventObjectType, UserMode,
(PVOID*)&ptiCurrent->pEventQueueServer, NULL);
if (!NT_SUCCESS(Status))
{
ERR_CH(UserThread, "Failed referencing the event object, Status 0x%08x.\n", Status);
ObCloseHandle(ptiCurrent->hEventQueueClient, UserMode);
ptiCurrent->hEventQueueClient = NULL;
goto error;
}
ptiCurrent->pcti->timeLastRead = EngGetTickCount32();
ptiCurrent->MessageQueue = MsqCreateMessageQueue(ptiCurrent);
if (ptiCurrent->MessageQueue == NULL)
{
ERR_CH(UserThread, "Failed to allocate message loop\n");
Status = STATUS_NO_MEMORY;
goto error;
}
ptiCurrent->KeyboardLayout = W32kGetDefaultKeyLayout();
if (ptiCurrent->KeyboardLayout)
UserReferenceObject(ptiCurrent->KeyboardLayout);
ptiCurrent->TIF_flags &= ~TIF_INCLEANUP;
// FIXME: Flag SYSTEM threads with... TIF_SYSTEMTHREAD !!
/* CSRSS threads have some special features */
if (Process == gpepCSRSS || !gpepCSRSS)
ptiCurrent->TIF_flags = TIF_CSRSSTHREAD | TIF_DONTATTACHQUEUE;
/* Initialize the CLIENTINFO */
pci = (PCLIENTINFO)pTeb->Win32ClientInfo;
RtlZeroMemory(pci, sizeof(*pci));
pci->ppi = ptiCurrent->ppi;
pci->fsHooks = ptiCurrent->fsHooks;
pci->dwTIFlags = ptiCurrent->TIF_flags;
if (ptiCurrent->KeyboardLayout)
{
pci->hKL = ptiCurrent->KeyboardLayout->hkl;
pci->CodePage = ptiCurrent->KeyboardLayout->CodePage;
}
/* Need to pass the user Startup Information to the current process. */
if ( ProcessParams )
{
if ( ptiCurrent->ppi->usi.cb == 0 ) // Not initialized yet.
{
if ( ProcessParams->WindowFlags != 0 ) // Need window flags set.
{
ptiCurrent->ppi->usi.cb = sizeof(USERSTARTUPINFO);
ptiCurrent->ppi->usi.dwX = ProcessParams->StartingX;
ptiCurrent->ppi->usi.dwY = ProcessParams->StartingY;
ptiCurrent->ppi->usi.dwXSize = ProcessParams->CountX;
ptiCurrent->ppi->usi.dwYSize = ProcessParams->CountY;
ptiCurrent->ppi->usi.dwFlags = ProcessParams->WindowFlags;
ptiCurrent->ppi->usi.wShowWindow = (WORD)ProcessParams->ShowWindowFlags;
}
}
}
/*
* Assign a default window station and desktop to the process.
* Do not try to open a desktop or window station before the very first
* (interactive) window station has been created by Winlogon.
*/
if (!(ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)) &&
ptiCurrent->ppi->hdeskStartup == NULL &&
InputWindowStation != NULL)
{
HWINSTA hWinSta = NULL;
HDESK hDesk = NULL;
UNICODE_STRING DesktopPath;
PDESKTOP pdesk;
/*
* Inherit the thread desktop and process window station (if not yet inherited)
* from the process startup info structure. See documentation of CreateProcess().
*/
Status = STATUS_UNSUCCESSFUL;
if (ProcessParams && ProcessParams->DesktopInfo.Length > 0)
{
Status = IntSafeCopyUnicodeStringTerminateNULL(&DesktopPath, &ProcessParams->DesktopInfo);
}
if (!NT_SUCCESS(Status))
{
RtlInitUnicodeString(&DesktopPath, NULL);
}
Status = IntResolveDesktop(Process,
&DesktopPath,
!!(ProcessParams->WindowFlags & STARTF_INHERITDESKTOP),
&hWinSta,
&hDesk);
if (DesktopPath.Buffer)
ExFreePoolWithTag(DesktopPath.Buffer, TAG_STRING);
if (!NT_SUCCESS(Status))
{
ERR_CH(UserThread, "Failed to assign default desktop and winsta to process\n");
goto error;
}
if (!UserSetProcessWindowStation(hWinSta))
{
Status = STATUS_UNSUCCESSFUL;
ERR_CH(UserThread, "Failed to set initial process winsta\n");
goto error;
}
/* Validate the new desktop */
Status = IntValidateDesktopHandle(hDesk, UserMode, 0, &pdesk);
if (!NT_SUCCESS(Status))
{
ERR_CH(UserThread, "Failed to validate initial desktop handle\n");
goto error;
}
/* Store the parsed desktop as the initial desktop */
ASSERT(ptiCurrent->ppi->hdeskStartup == NULL);
ASSERT(Process->UniqueProcessId != gpidLogon);
ptiCurrent->ppi->hdeskStartup = hDesk;
ptiCurrent->ppi->rpdeskStartup = pdesk;
}
if (ptiCurrent->ppi->hdeskStartup != NULL)
{
if (!IntSetThreadDesktop(ptiCurrent->ppi->hdeskStartup, FALSE))
{
ERR_CH(UserThread, "Failed to set thread desktop\n");
Status = STATUS_UNSUCCESSFUL;
goto error;
}
}
/* Mark the thread as fully initialized */
ptiCurrent->TIF_flags |= TIF_GUITHREADINITIALIZED;
if (!(ptiCurrent->ppi->W32PF_flags & (W32PF_ALLOWFOREGROUNDACTIVATE | W32PF_APPSTARTING)) &&
(gptiForeground && gptiForeground->ppi == ptiCurrent->ppi ))
{
ptiCurrent->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
}
ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
/* Last things to do only if we are not a SYSTEM or CSRSS thread */
if (!(ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)))
{
/* Callback to User32 Client Thread Setup */
TRACE_CH(UserThread, "Call co_IntClientThreadSetup...\n");
Status = co_IntClientThreadSetup();
if (!NT_SUCCESS(Status))
{
ERR_CH(UserThread, "ClientThreadSetup failed with Status 0x%08lx\n", Status);
goto error;
}
TRACE_CH(UserThread, "co_IntClientThreadSetup succeeded!\n");
}
else
{
TRACE_CH(UserThread, "co_IntClientThreadSetup cannot be called...\n");
}
TRACE_CH(UserThread, "UserCreateW32Thread pti 0x%p\n", ptiCurrent);
return STATUS_SUCCESS;
error:
ERR_CH(UserThread, "InitThreadCallback failed! Freeing pti 0x%p for TID:0x%lx\n",
ptiCurrent, HandleToUlong(Thread->Cid.UniqueThread));
ExitThreadCallback(Thread);
return Status;
}
VOID
UserDisplayNotifyShutdown(PPROCESSINFO ppiCurrent);
NTSTATUS
NTAPI
ExitThreadCallback(PETHREAD Thread)
{
PTHREADINFO *ppti;
PSINGLE_LIST_ENTRY psle;
PPROCESSINFO ppiCurrent;
PEPROCESS Process;
PTHREADINFO ptiCurrent;
Process = Thread->ThreadsProcess;
/* Get the Win32 Thread */
ptiCurrent = PsGetThreadWin32Thread(Thread);
ASSERT(ptiCurrent);
TRACE_CH(UserThread, "Destroying pti 0x%p eThread 0x%p\n", ptiCurrent, Thread);
ptiCurrent->TIF_flags |= TIF_INCLEANUP;
ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
ppiCurrent = ptiCurrent->ppi;
ASSERT(ppiCurrent);
IsRemoveAttachThread(ptiCurrent);
ptiCurrent->TIF_flags |= TIF_DONTATTACHQUEUE;
ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
UserCloseClipboard();
/* Decrement thread count and check if its 0 */
ppiCurrent->cThreads--;
if (ptiCurrent->TIF_flags & TIF_GUITHREADINITIALIZED)
{
/* Do now some process cleanup that requires a valid win32 thread */
if (ptiCurrent->ppi->cThreads == 0)
{
/* Check if we have registered the user api hook */
if (ptiCurrent->ppi == ppiUahServer)
{
/* Unregister the api hook */
UserUnregisterUserApiHook();
}
/* Notify logon application to restart shell if needed */
if (ptiCurrent->pDeskInfo)
{
if (ptiCurrent->pDeskInfo->ppiShellProcess == ppiCurrent)
{
DWORD ExitCode = PsGetProcessExitStatus(Process);
TRACE_CH(UserProcess, "Shell process is exiting (%lu)\n", ExitCode);
UserPostMessage(hwndSAS,
WM_LOGONNOTIFY,
LN_SHELL_EXITED,
ExitCode);
ptiCurrent->pDeskInfo->ppiShellProcess = NULL;
}
}
}
DceFreeThreadDCE(ptiCurrent);
DestroyTimersForThread(ptiCurrent);
KeSetEvent(ptiCurrent->pEventQueueServer, IO_NO_INCREMENT, FALSE);
UnregisterThreadHotKeys(ptiCurrent);
if (!UserDestroyObjectsForOwner(gHandleTable, ptiCurrent))
{
DPRINT1("Failed to delete objects belonging to thread %p. This is VERY BAD!.\n", ptiCurrent);
ASSERT(FALSE);
return STATUS_UNSUCCESSFUL;
}
if (ppiCurrent && ppiCurrent->ptiList == ptiCurrent && !ptiCurrent->ptiSibling &&
ppiCurrent->W32PF_flags & W32PF_CLASSESREGISTERED)
{
TRACE_CH(UserThread, "DestroyProcessClasses\n");
/* no process windows should exist at this point, or the function will assert! */
DestroyProcessClasses(ppiCurrent);
ppiCurrent->W32PF_flags &= ~W32PF_CLASSESREGISTERED;
}
IntBlockInput(ptiCurrent, FALSE);
IntCleanupThreadCallbacks(ptiCurrent);
/* cleanup user object references stack */
psle = PopEntryList(&ptiCurrent->ReferencesList);
while (psle)
{
PUSER_REFERENCE_ENTRY ref = CONTAINING_RECORD(psle, USER_REFERENCE_ENTRY, Entry);
TRACE_CH(UserThread, "thread clean: remove reference obj 0x%p\n",ref->obj);
UserDereferenceObject(ref->obj);
psle = PopEntryList(&ptiCurrent->ReferencesList);
}
}
if (ptiCurrent->cEnterCount)
{
KeSetKernelStackSwapEnable(TRUE);
ptiCurrent->cEnterCount = 0;
}
/* Find the THREADINFO in the PROCESSINFO's list */
ppti = &ppiCurrent->ptiList;
while (*ppti != NULL && *ppti != ptiCurrent)
{
ppti = &((*ppti)->ptiSibling);
}
/* we must have found it */
ASSERT(*ppti == ptiCurrent);
/* Remove it from the list */
*ppti = ptiCurrent->ptiSibling;
if (ptiCurrent->KeyboardLayout)
UserDereferenceObject(ptiCurrent->KeyboardLayout);
if (gptiForeground == ptiCurrent)
{
// IntNotifyWinEvent(EVENT_OBJECT_FOCUS, NULL, OBJID_CLIENT, CHILDID_SELF, 0);
// IntNotifyWinEvent(EVENT_SYSTEM_FOREGROUND, NULL, OBJID_WINDOW, CHILDID_SELF, 0);
gptiForeground = NULL;
}
/* Restore display mode when we are the last thread, and we changed the display mode */
if (ppiCurrent->cThreads == 0)
UserDisplayNotifyShutdown(ppiCurrent);
// Fixes CORE-6384 & CORE-7030.
/* if (ptiLastInput == ptiCurrent)
{
if (!ppiCurrent->ptiList)
ptiLastInput = gptiForeground;
else
ptiLastInput = ppiCurrent->ptiList;
ERR_CH(UserThread, "DTI: ptiLastInput is Cleared!!\n");
}
*/
TRACE_CH(UserThread, "Freeing pti 0x%p\n", ptiCurrent);
IntSetThreadDesktop(NULL, TRUE);
if (ptiCurrent->hEventQueueClient != NULL)
{
ObCloseHandle(ptiCurrent->hEventQueueClient, UserMode);
ObDereferenceObject(ptiCurrent->pEventQueueServer);
}
ptiCurrent->hEventQueueClient = NULL;
/* The thread is dying */
PsSetThreadWin32Thread(Thread /*ptiCurrent->pEThread*/, NULL, ptiCurrent);
/* Dereference the THREADINFO */
IntDereferenceThreadInfo(ptiCurrent);
return STATUS_SUCCESS;
}
NTSTATUS
APIENTRY
Win32kThreadCallback(PETHREAD Thread,
PSW32THREADCALLOUTTYPE Type)
{
NTSTATUS Status;
ASSERT(NtCurrentTeb());
UserEnterExclusive();
if (Type == PsW32ThreadCalloutInitialize)
{
ASSERT(PsGetThreadWin32Thread(Thread) == NULL);
Status = InitThreadCallback(Thread);
}
else // if (Type == PsW32ThreadCalloutExit)
{
ASSERT(PsGetThreadWin32Thread(Thread) != NULL);
Status = ExitThreadCallback(Thread);
}
UserLeave();
return Status;
}
_Function_class_(DRIVER_UNLOAD)
VOID NTAPI
DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
// TODO: Do more cleanup!
ResetCsrApiPort();
ResetCsrProcess();
}
// Return on failure
#define NT_ROF(x) \
{ \
Status = (x); \
if (!NT_SUCCESS(Status)) \
{ \
DPRINT1("Failed '%s' (0x%lx)\n", #x, Status); \
return Status; \
} \
}
/*
* This definition doesn't work
*/
INIT_FUNCTION
NTSTATUS
APIENTRY
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
NTSTATUS Status;
BOOLEAN Result;
WIN32_CALLOUTS_FPNS CalloutData = {0};
PVOID GlobalUserHeapBase = NULL;
/*
* Register user mode call interface
* (system service table index = 1)
*/
Result = KeAddSystemServiceTable(Win32kSSDT,
NULL,
Win32kNumberOfSysCalls,
Win32kSSPT,
1);
if (Result == FALSE)
{
DPRINT1("Adding system services failed!\n");
return STATUS_UNSUCCESSFUL;
}
hModuleWin = MmPageEntireDriver(DriverEntry);
DPRINT("Win32k hInstance 0x%p!\n", hModuleWin);
DriverObject->DriverUnload = DriverUnload;
/* Register Object Manager Callbacks */
CalloutData.ProcessCallout = Win32kProcessCallback;
CalloutData.ThreadCallout = Win32kThreadCallback;
// CalloutData.GlobalAtomTableCallout = NULL;
// CalloutData.PowerEventCallout = NULL;
// CalloutData.PowerStateCallout = NULL;
// CalloutData.JobCallout = NULL;
CalloutData.BatchFlushRoutine = NtGdiFlushUserBatch;
CalloutData.DesktopOpenProcedure = IntDesktopObjectOpen;
CalloutData.DesktopOkToCloseProcedure = IntDesktopOkToClose;
CalloutData.DesktopCloseProcedure = IntDesktopObjectClose;
CalloutData.DesktopDeleteProcedure = IntDesktopObjectDelete;
CalloutData.WindowStationOkToCloseProcedure = IntWinStaOkToClose;
// CalloutData.WindowStationCloseProcedure = NULL;
CalloutData.WindowStationDeleteProcedure = IntWinStaObjectDelete;
CalloutData.WindowStationParseProcedure = IntWinStaObjectParse;
// CalloutData.WindowStationOpenProcedure = NULL;
/* Register our per-process and per-thread structures. */
PsEstablishWin32Callouts(&CalloutData);
/* Register service hook callbacks */
#if DBG && defined(KDBG)
KdSystemDebugControl('CsoR', DbgPreServiceHook, ID_Win32PreServiceHook, 0, 0, 0, 0);
KdSystemDebugControl('CsoR', DbgPostServiceHook, ID_Win32PostServiceHook, 0, 0, 0, 0);
#endif
/* Create the global USER heap */
GlobalUserHeap = UserCreateHeap(&GlobalUserHeapSection,
&GlobalUserHeapBase,
1 * 1024 * 1024); /* FIXME: 1 MB for now... */
if (GlobalUserHeap == NULL)
{
DPRINT1("Failed to initialize the global heap!\n");
return STATUS_UNSUCCESSFUL;
}
/* Allocate global server info structure */
gpsi = UserHeapAlloc(sizeof(*gpsi));
if (!gpsi)
{
DPRINT1("Failed allocate server info structure!\n");
return STATUS_UNSUCCESSFUL;
}
RtlZeroMemory(gpsi, sizeof(*gpsi));
DPRINT("Global Server Data -> %p\n", gpsi);
NT_ROF(InitGdiHandleTable());
NT_ROF(InitPaletteImpl());
/* Create stock objects, ie. precreated objects commonly
used by win32 applications */
CreateStockObjects();
CreateSysColorObjects();
NT_ROF(InitBrushImpl());
NT_ROF(InitPDEVImpl());
NT_ROF(InitLDEVImpl());
NT_ROF(InitDeviceImpl());
NT_ROF(InitDcImpl());
NT_ROF(InitUserImpl());
NT_ROF(InitWindowStationImpl());
NT_ROF(InitDesktopImpl());
NT_ROF(InitInputImpl());
NT_ROF(InitKeyboardImpl());
NT_ROF(MsqInitializeImpl());
NT_ROF(InitTimerImpl());
NT_ROF(InitDCEImpl());
gusLanguageID = UserGetLanguageID();
/* Initialize FreeType library */
if (!InitFontSupport())
{
DPRINT1("Unable to initialize font support\n");
return Status;
}
return STATUS_SUCCESS;
}
/* EOF */