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.
568 lines
17 KiB
568 lines
17 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: random.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains a random collection of support routines for the User
|
|
* API functions. Many of these functions will be moved to more appropriate
|
|
* files once we get our act together.
|
|
*
|
|
* History:
|
|
* 10-17-90 DarrinM Created.
|
|
* 02-06-91 IanJa HWND revalidation added (none required)
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxUpdateWindows
|
|
*
|
|
* User mode wrapper
|
|
\***************************************************************************/
|
|
BOOL xxxUpdateWindows(
|
|
PWND pwnd,
|
|
HRGN hrgn)
|
|
{
|
|
CheckLock(pwnd);
|
|
|
|
xxxUpdateThreadsWindows(PtiCurrent(), pwnd, hrgn);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ValidateState
|
|
*
|
|
* States allowed to be set/cleared by Set/ClearWindowState. If you're
|
|
* allowing a new flag here, you must make sure it won't cause an AV
|
|
* in the kernel if someone sets it maliciously.
|
|
\***************************************************************************/
|
|
|
|
#define NUM_BYTES 16 // Window state bytes are 0 to F, explanation in user.h
|
|
|
|
CONST BYTE abValidateState[NUM_BYTES] = {
|
|
0, // 0
|
|
0, // 1
|
|
0, // 2
|
|
0, // 3
|
|
0, // 4
|
|
LOBYTE(WFWIN40COMPAT),
|
|
0, // 6
|
|
LOBYTE(WFNOANIMATE),
|
|
0, // 8
|
|
LOBYTE(WEFEDGEMASK),
|
|
LOBYTE(WEFSTATICEDGE),
|
|
0, // B
|
|
LOBYTE(EFPASSWORD),
|
|
LOBYTE(CBFHASSTRINGS | EFREADONLY),
|
|
LOBYTE(WFTABSTOP | WFSYSMENU | WFVSCROLL | WFHSCROLL | WFBORDER),
|
|
LOBYTE(WFCLIPCHILDREN)
|
|
};
|
|
|
|
BOOL ValidateState(
|
|
DWORD dwFlags)
|
|
{
|
|
BYTE bOffset = HIBYTE(dwFlags), bState = LOBYTE(dwFlags);
|
|
|
|
if (bOffset > NUM_BYTES - 1) {
|
|
return FALSE;
|
|
} else {
|
|
return ((bState & abValidateState[bOffset]) == bState);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* Set/ClearWindowState
|
|
*
|
|
* Wrapper functions for User mode to be able to set state flags.
|
|
\***************************************************************************/
|
|
VOID SetWindowState(
|
|
PWND pwnd,
|
|
DWORD dwFlags)
|
|
{
|
|
/*
|
|
* Don't let anyone mess with someone else's window.
|
|
*/
|
|
if (GETPTI(pwnd)->ppi == PtiCurrent()->ppi) {
|
|
if (ValidateState(dwFlags)) {
|
|
SetWF(pwnd, dwFlags);
|
|
} else {
|
|
RIPMSG1(RIP_ERROR, "SetWindowState: invalid flag 0x%x", dwFlags);
|
|
}
|
|
} else {
|
|
RIPMSG1(RIP_WARNING, "SetWindowState: current ppi doesn't own pwnd %#p", pwnd);
|
|
}
|
|
}
|
|
|
|
VOID ClearWindowState(
|
|
PWND pwnd,
|
|
DWORD dwFlags)
|
|
{
|
|
/*
|
|
* Don't let anyone mess with someone else's window.
|
|
*/
|
|
if (GETPTI(pwnd)->ppi == PtiCurrent()->ppi) {
|
|
if (ValidateState(dwFlags)) {
|
|
ClrWF(pwnd, dwFlags);
|
|
} else {
|
|
RIPMSG1(RIP_ERROR, "SetWindowState: invalid flag 0x%x", dwFlags);
|
|
}
|
|
} else {
|
|
RIPMSG1(RIP_WARNING, "ClearWindowState: current ppi doesn't own pwnd %#p", pwnd);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* CheckPwndFilter
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* 11-07-90 DarrinM Translated Win 3.0 ASM code.
|
|
\***************************************************************************/
|
|
BOOL CheckPwndFilter(
|
|
PWND pwnd,
|
|
PWND pwndFilter)
|
|
{
|
|
if ((pwndFilter == NULL) || (pwndFilter == pwnd) ||
|
|
((pwndFilter == (PWND)1) && (pwnd == NULL))) {
|
|
return TRUE;
|
|
}
|
|
|
|
return _IsChild(pwndFilter, pwnd);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* AllocateUnicodeString
|
|
*
|
|
* History:
|
|
* 10-25-90 MikeHar Wrote.
|
|
* 11-09-90 DarrinM Fixed.
|
|
* 01-13-92 GregoryW Neutralized.
|
|
* 03-05-98 FritzS Only allocate Length+1
|
|
\***************************************************************************/
|
|
BOOL AllocateUnicodeString(
|
|
PUNICODE_STRING pstrDst,
|
|
PUNICODE_STRING cczpstrSrc)
|
|
{
|
|
if (cczpstrSrc == NULL) {
|
|
RtlInitUnicodeString(pstrDst, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
pstrDst->Buffer = UserAllocPoolWithQuota(cczpstrSrc->Length+sizeof(UNICODE_NULL), TAG_TEXT);
|
|
if (pstrDst->Buffer == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
try {
|
|
RtlCopyMemory(pstrDst->Buffer, cczpstrSrc->Buffer, cczpstrSrc->Length);
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
UserFreePool(pstrDst->Buffer);
|
|
pstrDst->Buffer = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
pstrDst->MaximumLength = cczpstrSrc->Length+sizeof(UNICODE_NULL);
|
|
pstrDst->Length = cczpstrSrc->Length;
|
|
pstrDst->Buffer[pstrDst->Length / sizeof(WCHAR)] = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxGetControlColor
|
|
*
|
|
* History:
|
|
* 02-12-92 JimA Ported from Win31 sources
|
|
\***************************************************************************/
|
|
HBRUSH xxxGetControlColor(
|
|
PWND pwndParent,
|
|
PWND pwndCtl,
|
|
HDC hdc,
|
|
UINT message)
|
|
{
|
|
HBRUSH hbrush;
|
|
|
|
/*
|
|
* If we're sending to a window of another thread, don't send this message
|
|
* but instead call DefWindowProc(). New rule about the CTLCOLOR messages.
|
|
* Need to do this so that we don't send an hdc owned by one thread to
|
|
* another thread. It is also a harmless change.
|
|
*/
|
|
if (PpiCurrent() != GETPTI(pwndParent)->ppi) {
|
|
return (HBRUSH)xxxDefWindowProc(pwndParent, message, (WPARAM)hdc, (LPARAM)HW(pwndCtl));
|
|
}
|
|
|
|
hbrush = (HBRUSH)xxxSendMessage(pwndParent, message, (WPARAM)hdc, (LPARAM)HW(pwndCtl));
|
|
|
|
/*
|
|
* If the brush returned from the parent is invalid, get a valid brush from
|
|
* xxxDefWindowProc.
|
|
*/
|
|
if (hbrush == 0 || !GreValidateServerHandle(hbrush, BRUSH_TYPE)) {
|
|
if (hbrush != 0) {
|
|
RIPMSG2(RIP_WARNING,
|
|
"Invalid HBRUSH from WM_CTLCOLOR*** msg 0x%x brush 0x%x",
|
|
message, hbrush);
|
|
}
|
|
|
|
hbrush = (HBRUSH)xxxDefWindowProc(pwndParent, message,
|
|
(WPARAM)hdc, (LPARAM)pwndCtl);
|
|
}
|
|
|
|
return hbrush;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxGetControlBrush
|
|
*
|
|
* <brief description>
|
|
*
|
|
* History:
|
|
* 12-10-90 IanJa type replaced with new 32-bit message
|
|
* 01-21-91 IanJa Prefix '_' denoting exported function (although not API)
|
|
\***************************************************************************/
|
|
|
|
HBRUSH xxxGetControlBrush(
|
|
PWND pwnd,
|
|
HDC hdc,
|
|
UINT message)
|
|
{
|
|
HBRUSH hbr;
|
|
PWND pwndSend;
|
|
TL tlpwndSend;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
if ((pwndSend = (TestwndPopup(pwnd) ? pwnd->spwndOwner : pwnd->spwndParent))
|
|
== NULL) {
|
|
pwndSend = pwnd;
|
|
}
|
|
|
|
ThreadLock(pwndSend, &tlpwndSend);
|
|
|
|
/*
|
|
* Last parameter changes the message into a ctlcolor id.
|
|
*/
|
|
hbr = xxxGetControlColor(pwndSend, pwnd, hdc, message);
|
|
ThreadUnlock(&tlpwndSend);
|
|
|
|
return hbr;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxHardErrorControl
|
|
*
|
|
* Performs kernel-mode hard error support functions.
|
|
*
|
|
* History:
|
|
* 02-08-95 JimA Created.
|
|
\***************************************************************************/
|
|
UINT xxxHardErrorControl(
|
|
DWORD dwCmd,
|
|
HANDLE handle,
|
|
PDESKRESTOREDATA pdrdRestore)
|
|
{
|
|
PTHREADINFO ptiClient, ptiCurrent = PtiCurrent();
|
|
PDESKTOP pdesk;
|
|
PUNICODE_STRING pstrName;
|
|
NTSTATUS Status;
|
|
PETHREAD Thread;
|
|
BOOL fAllowForeground;
|
|
|
|
/*
|
|
* turn off BlockInput so the user can respond to the hard error popup
|
|
*/
|
|
gptiBlockInput = NULL;
|
|
|
|
UserAssert(ISCSRSS());
|
|
|
|
switch (dwCmd) {
|
|
/*
|
|
* Code to catch Windows Bug 469607.
|
|
*/
|
|
#ifdef PRERELEASE
|
|
case HardErrorCheckOnDesktop:
|
|
if (ptiCurrent == gptiForeground && ptiCurrent->rpdesk == NULL) {
|
|
FRE_RIPMSG0(RIP_ERROR,
|
|
"Harderror thread exiting while not on a desktop");
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case HardErrorSetup:
|
|
/*
|
|
* Don't do it if the system has not been initialized.
|
|
*/
|
|
if (grpdeskRitInput == NULL) {
|
|
RIPMSG0(RIP_WARNING, "HardErrorControl: System not initialized");
|
|
return HEC_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Setup caller as the hard error handler.
|
|
*/
|
|
if (gHardErrorHandler.pti != NULL) {
|
|
RIPMSG1(RIP_WARNING, "HardErrorControl: pti not NULL %#p", gHardErrorHandler.pti);
|
|
return HEC_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Mark the handler as active.
|
|
*/
|
|
gHardErrorHandler.pti = ptiCurrent;
|
|
|
|
/*
|
|
* Clear any pending quits.
|
|
*/
|
|
ptiCurrent->TIF_flags &= ~TIF_QUITPOSTED;
|
|
|
|
break;
|
|
|
|
case HardErrorCleanup:
|
|
|
|
/*
|
|
* Remove caller as the hard error handler.
|
|
*/
|
|
if (gHardErrorHandler.pti != ptiCurrent) {
|
|
return HEC_ERROR;
|
|
}
|
|
|
|
gHardErrorHandler.pti = NULL;
|
|
break;
|
|
|
|
case HardErrorAttachUser:
|
|
case HardErrorInDefDesktop:
|
|
/*
|
|
* Check for exit conditions. We do not allow attaches to the
|
|
* disconnect desktop.
|
|
*/
|
|
if (ISTS()) {
|
|
if ((grpdeskRitInput == NULL) ||
|
|
|
|
((grpdeskRitInput == gspdeskDisconnect) &&
|
|
(gspdeskShouldBeForeground == NULL)) ||
|
|
|
|
((grpdeskRitInput == gspdeskDisconnect) &&
|
|
(gspdeskShouldBeForeground == gspdeskDisconnect))) {
|
|
return HEC_ERROR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Only attach to a user desktop.
|
|
*/
|
|
if (ISTS() && grpdeskRitInput == gspdeskDisconnect) {
|
|
pstrName = POBJECT_NAME(gspdeskShouldBeForeground);
|
|
} else {
|
|
pstrName = POBJECT_NAME(grpdeskRitInput);
|
|
}
|
|
|
|
if (pstrName && (!_wcsicmp(TEXT("Winlogon"), pstrName->Buffer) ||
|
|
!_wcsicmp(TEXT("Disconnect"), pstrName->Buffer) ||
|
|
!_wcsicmp(TEXT("Screen-saver"), pstrName->Buffer))) {
|
|
RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "");
|
|
return HEC_WRONGDESKTOP;
|
|
}
|
|
if (dwCmd == HardErrorInDefDesktop) {
|
|
/*
|
|
* Clear any pending quits.
|
|
*/
|
|
ptiCurrent->TIF_flags &= ~TIF_QUITPOSTED;
|
|
return HEC_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* Fall through.
|
|
*/
|
|
|
|
case HardErrorAttach:
|
|
|
|
/*
|
|
* Save a pointer to and prevent destruction of the
|
|
* current queue. This will give us a queue to return
|
|
* to if journalling is occuring when we tear down the
|
|
* hard error popup.
|
|
*/
|
|
gHardErrorHandler.pqAttach = ptiCurrent->pq;
|
|
(ptiCurrent->pq->cLockCount)++;
|
|
|
|
/*
|
|
* Fall through.
|
|
*/
|
|
|
|
case HardErrorAttachNoQueue:
|
|
|
|
/*
|
|
* Check for exit conditions. We do not allow attaches to the
|
|
* disconnect desktop.
|
|
*/
|
|
if (ISTS()) {
|
|
if ((grpdeskRitInput == NULL) ||
|
|
|
|
((grpdeskRitInput == gspdeskDisconnect) &&
|
|
(gspdeskShouldBeForeground == NULL)) ||
|
|
|
|
((grpdeskRitInput == gspdeskDisconnect) &&
|
|
(gspdeskShouldBeForeground == gspdeskDisconnect))) {
|
|
return HEC_ERROR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Attach the handler to the current desktop.
|
|
*/
|
|
/*
|
|
* Don't allow an attach to the disconnected desktop, but
|
|
* remember this for later when we detach.
|
|
*/
|
|
gbDisconnectHardErrorAttach = FALSE;
|
|
|
|
if (ISTS() && grpdeskRitInput == gspdeskDisconnect) {
|
|
pdesk = gspdeskShouldBeForeground;
|
|
gbDisconnectHardErrorAttach = TRUE;
|
|
} else {
|
|
pdesk = grpdeskRitInput;
|
|
}
|
|
|
|
UserAssert(pdesk != NULL);
|
|
|
|
Status = xxxSetCsrssThreadDesktop(pdesk, pdrdRestore);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING,
|
|
"HardErrorControl: HardErrorAttachNoQueue failed: 0x%x",
|
|
Status);
|
|
if (dwCmd != HardErrorAttachNoQueue) {
|
|
gHardErrorHandler.pqAttach = NULL;
|
|
UserAssert(ptiCurrent->pq->cLockCount);
|
|
(ptiCurrent->pq->cLockCount)--;
|
|
}
|
|
|
|
return HEC_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Make sure we actually set the pdesk in the current thread
|
|
*/
|
|
UserAssert(ptiCurrent->rpdesk != NULL);
|
|
|
|
/*
|
|
* Determine if this box can come to the foreground.
|
|
* Let it come to the foreground if it doesn't have a pti
|
|
* (it might have just failed to load).
|
|
*/
|
|
fAllowForeground = FALSE;
|
|
if (handle != NULL) {
|
|
Status = ObReferenceObjectByHandle(handle,
|
|
THREAD_QUERY_INFORMATION,
|
|
*PsThreadType,
|
|
UserMode,
|
|
&Thread,
|
|
NULL);
|
|
if (NT_SUCCESS(Status)) {
|
|
ptiClient = PtiFromThread(Thread);
|
|
if ((ptiClient == NULL) || CanForceForeground(ptiClient->ppi)) {
|
|
fAllowForeground = TRUE;
|
|
}
|
|
|
|
UnlockThread(Thread);
|
|
} else {
|
|
RIPMSGF2(RIP_WARNING,
|
|
"Failed to get thread (0x%p), Status: 0x%x",
|
|
handle,
|
|
Status);
|
|
}
|
|
}
|
|
|
|
if (fAllowForeground) {
|
|
ptiCurrent->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
|
|
TAGMSG1(DBGTAG_FOREGROUND, "xxxHardErrorControl set TIF %#lx", ptiCurrent);
|
|
} else {
|
|
ptiCurrent->TIF_flags &= ~TIF_ALLOWFOREGROUNDACTIVATE;
|
|
TAGMSG1(DBGTAG_FOREGROUND, "xxxHardErrorControl clear TIF %#lx", ptiCurrent);
|
|
}
|
|
|
|
break;
|
|
|
|
case HardErrorDetach:
|
|
|
|
/*
|
|
* xxxSwitchDesktop may have sent WM_QUIT to the msgbox, so
|
|
* ensure that the quit flag is reset.
|
|
*/
|
|
ptiCurrent->TIF_flags &= ~TIF_QUITPOSTED;
|
|
|
|
/*
|
|
* We will reset the hard-error queue to the pre-allocated
|
|
* one so if we end up looping back (i.e. from a desktop
|
|
* switch), we will have a valid queue in case the desktop
|
|
* was deleted.
|
|
*/
|
|
UserAssert(gHardErrorHandler.pqAttach->cLockCount);
|
|
(gHardErrorHandler.pqAttach->cLockCount)--;
|
|
|
|
DeferWinEventNotify();
|
|
|
|
BEGINATOMICCHECK();
|
|
|
|
if (ptiCurrent->pq != gHardErrorHandler.pqAttach) {
|
|
UserAssert(gHardErrorHandler.pqAttach->cThreads == 0);
|
|
AllocQueue(NULL, gHardErrorHandler.pqAttach);
|
|
gHardErrorHandler.pqAttach->cThreads++;
|
|
zzzAttachToQueue(ptiCurrent, gHardErrorHandler.pqAttach, NULL, FALSE);
|
|
}
|
|
|
|
gHardErrorHandler.pqAttach = NULL;
|
|
|
|
ENDATOMICCHECK();
|
|
|
|
zzzEndDeferWinEventNotify();
|
|
|
|
/*
|
|
* Fall through.
|
|
*/
|
|
|
|
case HardErrorDetachNoQueue:
|
|
/*
|
|
* Detach the handler from the desktop and return
|
|
* status to indicate if a switch has occured.
|
|
*/
|
|
pdesk = ptiCurrent->rpdesk;
|
|
xxxRestoreCsrssThreadDesktop(pdrdRestore);
|
|
|
|
if (ISTS()) {
|
|
/*
|
|
* The hard error message box gets a desktop switch notification,
|
|
* so remember that we lied to him and lie (or unlie) to him again.
|
|
* A desktop switch did occur.
|
|
*/
|
|
if (gbDisconnectHardErrorAttach) {
|
|
gbDisconnectHardErrorAttach = FALSE;
|
|
return HEC_DESKTOPSWITCH;
|
|
}
|
|
#ifdef WAY_LATER
|
|
/*
|
|
* This happened once and caused a trap when a KeyEvent() came in and we
|
|
* directed it to this queue. I think this is a MS window that we caught
|
|
* since we use this so much for license popup's.
|
|
*/
|
|
if (gHardErrorHandler.pqAttach == gpqForeground) {
|
|
gpqForeground = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return (pdesk != grpdeskRitInput ? HEC_DESKTOPSWITCH : HEC_SUCCESS);
|
|
}
|
|
|
|
return HEC_SUCCESS;
|
|
}
|