mirror of https://github.com/lianthony/NT4.0
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.
2599 lines
79 KiB
2599 lines
79 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: createw.c
|
|
*
|
|
* Copyright (c) 1985-1996, Microsoft Corporation
|
|
*
|
|
* Contains xxxCreateWindow, xxxDestroyWindow and a few close friends.
|
|
*
|
|
* Note that during creation or deletion, the window is locked so that
|
|
* it can't be deleted recursively
|
|
*
|
|
* History:
|
|
* 19-Oct-1990 DarrinM Created.
|
|
* 11-Feb-1991 JimA Added access checks.
|
|
* 19-Feb-1991 MikeKe Added Revalidation code
|
|
* 20-Jan-1992 IanJa ANSI/UNICODE neutralization
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
/***************************************************************************\
|
|
* xxxCreateWindowExWOW (API)
|
|
*
|
|
* History:
|
|
* 10-18-90 darrinm Ported from Win 3.0 sources.
|
|
* 02-07-91 DavidPe Added Win 3.1 WH_CBT support.
|
|
* 02-11-91 JimA Added access checks.
|
|
* 04-11-92 ChandanC Added initialization of WOW words
|
|
\***************************************************************************/
|
|
|
|
PWND xxxCreateWindowExWOW(
|
|
DWORD dwExStyle,
|
|
PLARGE_STRING pstrClass,
|
|
PLARGE_STRING pstrName,
|
|
DWORD style,
|
|
int x,
|
|
int y,
|
|
int cx,
|
|
int cy,
|
|
PWND pwndParent,
|
|
PMENU pMenu,
|
|
HANDLE hInstance,
|
|
LPVOID lpCreateParams,
|
|
DWORD dwExpWinVerAndFlags,
|
|
LPDWORD lpWOW)
|
|
{
|
|
UINT mask;
|
|
BOOL fChild;
|
|
BOOL fDefPos = FALSE;
|
|
BOOL fStartup = FALSE;
|
|
PCLS pcls;
|
|
PPCLS ppcls;
|
|
RECT rc;
|
|
int dx, dy;
|
|
RECT rcSave;
|
|
int sw = SW_SHOW;
|
|
PWND pwnd;
|
|
PWND pwndZOrder, pwndHardError;
|
|
CREATESTRUCTEX csex;
|
|
PDESKTOP pdesk;
|
|
ATOM atomT;
|
|
PTHREADINFO ptiCurrent;
|
|
TL tlpwnd;
|
|
TL tlpwndParent;
|
|
TL tlpwndParentT;
|
|
BOOL fLockParent = FALSE;
|
|
WORD wWFAnsiCreator = 0;
|
|
DWORD dw;
|
|
|
|
|
|
/*
|
|
* For Edit Controls (including those in comboboxes), we must know whether
|
|
* the App used an ANSI or a Unicode CreateWindow call. This is passed in
|
|
* with the private WS_EX_ANSICREATOR dwExStyle bit, but we MUST NOT leave
|
|
* out this bit in the window's dwExStyle! Transfer to the internal window
|
|
* flag WFANSICREATOR immediately.
|
|
*/
|
|
if (dwExStyle & WS_EX_ANSICREATOR) {
|
|
wWFAnsiCreator = WFANSICREATOR;
|
|
dwExStyle &= ~WS_EX_ANSICREATOR;
|
|
}
|
|
|
|
CheckLock(pwndParent);
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
pdesk = ptiCurrent->rpdesk;
|
|
|
|
/*
|
|
* If a parent window is specified, make sure it's on the
|
|
* same desktop.
|
|
*/
|
|
if (pwndParent != NULL && pwndParent->head.rpdesk != pdesk) {
|
|
RIPERR0(ERROR_INVALID_PARAMETER, RIP_VERBOSE, "");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Ensure that we can create the window. If there is no desktop
|
|
* yet, assume that this will be the root desktop window and allow
|
|
* the creation.
|
|
*/
|
|
if (ptiCurrent->hdesk)
|
|
RETURN_IF_ACCESS_DENIED(ptiCurrent->amdesk,
|
|
DESKTOP_CREATEWINDOW, NULL);
|
|
|
|
/*
|
|
* Don't allow child windows without a parent handle.
|
|
*/
|
|
if (pwndParent == NULL) {
|
|
if ((HIWORD(style) & MaskWF(WFTYPEMASK)) == MaskWF(WFCHILD)) {
|
|
RIPERR0(ERROR_TLW_WITH_WSCHILD, RIP_VERBOSE, "");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make sure we can get the window class.
|
|
*/
|
|
if (HIWORD(pstrClass) != 0)
|
|
atomT = FindAtomW(pstrClass->Buffer);
|
|
else
|
|
atomT = LOWORD(pstrClass);
|
|
|
|
if (atomT == 0) {
|
|
CantFindClassMessageAndFail:
|
|
#ifdef DEBUG
|
|
if (HIWORD(pstrClass) != 0) {
|
|
try {
|
|
RIPMSG1(RIP_WARNING,
|
|
"Couldn't find class string %ws",
|
|
pstrClass->Buffer);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
} else {
|
|
RIPMSG1(RIP_WARNING,
|
|
"Couldn't find class atom %lx",
|
|
pstrClass);
|
|
}
|
|
#endif
|
|
|
|
RIPERR0(ERROR_CANNOT_FIND_WND_CLASS, RIP_VERBOSE, "");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* First scan the private classes. If we don't find the class there
|
|
* scan the public classes. If we don't find it there, fail.
|
|
*/
|
|
ppcls = GetClassPtr(atomT, ptiCurrent->ppi, hInstance);
|
|
if (ppcls == NULL) {
|
|
goto CantFindClassMessageAndFail;
|
|
}
|
|
|
|
pcls = *ppcls;
|
|
|
|
if (NeedsWindowEdge(style, dwExStyle, (LOWORD(dwExpWinVerAndFlags) >= VER40)))
|
|
dwExStyle |= WS_EX_WINDOWEDGE;
|
|
else
|
|
dwExStyle &= ~WS_EX_WINDOWEDGE;
|
|
|
|
/*
|
|
* Allocate memory for regular windows.
|
|
*/
|
|
pwnd = HMAllocObject(ptiCurrent, pdesk, TYPE_WINDOW,
|
|
sizeof(WND) + pcls->cbwndExtra);
|
|
if (pwnd == NULL) {
|
|
RIPERR0(ERROR_OUTOFMEMORY,
|
|
RIP_WARNING,
|
|
"Out of pool in xxxCreateWindowExWOW");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Stuff in the pq, class pointer, and window style.
|
|
*/
|
|
pwnd->bFullScreen = WINDOWED;
|
|
pwnd->pcls = pcls;
|
|
pwnd->style = style;
|
|
pwnd->ExStyle = dwExStyle;
|
|
pwnd->pwo = (PVOID)NULL;
|
|
pwnd->hdcOwn = 0;
|
|
pwnd->iHungRedraw = -1;
|
|
pwnd->cbwndExtra = pcls->cbwndExtra;
|
|
|
|
/*
|
|
* Increment the Window Reference Count in the Class structure
|
|
* Because xxxFreeWindow() decrements the count, incrementing has
|
|
* to be done now. Incase of error, xxxFreeWindow() will decrement it.
|
|
*/
|
|
if (!ReferenceClass(pcls, pwnd)) {
|
|
HMFreeObject(pwnd);
|
|
goto CantFindClassMessageAndFail;
|
|
}
|
|
|
|
#ifdef FE_IME
|
|
/*
|
|
* Button control doesn't need input context. Other windows
|
|
* will assoicate with the default input context.
|
|
*/
|
|
if (atomT == gpsi->atomSysClass[ICLS_BUTTON])
|
|
pwnd->hImc = NULL_HIMC;
|
|
else
|
|
{
|
|
if (ptiCurrent->spDefaultImc == NULL) {
|
|
/*
|
|
* Create per-thread default input context
|
|
*/
|
|
xxxCreateInputContext(0);
|
|
}
|
|
pwnd->hImc = (HIMC)PtoH(ptiCurrent->spDefaultImc);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Update the window count. Doing this now will ensure that if
|
|
* the creation fails, xxxFreeWindow will keep the window count
|
|
* correct.
|
|
*/
|
|
ptiCurrent->cWindows++;
|
|
|
|
/*
|
|
* Get the class from the window because ReferenceClass may have
|
|
* cloned the class.
|
|
*/
|
|
pcls = pwnd->pcls;
|
|
|
|
/*
|
|
* Copy WOW aliasing info into WOW DWORDs
|
|
*/
|
|
if (lpWOW) {
|
|
memcpy (pwnd->adwWOW, lpWOW, sizeof(pwnd->adwWOW));
|
|
}
|
|
|
|
/*
|
|
* This is a replacement for the &lpCreateParams stuff that used to
|
|
* pass a pointer directly to the parameters on the stack. This
|
|
* step must be done AFTER referencing the class because we
|
|
* may use the ANSI class name.
|
|
*/
|
|
RtlZeroMemory(&csex, sizeof(csex));
|
|
csex.cs.dwExStyle = dwExStyle;
|
|
csex.cs.hInstance = hInstance;
|
|
|
|
if (HIWORD(pstrClass) == 0) {
|
|
csex.cs.lpszClass = (LPWSTR)pstrClass;
|
|
} else {
|
|
if (wWFAnsiCreator) {
|
|
csex.cs.lpszClass = (LPWSTR)pcls->lpszAnsiClassName;
|
|
if (HIWORD(csex.cs.lpszClass))
|
|
RtlInitLargeAnsiString((PLARGE_ANSI_STRING)&csex.strClass,
|
|
(LPSTR)csex.cs.lpszClass, (UINT)-1);
|
|
} else {
|
|
csex.cs.lpszClass = pstrClass->Buffer;
|
|
csex.strClass = *pstrClass;
|
|
}
|
|
}
|
|
|
|
if (pstrName != NULL) {
|
|
csex.cs.lpszName = pstrName->Buffer;
|
|
csex.strName = *pstrName;
|
|
}
|
|
csex.cs.style = style;
|
|
csex.cs.x = x;
|
|
csex.cs.y = y;
|
|
csex.cs.cx = cx;
|
|
csex.cs.cy = cy;
|
|
csex.cs.hwndParent = HW(pwndParent);
|
|
|
|
/*
|
|
* If pMenu is non-NULL and the window is not a child, pMenu must
|
|
* be a menu
|
|
*
|
|
* The below test is equivalent to TestwndChild().
|
|
*/
|
|
if ((style & (WS_CHILD | WS_POPUP)) == WS_CHILD) {
|
|
csex.cs.hMenu = (HMENU)pMenu;
|
|
} else {
|
|
csex.cs.hMenu = PtoH(pMenu);
|
|
}
|
|
|
|
csex.cs.lpCreateParams = lpCreateParams;
|
|
|
|
/*
|
|
* ThreadLock: we are going to be doing multiple callbacks here.
|
|
*/
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
|
|
|
|
//
|
|
// Create the class small icon if there isn't one since we are in context
|
|
// and we are creating a window from this class...
|
|
//
|
|
if (pcls->spicn && !pcls->spicnSm)
|
|
xxxCreateClassSmIcon(pcls);
|
|
|
|
/*
|
|
* Store the instance handle and window proc address. We do this earlier
|
|
* than Windows because they have a bug were a message can be sent
|
|
* but lpfnWndProc is not set (3986 CBT WM_CREATE not allowed.)
|
|
*/
|
|
pwnd->hModule = hInstance;
|
|
|
|
/*
|
|
* Get rid of EditWndProc plain.
|
|
*/
|
|
pwnd->lpfnWndProc = (WNDPROC_PWND)MapClientNeuterToClientPfn(pcls, 0, wWFAnsiCreator);
|
|
|
|
/*
|
|
* If this window class has a server-side window procedure, mark
|
|
* it as such. If the app subclasses it later with an app-side proc
|
|
* then this mark will be removed.
|
|
*/
|
|
if (pcls->flags & CSF_SERVERSIDEPROC) {
|
|
SetWF(pwnd, WFSERVERSIDEPROC);
|
|
UserAssert(!(pcls->flags & CSF_ANSIPROC));
|
|
}
|
|
|
|
/*
|
|
* If this window was created with an ANSI CreateWindow*() call, mark
|
|
* it as such so edit controls will be created correctly. (A combobox
|
|
* will be able to pass the WFANSICREATOR bit on to its edit control)
|
|
*/
|
|
SetWF(pwnd, wWFAnsiCreator);
|
|
|
|
/*
|
|
* If this window belongs to an ANSI class or it is a WFANSICREATOR
|
|
* control, then mark it as an ANSI window
|
|
*/
|
|
if ((pcls->flags & CSF_ANSIPROC) ||
|
|
(wWFAnsiCreator &&
|
|
((atomT == gpsi->atomSysClass[ICLS_BUTTON]) ||
|
|
(atomT == gpsi->atomSysClass[ICLS_COMBOBOX]) ||
|
|
(atomT == gpsi->atomSysClass[ICLS_COMBOLISTBOX]) ||
|
|
(atomT == gpsi->atomSysClass[ICLS_DIALOG]) ||
|
|
(atomT == gpsi->atomSysClass[ICLS_EDIT]) ||
|
|
(atomT == gpsi->atomSysClass[ICLS_LISTBOX]) ||
|
|
(atomT == gpsi->atomSysClass[ICLS_MDICLIENT]) ||
|
|
#ifdef FE_IME
|
|
(atomT == gpsi->atomSysClass[ICLS_IME]) ||
|
|
#endif
|
|
(atomT == gpsi->atomSysClass[ICLS_STATIC])))) {
|
|
SetWF(pwnd, WFANSIPROC);
|
|
}
|
|
|
|
/*
|
|
* If a 3.1-compatible application is creating the window, set this
|
|
* bit to enable various backward-compatibility hacks.
|
|
*
|
|
* If it's not 3.1 compatible, see if we need to turn on the PixieHack
|
|
* (see wmupdate.c for more info on this)
|
|
*/
|
|
|
|
dw = GetAppCompatFlags(ptiCurrent);
|
|
|
|
if (dw & GACF_RANDOM3XUI) {
|
|
SetWF(pwnd, WFOLDUI);
|
|
|
|
dwExStyle &= 0x0000003f;
|
|
csex.cs.dwExStyle &= 0x0000003f;
|
|
}
|
|
|
|
pwnd->dwExpWinVer = (DWORD)LOWORD(dwExpWinVerAndFlags);
|
|
if (Is310Compat(pwnd->dwExpWinVer)) {
|
|
SetWF(pwnd, WFWIN31COMPAT);
|
|
if (Is400Compat(pwnd->dwExpWinVer)) {
|
|
SetWF(pwnd, WFWIN40COMPAT);
|
|
}
|
|
} else if (dw & GACF_ALWAYSSENDNCPAINT)
|
|
SetWF(pwnd, WFALWAYSSENDNCPAINT);
|
|
|
|
mask = 0;
|
|
ClrWF(pwnd, WFVISIBLE);
|
|
|
|
/*
|
|
* Inform the CBT hook that a window is being created. Pass it the
|
|
* CreateParams and the window handle that the new one will be inserted
|
|
* after. The CBT hook handler returns TRUE to prevent the window
|
|
* from being created. It can also modify the CREATESTRUCT info, which
|
|
* will affect the size, parent, and position of the window.
|
|
* Defaultly position non-child windows at the top of their list.
|
|
*/
|
|
|
|
if (IsHooked(ptiCurrent, WHF_CBT)) {
|
|
CBT_CREATEWND cbt;
|
|
|
|
/*
|
|
* Use the extended createstruct so the hook thunk can
|
|
* handle the strings correctly.
|
|
*/
|
|
cbt.lpcs = (LPCREATESTRUCT)&csex;
|
|
cbt.hwndInsertAfter = HWND_TOP;
|
|
|
|
if ((BOOL)xxxCallHook(HCBT_CREATEWND, (DWORD)HWq(pwnd),
|
|
(DWORD)&cbt, WH_CBT)) {
|
|
goto MemError;
|
|
} else {
|
|
/*
|
|
* The CreateHook may have modified some parameters so write them
|
|
* out (in Windows 3.1 we used to write directly to the variables
|
|
* on the stack).
|
|
*/
|
|
|
|
x = csex.cs.x;
|
|
y = csex.cs.y;
|
|
cx = csex.cs.cx;
|
|
cy = csex.cs.cy;
|
|
|
|
if (HIWORD(cbt.hwndInsertAfter) == 0)
|
|
pwndZOrder = (PWND)cbt.hwndInsertAfter;
|
|
else
|
|
pwndZOrder = RevalidateHwnd(cbt.hwndInsertAfter);
|
|
}
|
|
} else {
|
|
pwndZOrder = (PWND)HWND_TOP;
|
|
}
|
|
|
|
if (!TestwndTiled(pwnd)) {
|
|
|
|
/*
|
|
* CW_USEDEFAULT is only valid for tiled and overlapped windows.
|
|
* Don't let it be used.
|
|
*/
|
|
if (x == CW_USEDEFAULT || x == CW2_USEDEFAULT) {
|
|
x = 0;
|
|
y = 0;
|
|
}
|
|
|
|
if (cx == CW_USEDEFAULT || cx == CW2_USEDEFAULT) {
|
|
cx = 0;
|
|
cy = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Make local copies of these parameters.
|
|
*/
|
|
rcSave.left = x;
|
|
rcSave.top = y;
|
|
rcSave.right = cx;
|
|
rcSave.bottom = cy;
|
|
|
|
/*
|
|
* Position Child Windows
|
|
*/
|
|
|
|
if (fChild = (BOOL)TestwndChild(pwnd)) {
|
|
|
|
/*
|
|
* Child windows are offset from the parent's origin.
|
|
*/
|
|
rcSave.left += pwndParent->rcClient.left;
|
|
rcSave.top += pwndParent->rcClient.top;
|
|
|
|
/*
|
|
* Defaultly position child windows at bottom of their list.
|
|
*/
|
|
pwndZOrder = PWND_BOTTOM;
|
|
}
|
|
|
|
/*
|
|
* Position Tiled Windows
|
|
*/
|
|
|
|
/*
|
|
* Is this a Tiled/Overlapping window?
|
|
*/
|
|
if (TestwndTiled(pwnd)) {
|
|
|
|
/*
|
|
* Force the WS_CLIPSIBLINGS window style and add a caption and
|
|
* a border.
|
|
*/
|
|
SetWF(pwnd, WFCLIPSIBLINGS);
|
|
mask = MaskWF(WFCAPTION) | MaskWF(WFBORDER);
|
|
|
|
//
|
|
// We add on a raised edge since IF the person had passed in WS_CAPTION,
|
|
// and didn't specify any 3D borders, we would've added it on to the
|
|
// style bits above.
|
|
//
|
|
|
|
if (TestWF(pwnd, WFWIN40COMPAT))
|
|
// if (!TestWF(pwnd, WEFEDGEMASK))
|
|
SetWF(pwnd, WEFWINDOWEDGE);
|
|
|
|
/*
|
|
* Set bit that will force size message to be sent at SHOW time.
|
|
*/
|
|
SetWF(pwnd, WFSENDSIZEMOVE);
|
|
|
|
/*
|
|
* Here is how the "tiled" window initial positioning works...
|
|
* If the app is a 1.0x app, then we use our standard "stair step"
|
|
* default positioning scheme. Otherwise, we check the x & cx
|
|
* parameters. If either of these == CW_USEDEFAULT then use the
|
|
* default position/size, otherwise use the position/size they
|
|
* specified. If not using default position, use SW_SHOW for the
|
|
* xxxShowWindow() parameter, otherwise use the y parameter given.
|
|
*
|
|
* In 32-bit world, CW_USEDEFAULT is 0x80000000, but apps still
|
|
* store word-oriented values either in dialog templates or
|
|
* in their own structures. So CreateWindow still recognizes the
|
|
* 16 bit equivalent, which is 0x8000, CW2_USEDEFAULT. The original
|
|
* is changed because parameters to CreateWindow() are 32 bit
|
|
* values, which can cause sign extention, or weird results if
|
|
* 16 bit math assumptions are being made, etc.
|
|
*/
|
|
|
|
/*
|
|
* Default to passing the y parameter to xxxShowWindow().
|
|
*/
|
|
if (x == CW_USEDEFAULT || x == CW2_USEDEFAULT) {
|
|
|
|
/*
|
|
* If the y value is not CW_USEDEFAULT, use it as a SW_* command.
|
|
*/
|
|
if (rcSave.top != CW_USEDEFAULT && rcSave.top != CW2_USEDEFAULT)
|
|
sw = rcSave.top;
|
|
}
|
|
|
|
/*
|
|
* Calculate the rect which the next "stacked" window will use.
|
|
*/
|
|
SetTiledRect(pwnd, &rc);
|
|
|
|
/*
|
|
* Did the app ask for default positioning?
|
|
*/
|
|
if (x == CW_USEDEFAULT || x == CW2_USEDEFAULT) {
|
|
|
|
/*
|
|
* Use default positioning.
|
|
*/
|
|
if (ptiCurrent->ppi->usi.dwFlags & STARTF_USEPOSITION ) {
|
|
fStartup = TRUE;
|
|
x = rcSave.left = ptiCurrent->ppi->usi.dwX;
|
|
y = rcSave.top = ptiCurrent->ppi->usi.dwY;
|
|
} else {
|
|
x = rcSave.left = rc.left;
|
|
y = rcSave.top = rc.top;
|
|
}
|
|
fDefPos = TRUE;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Use the apps specified positioning. Undo the "stacking"
|
|
* effect caused by SetTiledRect().
|
|
*/
|
|
if (iwndStack)
|
|
iwndStack--;
|
|
}
|
|
|
|
/*
|
|
* Did the app ask for default sizing?
|
|
*/
|
|
if (rcSave.right == CW_USEDEFAULT || rcSave.right == CW2_USEDEFAULT) {
|
|
|
|
/*
|
|
* Use default sizing.
|
|
*/
|
|
if (ptiCurrent->ppi->usi.dwFlags & STARTF_USESIZE) {
|
|
fStartup = TRUE;
|
|
rcSave.right = ptiCurrent->ppi->usi.dwXSize;
|
|
rcSave.bottom = ptiCurrent->ppi->usi.dwYSize;
|
|
} else {
|
|
rcSave.right = rc.right - x;
|
|
rcSave.bottom = rc.bottom - y;
|
|
}
|
|
fDefPos = TRUE;
|
|
|
|
} else if (fDefPos) {
|
|
|
|
/*
|
|
* The app wants default positioning but not default sizing.
|
|
* Make sure that it's still entirely visible.
|
|
*/
|
|
dx = (rcSave.left + rcSave.right) - gpDispInfo->rcScreen.right;
|
|
dy = (rcSave.top + rcSave.bottom) - gpDispInfo->rcScreen.bottom;
|
|
if (dx > 0) {
|
|
x -= dx;
|
|
rcSave.left = x;
|
|
if (rcSave.left < 0)
|
|
rcSave.left = x = 0;
|
|
}
|
|
|
|
if (dy > 0) {
|
|
y -= dy;
|
|
rcSave.top = y;
|
|
if (rcSave.top < 0)
|
|
rcSave.top = y = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we have used any startup postitions, turn off the startup
|
|
* info so we don't use it again.
|
|
*/
|
|
if (fStartup) {
|
|
ptiCurrent->ppi->usi.dwFlags &=
|
|
~(STARTF_USESIZE | STARTF_USEPOSITION);
|
|
}
|
|
|
|
/*
|
|
* Position Popup Windows
|
|
*/
|
|
|
|
if (TestwndPopup(pwnd)) {
|
|
// LATER: Why is this test necessary? Can one create a popup desktop?
|
|
if (pwnd != _GetDesktopWindow()) {
|
|
|
|
/*
|
|
* Force the clipsiblings/overlap style.
|
|
*/
|
|
SetWF(pwnd, WFCLIPSIBLINGS);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Shove in those default style bits.
|
|
*/
|
|
*(((WORD *)&pwnd->style) + 1) |= mask;
|
|
|
|
/*
|
|
* Menu/SysMenu Stuff
|
|
*/
|
|
|
|
/*
|
|
* If there is no menu handle given and it's not a child window but
|
|
* there is a class menu, use the class menu.
|
|
*/
|
|
if (pMenu == NULL && !fChild && (pcls->lpszMenuName != NULL)) {
|
|
UNICODE_STRING strMenuName;
|
|
|
|
RtlInitUnicodeStringOrId(&strMenuName, pcls->lpszMenuName);
|
|
pMenu = xxxClientLoadMenu(pcls->hModule, &strMenuName);
|
|
csex.cs.hMenu = PtoH(pMenu);
|
|
|
|
/*
|
|
* This load fails if the caller does not have DESKTOP_CREATEMENU
|
|
* permission.
|
|
*/
|
|
|
|
/* LATER
|
|
* 21-May-1991 mikeke
|
|
* but that's ok they will just get a window without a menu
|
|
*/
|
|
//if (pMenu == NULL)
|
|
// goto MemError;
|
|
}
|
|
|
|
/*
|
|
* Store the menu handle.
|
|
*/
|
|
if (TestwndChild(pwnd)) {
|
|
|
|
/*
|
|
* It's an id in this case.
|
|
*/
|
|
pwnd->spmenu = pMenu;
|
|
} else {
|
|
|
|
/*
|
|
* It's a real handle in this case.
|
|
*/
|
|
Lock(&(pwnd->spmenu), pMenu);
|
|
}
|
|
|
|
// LATER does this work?
|
|
/*
|
|
* Delete the Close menu item if directed.
|
|
*/
|
|
if (TestCF(pwnd, CFNOCLOSE)) {
|
|
|
|
/*
|
|
* Do this by position since the separator does not have an ID.
|
|
*/
|
|
// LATER mikeke why is _GetSystemMenu() returning NULL?
|
|
pMenu = _GetSystemMenu(pwnd, FALSE);
|
|
if (pMenu != NULL) {
|
|
#ifdef MEMPHIS_MENUS
|
|
TL tlpMenu;
|
|
|
|
ThreadLock(pMenu, &tlpMenu);
|
|
xxxDeleteMenu(pMenu, 5, MF_BYPOSITION);
|
|
xxxDeleteMenu(pMenu, 5, MF_BYPOSITION);
|
|
ThreadUnlock(&tlpMenu);
|
|
#else // MEMPHIS_MENUS
|
|
_DeleteMenu(pMenu, 5, MF_BYPOSITION);
|
|
_DeleteMenu(pMenu, 5, MF_BYPOSITION);
|
|
#endif //MEMPHIS_MENUS
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parent/Owner Stuff
|
|
*/
|
|
|
|
/*
|
|
* If this isn't a child window, reset the Owner/Parent info.
|
|
*/
|
|
if (!fChild) {
|
|
Lock(&(pwnd->spwndLastActive), pwnd);
|
|
if ((pwndParent != NULL) &&
|
|
(pwndParent != pwndParent->head.rpdesk->pDeskInfo->spwnd)) {
|
|
|
|
Lock(&(pwnd->spwndOwner), GetTopLevelWindow(pwndParent));
|
|
if (pwnd->spwndOwner && TestWF(pwnd->spwndOwner, WEFTOPMOST)) {
|
|
|
|
/*
|
|
* If this window's owner is a topmost window, then it has to
|
|
* be one also since a window must be above its owner.
|
|
*/
|
|
SetWF(pwnd, WEFTOPMOST);
|
|
}
|
|
|
|
/*
|
|
* If this is a owner window on another thread, share input
|
|
* state so this window gets z-ordered correctly.
|
|
*/
|
|
#ifdef FE_IME
|
|
if (atomT != gpsi->atomSysClass[ICLS_IME] &&
|
|
pwnd->spwndOwner != NULL &&
|
|
#else
|
|
if (pwnd->spwndOwner != NULL &&
|
|
#endif
|
|
GETPTI(pwnd->spwndOwner) != ptiCurrent) {
|
|
_AttachThreadInput(ptiCurrent, GETPTI(pwnd->spwndOwner), TRUE);
|
|
}
|
|
|
|
} else {
|
|
pwnd->spwndOwner = NULL;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (ptiCurrent->rpdesk != NULL) {
|
|
UserAssert(!(ptiCurrent->rpdesk->dwDTFlags & (DF_DESTROYED | DF_DESKWNDDESTROYED | DF_DYING)));
|
|
}
|
|
#endif
|
|
pwndParent = _GetDesktopWindow();
|
|
ThreadLockWithPti(ptiCurrent, pwndParent, &tlpwndParent);
|
|
fLockParent = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Store backpointer to parent.
|
|
*/
|
|
Lock(&(pwnd->spwndParent), pwndParent);
|
|
|
|
/*
|
|
* Final Window Positioning
|
|
*/
|
|
|
|
if (!TestWF(pwnd, WFWIN31COMPAT)) {
|
|
/*
|
|
* BACKWARD COMPATIBILITY HACK
|
|
*
|
|
* In 3.0, CS_PARENTDC overrides WS_CLIPCHILDREN and WS_CLIPSIBLINGS,
|
|
* but only if the parent is not WS_CLIPCHILDREN.
|
|
* This behavior is required by PowerPoint and Charisma, among others.
|
|
*/
|
|
if ((pcls->style & CS_PARENTDC) &&
|
|
!TestWF(pwndParent, WFCLIPCHILDREN)) {
|
|
#ifdef DEBUG
|
|
if (TestWF(pwnd, WFCLIPCHILDREN))
|
|
RIPMSG0(RIP_WARNING, "WS_CLIPCHILDREN overridden by CS_PARENTDC");
|
|
if (TestWF(pwnd, WFCLIPSIBLINGS))
|
|
RIPMSG0(RIP_WARNING, "WS_CLIPSIBLINGS overridden by CS_PARENTDC");
|
|
#endif
|
|
ClrWF(pwnd, (WFCLIPCHILDREN | WFCLIPSIBLINGS));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If this is a child window being created in a parent window
|
|
* of a different thread, but not on the desktop, attach their
|
|
* input streams together. [windows with WS_CHILD can be created
|
|
* on the desktop, that's why we check both the style bits
|
|
* and the parent window.]
|
|
*/
|
|
if (TestwndChild(pwnd) && (pwndParent != PWNDDESKTOP(pwnd)) &&
|
|
(ptiCurrent != GETPTI(pwndParent))) {
|
|
_AttachThreadInput(ptiCurrent, GETPTI(pwndParent), TRUE);
|
|
}
|
|
|
|
/*
|
|
* Make sure the window is between the minimum and maximum sizes.
|
|
*/
|
|
|
|
/*
|
|
* HACK ALERT!
|
|
* This sends WM_GETMINMAXINFO to a (tiled or sizable) window before
|
|
* it has been created (before it is sent WM_NCCREATE).
|
|
* Maybe some app expects this, so we nustn't reorder the messages.
|
|
*/
|
|
xxxAdjustSize(pwnd, &rcSave.right, &rcSave.bottom);
|
|
|
|
rcSave.right += rcSave.left;
|
|
rcSave.bottom += rcSave.top;
|
|
|
|
/*
|
|
* Calculate final window dimensions...
|
|
*/
|
|
pwnd->rcWindow.left = rcSave.left;
|
|
pwnd->rcWindow.right = rcSave.right;
|
|
pwnd->rcWindow.top = rcSave.top;
|
|
pwnd->rcWindow.bottom = rcSave.bottom;
|
|
|
|
/*
|
|
* If the window is an OWNDC window, or if it is CLASSDC and the
|
|
* class DC hasn't been created yet, create it now.
|
|
*/
|
|
if (TestCF2(pcls, CFCLASSDC) && pcls->pdce) {
|
|
pwnd->hdcOwn = pcls->pdce->hdc;
|
|
}
|
|
|
|
if (TestCF2(pcls, CFOWNDC) ||
|
|
(TestCF2(pcls, CFCLASSDC) && pcls->pdce == NULL)) {
|
|
pwnd->hdcOwn = CreateCacheDC(pwnd, DCX_OWNDC | DCX_NEEDFONT);
|
|
if (pwnd->hdcOwn == 0) {
|
|
goto MemError;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update the create struct now that we've modified some passed in
|
|
* parameters.
|
|
*/
|
|
csex.cs.x = x;
|
|
csex.cs.y = y;
|
|
csex.cs.cx = cx;
|
|
csex.cs.cy = cy;
|
|
|
|
/*
|
|
* Send a NCCREATE message to the window.
|
|
*/
|
|
if (!xxxSendMessage(pwnd, WM_NCCREATE, 0L, (LONG)&csex)) {
|
|
|
|
MemError:
|
|
|
|
#ifdef DEBUG
|
|
if (HIWORD(pstrClass) == 0) {
|
|
RIPMSG2(RIP_WARNING,
|
|
(pwndParent) ?
|
|
"xxxCreateWindowExWOW failed, Class=%#.4x, ID=%d" :
|
|
"xxxCreateWindowExWOW failed, Class=%#.4x",
|
|
LOWORD(pstrClass),
|
|
(int) pMenu);
|
|
} else {
|
|
RIPMSG2(RIP_WARNING,
|
|
(pwndParent) ?
|
|
"xxxCreateWindowExWOW failed, Class=\"%s\", ID=%d" :
|
|
"xxxCreateWindowExWOW failed, Class=\"%s\"",
|
|
pcls->lpszAnsiClassName,
|
|
(int) pMenu);
|
|
}
|
|
#endif
|
|
|
|
if (fLockParent)
|
|
ThreadUnlock(&tlpwndParent);
|
|
|
|
/*
|
|
* Set the state as destroyed so any z-ordering events will be ignored.
|
|
* We cannot NULL out the owner field until WM_NCDESTROY is send or
|
|
* apps like Rumba fault (they call GetParent after every message)
|
|
*/
|
|
SetWF(pwnd, WFDESTROYED);
|
|
|
|
xxxFreeWindow(pwnd, &tlpwnd);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* WM_NCCREATE processing may have changed the window text. Change
|
|
* the CREATESTRUCT to point to the real window text.
|
|
*
|
|
* MSMoney needs this because it clears the window and we need to
|
|
* reflect the new name back into the cs structure.
|
|
* A better thing to do would be to have a pointer to the CREATESTRUCT
|
|
* within the window itself so that DefWindowProc can change the
|
|
* the window name in the CREATESTRUCT to point to the real name and
|
|
* this funky check is no longer needed.
|
|
*
|
|
* DefSetText converts a pointer to NULL to a NULL title so
|
|
* we don't want to over-write cs.lpszName if it was a pointer to
|
|
* a NULL string and pName is NULL. Approach Database for Windows creates
|
|
* windows with a pointer to NULL and then accesses the pointer later
|
|
* during WM_CREATE
|
|
*/
|
|
if (TestWF(pwnd, WFTITLESET))
|
|
if (!(csex.strName.Buffer != NULL && csex.strName.Length == 0 &&
|
|
pwnd->strName.Buffer == NULL)) {
|
|
csex.cs.lpszName = pwnd->strName.Buffer;
|
|
RtlCopyMemory(&csex.strName, &pwnd->strName, sizeof(LARGE_STRING));
|
|
}
|
|
|
|
/*
|
|
* The Window is now officially "created." Change the relevant global
|
|
* stuff.
|
|
*/
|
|
|
|
#ifdef FE_IME
|
|
/*
|
|
* Create per thread default IME window.
|
|
*/
|
|
if (ptiCurrent->spwndDefaultIme == NULL) {
|
|
Lock( &(ptiCurrent->spwndDefaultIme),
|
|
xxxCreateDefaultImeWindow(pwnd, atomT, hInstance));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Update the Parent/Child linked list.
|
|
*/
|
|
if (pwndParent != NULL) {
|
|
if (!fChild) {
|
|
|
|
/*
|
|
* If this is a top-level window, and it's not part of the
|
|
* topmost pile of windows, then we have to make sure it
|
|
* doesn't go on top of any of the topmost windows.
|
|
*
|
|
* If he's trying to put the window on the top, or trying
|
|
* to insert it after one of the topmost windows, insert
|
|
* it after the last topmost window in the pile.
|
|
*/
|
|
if (!TestWF(pwnd, WEFTOPMOST)) {
|
|
if (pwndZOrder == PWND_TOP ||
|
|
TestWF(pwndZOrder, WEFTOPMOST)) {
|
|
pwndZOrder = CalcForegroundInsertAfter(pwnd);
|
|
}
|
|
} else {
|
|
pwndHardError = GETTOPMOSTINSERTAFTER(pwnd);
|
|
if (pwndHardError != NULL) {
|
|
pwndZOrder = pwndHardError;
|
|
}
|
|
}
|
|
}
|
|
|
|
LinkWindow(pwnd, pwndZOrder, &pwndParent->spwndChild);
|
|
}
|
|
|
|
/*
|
|
* Message Sending
|
|
*/
|
|
|
|
/*
|
|
* Send a NCCALCSIZE message to the window and have it return the official
|
|
* size of its client area.
|
|
*/
|
|
rc = pwnd->rcWindow;
|
|
xxxSendMessage(pwnd, WM_NCCALCSIZE, 0L, (LONG)&rc);
|
|
pwnd->rcClient = rc;
|
|
|
|
/*
|
|
* Send a CREATE message to the window.
|
|
*/
|
|
if (xxxSendMessage(pwnd, WM_CREATE, 0L, (LONG)&csex) == -1L) {
|
|
#ifdef DEBUG
|
|
if (HIWORD(pstrClass) == 0) {
|
|
RIPMSG1(RIP_WARNING,
|
|
"CreateWindow() send of WM_CREATE failed, Class = 0x%x",
|
|
LOWORD(pstrClass));
|
|
} else {
|
|
RIPMSG1(RIP_WARNING,
|
|
"CreateWindow() send of WM_CREATE failed, Class = \"%s\"",
|
|
pcls->lpszAnsiClassName);
|
|
}
|
|
#endif
|
|
|
|
if (fLockParent)
|
|
ThreadUnlock(&tlpwndParent);
|
|
if (ThreadUnlock(&tlpwnd))
|
|
xxxDestroyWindow(pwnd);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* If this is a Tiled/Overlapped window, don't send size or move msgs yet.
|
|
*/
|
|
if (!TestWF(pwnd, WFSENDSIZEMOVE)) {
|
|
xxxSendSizeMessage(pwnd, SIZENORMAL);
|
|
|
|
if (pwndParent != NULL) {
|
|
rc.left -= pwndParent->rcClient.left;
|
|
rc.top -= pwndParent->rcClient.top;
|
|
}
|
|
|
|
xxxSendMessage(pwnd, WM_MOVE, 0L, MAKELONG(rc.left, rc.top));
|
|
}
|
|
|
|
/*
|
|
* Min/Max Stuff
|
|
*/
|
|
|
|
/*
|
|
* If app specified either min/max style, then we must call our minmax
|
|
* code to get it all set up correctly so that when the show is done,
|
|
* the window is displayed right. The TRUE param to minmax means keep
|
|
* hidden.
|
|
*/
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
SetMinimize(pwnd, SMIN_CLEAR);
|
|
xxxMinMaximize(pwnd, SW_SHOWMINNOACTIVE, MAKELONG(TRUE, gfAnimate));
|
|
} else if (TestWF(pwnd, WFMAXIMIZED)) {
|
|
ClrWF(pwnd, WFMAXIMIZED);
|
|
xxxMinMaximize(pwnd, SW_SHOWMAXIMIZED, MAKELONG(TRUE, gfAnimate));
|
|
}
|
|
|
|
/*
|
|
* Send notification if child
|
|
*/
|
|
|
|
// LATER 15-Aug-1991 mikeke
|
|
// pointer passed as a word here
|
|
|
|
if (fChild && !TestWF(pwnd, WEFNOPARENTNOTIFY) &&
|
|
(pwnd->spwndParent != NULL)) {
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwnd->spwndParent, &tlpwndParentT);
|
|
xxxSendMessage(pwnd->spwndParent, WM_PARENTNOTIFY,
|
|
MAKELONG(WM_CREATE, (UINT)pwnd->spmenu), (LONG)HWq(pwnd));
|
|
ThreadUnlock(&tlpwndParentT);
|
|
}
|
|
|
|
/*
|
|
* Show the Window
|
|
*/
|
|
if (style & WS_VISIBLE) {
|
|
xxxShowWindow(pwnd, MAKELONG(sw, gfAnimate));
|
|
}
|
|
|
|
/*
|
|
* Try and set the application's hot key. Use the Win95 logic of
|
|
* looking for the first tiled and/or APPWINDOW to be created by
|
|
* this process.
|
|
*/
|
|
if (TestwndTiled(pwnd) || TestWF(pwnd, WEFAPPWINDOW)) {
|
|
if (ptiCurrent->ppi->dwHotkey) {
|
|
/*
|
|
* Ignore hot keys for WowExe the first thread of a wow process.
|
|
*/
|
|
if (!(ptiCurrent->TIF_flags & TIF_16BIT) || (ptiCurrent->ppi->cThreads > 1)) {
|
|
#ifdef LATER
|
|
/*
|
|
* Win95 sets the hot key directly, we on the other hand send
|
|
* a WM_SETHOTKEY message to the app. Which is right?
|
|
*/
|
|
DWP_SetHotKey(pwnd, ptiCurrent->ppi->dwHotkey);
|
|
#else
|
|
xxxSendMessage(pwnd, WM_SETHOTKEY, ptiCurrent->ppi->dwHotkey, 0);
|
|
#endif
|
|
ptiCurrent->ppi->dwHotkey = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* check for a window being created full screen
|
|
*
|
|
* Note the check for a non-NULL pdeskParent -- this is important for CreateWindowStation
|
|
*/
|
|
if ((pwnd->head.rpdesk != NULL) && !TestWF(pwnd, WFCHILD) && !TestWF(pwnd, WEFTOOLWINDOW))
|
|
xxxCheckFullScreen(pwnd, &pwnd->rcWindow);
|
|
|
|
|
|
if (fLockParent)
|
|
ThreadUnlock(&tlpwndParent);
|
|
|
|
if (ThreadUnlock(&tlpwnd))
|
|
return pwnd;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* SetTiledRect
|
|
*
|
|
* History:
|
|
* 10-19-90 darrinm Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
void SetTiledRect(
|
|
PWND pwnd,
|
|
LPRECT lprc)
|
|
{
|
|
int x, y;
|
|
RECT rcT;
|
|
|
|
/*
|
|
* Get available desktop area, minus minimized spacing area.
|
|
*/
|
|
GetRealClientRect(PWNDDESKTOP(pwnd), &rcT, GRC_MINWNDS);
|
|
|
|
/*
|
|
* Normalized rectangle is 3/4 width, 3/4 height of desktop area. We
|
|
* offset it based on the value of iwndStack for cascading.
|
|
*/
|
|
|
|
/*
|
|
* We want the left edge of the new window to align with the
|
|
* right edge of the old window's system menu. And we want the
|
|
* top edge of the new window to align with the bottom edge of the
|
|
* selected caption area (caption height - cyBorder) of the old
|
|
* window.
|
|
*/
|
|
x = iwndStack * (SYSMET(CXSIZEFRAME) + SYSMET(CXSIZE));
|
|
y = iwndStack * (SYSMET(CYSIZEFRAME) + SYSMET(CYSIZE));
|
|
|
|
/*
|
|
* If below upper top left 1/4 of free area, reset.
|
|
*/
|
|
if ( (x > ((rcT.right-rcT.left) / 4)) ||
|
|
(y > ((rcT.bottom-rcT.top) / 4)) ) {
|
|
iwndStack = 0;
|
|
x = 0;
|
|
y = 0;
|
|
}
|
|
|
|
/*
|
|
* Get starting position
|
|
*/
|
|
x += rcT.left;
|
|
y += rcT.top;
|
|
|
|
lprc->left = x;
|
|
lprc->top = y;
|
|
lprc->right = x + MultDiv(rcT.right-rcT.left, 3, 4);
|
|
lprc->bottom = y + MultDiv(rcT.bottom-rcT.top, 3, 4);
|
|
|
|
/*
|
|
* Increment the count of stacked windows.
|
|
*/
|
|
iwndStack++;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxAdjustSize
|
|
*
|
|
* Make sure that *lpcx and *lpcy are within the legal limits.
|
|
*
|
|
* History:
|
|
* 10-19-90 darrinm Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
void xxxAdjustSize(
|
|
PWND pwnd,
|
|
LPINT lpcx,
|
|
LPINT lpcy)
|
|
{
|
|
POINT ptmin, ptmax;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* If this window is sizeable or if this window is tiled, check size
|
|
*/
|
|
if (TestwndTiled(pwnd) || TestWF(pwnd, WFSIZEBOX)) {
|
|
|
|
/*
|
|
* Get size info from pwnd
|
|
*/
|
|
xxxInitSendValidateMinMaxInfo(pwnd);
|
|
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
ptmin.x = (int)rgptMinMaxWnd[MMI_MINSIZE].x;
|
|
ptmin.y = (int)rgptMinMaxWnd[MMI_MINSIZE].y;
|
|
ptmax.x = (int)rgptMinMaxWnd[MMI_MAXSIZE].x;
|
|
ptmax.y = (int)rgptMinMaxWnd[MMI_MAXSIZE].y;
|
|
|
|
} else {
|
|
ptmin.x = (int)rgptMinMaxWnd[MMI_MINTRACK].x;
|
|
ptmin.y = (int)rgptMinMaxWnd[MMI_MINTRACK].y;
|
|
ptmax.x = (int)rgptMinMaxWnd[MMI_MAXTRACK].x;
|
|
ptmax.y = (int)rgptMinMaxWnd[MMI_MAXTRACK].y;
|
|
}
|
|
|
|
//
|
|
// Make sure we're less than the max, and greater than the min
|
|
//
|
|
*lpcx = max(ptmin.x, min(*lpcx, ptmax.x));
|
|
*lpcy = max(ptmin.y, min(*lpcy, ptmax.y));
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* LinkWindow
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
void LinkWindow(
|
|
PWND pwnd,
|
|
PWND pwndInsert,
|
|
PWND *ppwndFirst)
|
|
{
|
|
if (*ppwndFirst == pwnd) {
|
|
RIPMSG0(RIP_WARNING, "Attempting to link a window to itself");
|
|
return;
|
|
}
|
|
if (pwndInsert == PWND_TOP) {
|
|
|
|
/*
|
|
* We are at the top of the list.
|
|
*/
|
|
LinkTop:
|
|
#if DBG
|
|
if (pwnd->spwndParent)
|
|
UserAssert(&pwnd->spwndParent->spwndChild == ppwndFirst);
|
|
#endif
|
|
|
|
Lock(&pwnd->spwndNext, *ppwndFirst);
|
|
Lock(ppwndFirst, pwnd);
|
|
} else {
|
|
if (pwndInsert == PWND_BOTTOM) {
|
|
|
|
/*
|
|
* Find bottom-most window.
|
|
*/
|
|
if (((pwndInsert = *ppwndFirst) == NULL) ||
|
|
TestWF(pwndInsert, WFBOTTOMMOST))
|
|
goto LinkTop;
|
|
|
|
/*
|
|
* Since we know (ahem) that there's only one bottommost window,
|
|
* we can't possibly insert after it. Either we're inserting
|
|
* the bottomost window, in which case it's not in the linked
|
|
* list currently, or we're inserting some other window.
|
|
*/
|
|
|
|
while (pwndInsert->spwndNext != NULL) {
|
|
if (TestWF(pwndInsert->spwndNext, WFBOTTOMMOST)) {
|
|
#ifdef DEBUG
|
|
UserAssert(pwnd != pwndInsert->spwndNext);
|
|
if (TestWF(pwnd, WFBOTTOMMOST))
|
|
UserAssert(FALSE);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
pwndInsert = pwndInsert->spwndNext;
|
|
}
|
|
}
|
|
|
|
UserAssert(pwnd != pwndInsert);
|
|
UserAssert(pwnd != pwndInsert->spwndNext);
|
|
UserAssert(!TestWF(pwndInsert, WFDESTROYED));
|
|
UserAssert(pwnd->spwndParent == pwndInsert->spwndParent);
|
|
|
|
Lock(&pwnd->spwndNext, pwndInsert->spwndNext);
|
|
Lock(&pwndInsert->spwndNext, pwnd);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxDestroyWindow (API)
|
|
*
|
|
* Destroy the specified window. The window passed in is not thread locked.
|
|
*
|
|
* History:
|
|
* 10-20-90 darrinm Ported from Win 3.0 sources.
|
|
* 02-07-91 DavidPe Added Win 3.1 WH_CBT support.
|
|
* 02-11-91 JimA Added access checks.
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxDestroyWindow(
|
|
PWND pwnd)
|
|
{
|
|
PMENUSTATE pMenuState;
|
|
PTHREADINFO pti;
|
|
TL tlpwnd;
|
|
TL tlpwndOwner;
|
|
TL tlpwndParent;
|
|
BOOL fAlreadyDestroyed;
|
|
|
|
pti = PtiCurrent();
|
|
ThreadLockWithPti(pti, pwnd, &tlpwnd);
|
|
|
|
/*
|
|
* First, if this handle has been marked for destruction, that means it
|
|
* is possible that the current thread is not its owner! (meaning we're
|
|
* being called from a handle unlock call). In this case, set the owner
|
|
* to be the current thread so inter-thread send messages occur.
|
|
*/
|
|
if (fAlreadyDestroyed = HMIsMarkDestroy(pwnd))
|
|
HMChangeOwnerThread(pwnd, pti);
|
|
|
|
/*
|
|
* Ensure that we can destroy the window. JIMA: no other process or thread
|
|
* should be able to destroy any other process or thread's window.
|
|
*/
|
|
if (pti != GETPTI(pwnd)) {
|
|
RIPERR0(ERROR_ACCESS_DENIED,
|
|
RIP_WARNING,
|
|
"Access denied in xxxDestroyWindow");
|
|
|
|
goto FalseReturn;
|
|
}
|
|
|
|
/*
|
|
* First ask the CBT hook if we can destroy this window.
|
|
* If this object has already been destroyed OR this thread is currently
|
|
* in cleanup mode, *do not* make any callbacks via hooks to the client
|
|
* process.
|
|
*/
|
|
if (!fAlreadyDestroyed && !(pti->TIF_flags & TIF_INCLEANUP) &&
|
|
IsHooked(pti, WHF_CBT)) {
|
|
if (xxxCallHook(HCBT_DESTROYWND, (DWORD)HWq(pwnd), 0, WH_CBT)) {
|
|
goto FalseReturn;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the window we are destroying is in menu mode, get out
|
|
*/
|
|
pMenuState = GetpMenuState(pwnd);
|
|
if ((pMenuState != NULL)
|
|
&& (pwnd == pMenuState->pGlobalPopupMenu->spwndNotify)) {
|
|
/*
|
|
* Kill hwnd notify so we don't call into the app again.
|
|
*/
|
|
MNEndMenuStateNotify (pMenuState);
|
|
Unlock(&(pMenuState->pGlobalPopupMenu->spwndNotify));
|
|
xxxEndMenu(pMenuState);
|
|
}
|
|
|
|
|
|
if (ghwndSwitch == HWq(pwnd))
|
|
ghwndSwitch = NULL;
|
|
|
|
if (!TestWF(pwnd, WFCHILD) && (pwnd->spwndOwner == NULL)) {
|
|
|
|
if (TestWF(pwnd, WFHASPALETTE)) {
|
|
|
|
TL tlpwndDesktop;
|
|
PWND pwndDesktop;
|
|
|
|
/*
|
|
* Set the desktop-flag to allow it to refresh
|
|
* itself. This is to make sure any app which
|
|
* takes the static-colors won't leave us in a
|
|
* screwy state. This bit is cleared in the
|
|
* desktop-wnd-proc.
|
|
*/
|
|
pwnd->head.rpdesk->dwDTFlags |= DTF_NEEDSREDRAW;
|
|
|
|
/*
|
|
* if the app is going away (ie we are destoying its top-level
|
|
* window), and the app was palette-using (at least the top-level
|
|
* window was), free up the system palette and send out a
|
|
* PALETTECHANGED message.
|
|
*/
|
|
|
|
GreRealizeDefaultPalette(gpDispInfo->hdcScreen, TRUE);
|
|
|
|
xxxBroadcastMessage(pwnd->head.rpdesk->pDeskInfo->spwnd, WM_PALETTECHANGED,
|
|
(DWORD)HWq(pwnd), 0L, BMSG_SENDNOTIFYMSGPROCESS, NULL);
|
|
|
|
pwndDesktop = grpdeskRitInput->pDeskInfo->spwnd;
|
|
if (pwndDesktop != NULL) {
|
|
ThreadLockAlwaysWithPti(pti, pwndDesktop, &tlpwndDesktop);
|
|
xxxSendNotifyMessage(pwndDesktop, WM_PALETTECHANGED, (DWORD)HWq(pwnd), 0);
|
|
ThreadUnlock(&tlpwndDesktop);
|
|
}
|
|
|
|
/*
|
|
* Walk through the SPB list (the saved bitmaps under windows
|
|
* with the CS_SAVEBITS style) discarding all bitmaps
|
|
*/
|
|
FreeAllSpbs(pwnd);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Disassociate thread state if this is top level and owned by a different
|
|
* thread. This is done to begin with so these windows z-order together.
|
|
*/
|
|
#ifdef FE_IME
|
|
if (pwnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME] &&
|
|
!TestwndChild(pwnd) && pwnd->spwndOwner != NULL &&
|
|
#else
|
|
if (!TestwndChild(pwnd) && pwnd->spwndOwner != NULL &&
|
|
#endif
|
|
GETPTI(pwnd->spwndOwner) != GETPTI(pwnd)) {
|
|
_AttachThreadInput(GETPTI(pwnd), GETPTI(pwnd->spwndOwner), FALSE);
|
|
}
|
|
|
|
/*
|
|
* If we are a child window without the WS_NOPARENTNOTIFY style, send
|
|
* the appropriate notification message.
|
|
*
|
|
* NOTE: Although it would appear that we are illegally cramming a
|
|
* a WORD (WM_DESTROY) and a DWORD (pwnd->spmenu) into a single LONG
|
|
* (wParam) this isn't really the case because we first test if this
|
|
* is a child window. The pMenu field in a child window is really
|
|
* the window's id and only the LOWORD is significant.
|
|
*/
|
|
if (TestWF(pwnd, WFCHILD) && !TestWF(pwnd, WEFNOPARENTNOTIFY) &&
|
|
pwnd->spwndParent != NULL) {
|
|
|
|
ThreadLockAlwaysWithPti(pti, pwnd->spwndParent, &tlpwndParent);
|
|
xxxSendMessage(pwnd->spwndParent, WM_PARENTNOTIFY,
|
|
MAKELONG(WM_DESTROY, (UINT)pwnd->spmenu), (LONG)HWq(pwnd));
|
|
ThreadUnlock(&tlpwndParent);
|
|
}
|
|
|
|
/*
|
|
* Mark this window as beginning the destroy process. This is necessary
|
|
* to prevent window-management calls such as ShowWindow or SetWindowPos
|
|
* from comming in and changing the visible-state of the window
|
|
* once we hide it. Otherwise, if the app attempts to make it
|
|
* visible, then we can get our vis-rgns screwed up once we truely
|
|
* destroy the window.
|
|
*
|
|
* Don't mark the mother desktop with this bit. The xxxSetWindowPos()
|
|
* will fail for this window, and thus possibly cause an assertion
|
|
* in the xxxFreeWindow() call when we check for the visible-bit.
|
|
*/
|
|
if (pwnd->spwndParent && (pwnd->spwndParent->head.rpdesk != NULL))
|
|
SetWF(pwnd, WFINDESTROY);
|
|
|
|
/*
|
|
* Hide the window.
|
|
*/
|
|
if (TestWF(pwnd, WFVISIBLE)) {
|
|
if (TestWF(pwnd, WFCHILD)) {
|
|
xxxShowWindow(pwnd, MAKELONG(SW_HIDE, gfAnimate));
|
|
} else {
|
|
|
|
/*
|
|
* Hide this window without activating anyone else.
|
|
*/
|
|
xxxSetWindowPos(pwnd, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW |
|
|
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
|
|
}
|
|
} else if (IsTrayWindow(pwnd)) {
|
|
PostShellHookMessages(HSHELL_WINDOWDESTROYED,
|
|
PtoHq( pwnd ));
|
|
}
|
|
|
|
/*
|
|
* Destroy any owned windows.
|
|
*/
|
|
if (!TestWF(pwnd, WFCHILD)) {
|
|
xxxDW_DestroyOwnedWindows(pwnd);
|
|
|
|
/*
|
|
* And remove the window hot-key, if it has one
|
|
*/
|
|
DWP_SetHotKey(pwnd, 0);
|
|
}
|
|
|
|
/*
|
|
* If the window has already been destroyed, don't muck with
|
|
* activation because we may already be in the middle of
|
|
* an activation event. Changing activation now may cause us
|
|
* to leave our critical section while holding the display lock.
|
|
* This will result in a deadlock if another thread gets the
|
|
* critical section before we do and attempts to lock the
|
|
* display.
|
|
*/
|
|
if (!fAlreadyDestroyed) {
|
|
|
|
BOOL fActivePaletteWindow = FALSE;
|
|
|
|
/*
|
|
* If hiding the active window, activate someone else.
|
|
* This call is strategically located after DestroyOwnedWindows() so we
|
|
* don't end up activating our owner window.
|
|
*
|
|
* If the window is a popup, try to activate his creator not the top
|
|
* window in the Z list.
|
|
*/
|
|
if (pwnd == pti->pq->spwndActive) {
|
|
if (TestWF(pwnd, WFPOPUP) && pwnd->spwndOwner) {
|
|
|
|
ThreadLockAlwaysWithPti(pti, pwnd->spwndOwner, &tlpwndOwner);
|
|
if (!xxxActivateWindow(pwnd->spwndOwner, AW_TRY)) {
|
|
if (pwnd == pti->pq->spwndActive) {
|
|
Unlock(&pti->pq->spwndActive);
|
|
Unlock(&pti->pq->spwndFocus);
|
|
InternalDestroyCaret();
|
|
}
|
|
}
|
|
ThreadUnlock(&tlpwndOwner);
|
|
|
|
} else {
|
|
|
|
if (!xxxActivateWindow(pwnd, AW_SKIP) ||
|
|
(pwnd == pti->pq->spwndActive)) {
|
|
|
|
|
|
Unlock(&pti->pq->spwndActive);
|
|
Unlock(&pti->pq->spwndFocus);
|
|
InternalDestroyCaret();
|
|
}
|
|
|
|
fActivePaletteWindow = TestWF(pwnd, WFHASPALETTE);
|
|
}
|
|
|
|
} else if ((pti->pq->spwndActive == NULL) && (gpqForeground == pti->pq)) {
|
|
xxxActivateWindow(pwnd, AW_SKIP);
|
|
}
|
|
|
|
if (fActivePaletteWindow && !(pti->TIF_flags & TIF_INCLEANUP))
|
|
xxxFlushPalette(pwnd);
|
|
}
|
|
|
|
/*
|
|
* fix last active popup
|
|
*/
|
|
{
|
|
PWND pwndOwner = pwnd->spwndOwner;
|
|
|
|
if (pwndOwner != NULL) {
|
|
while (pwndOwner->spwndOwner != NULL) {
|
|
pwndOwner = pwndOwner->spwndOwner;
|
|
}
|
|
|
|
if (pwnd == pwndOwner->spwndLastActive) {
|
|
Lock(&(pwndOwner->spwndLastActive), pwnd->spwndOwner);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send destroy messages before the WindowLockStart in case
|
|
* he tries to destroy windows as a result.
|
|
*/
|
|
xxxDW_SendDestroyMessages(pwnd);
|
|
|
|
#ifdef FE_IME
|
|
/*
|
|
* Check the owner of IME window again.
|
|
*/
|
|
if (pti->spwndDefaultIme != NULL &&
|
|
!TestCF(pwnd, CFIME) &&
|
|
pwnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME]) {
|
|
|
|
if (!TestWF(pwnd, WFCHILD)) {
|
|
if (ImeCanDestroyDefIME(pti->spwndDefaultIme, pwnd))
|
|
xxxDestroyWindow(pti->spwndDefaultIme);
|
|
}
|
|
else if (pwnd->spwndParent != NULL) {
|
|
if (ImeCanDestroyDefIMEforChild(pti->spwndDefaultIme, pwnd))
|
|
xxxDestroyWindow(pti->spwndDefaultIme);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((pwnd->spwndParent != NULL) && !fAlreadyDestroyed) {
|
|
|
|
/*
|
|
* TestwndChild() on checks to WFCHILD bit. Make sure this
|
|
* window wasn't SetParent()'ed to the desktop as well.
|
|
*/
|
|
if (TestwndChild(pwnd) && (pwnd->spwndParent != PWNDDESKTOP(pwnd)) &&
|
|
(GETPTI(pwnd) != GETPTI(pwnd->spwndParent))) {
|
|
_AttachThreadInput(GETPTI(pwnd), GETPTI(pwnd->spwndParent), FALSE);
|
|
}
|
|
|
|
UnlinkWindow(pwnd, &(pwnd->spwndParent->spwndChild));
|
|
}
|
|
|
|
/*
|
|
* This in intended to check for a case where we destroy the window,
|
|
* but it's still listed as the active-window in the queue. This
|
|
* could cause problems in window-activation (see xxxActivateThisWindow)
|
|
* where we attempt to activate another window and in the process, try
|
|
* to deactivate this window (bad).
|
|
*/
|
|
#ifdef DEBUG
|
|
if (pwnd == pti->pq->spwndActive) {
|
|
RIPMSG1(RIP_WARNING, "xxxDestroyWindow: pwnd == pti->pq->spwndActive (%x)", pwnd);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Set the state as destroyed so any z-ordering events will be ignored.
|
|
* We cannot NULL out the owner field until WM_NCDESTROY is send or
|
|
* apps like Rumba fault (they call GetParent after every message)
|
|
*/
|
|
SetWF(pwnd, WFDESTROYED);
|
|
|
|
xxxFreeWindow(pwnd, &tlpwnd);
|
|
|
|
return TRUE;
|
|
|
|
FalseReturn:
|
|
ThreadUnlock(&tlpwnd);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxDW_DestroyOwnedWindows
|
|
*
|
|
* History:
|
|
* 10-20-90 darrinm Ported from Win 3.0 sources.
|
|
* 07-22-91 darrinm Re-ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
void xxxDW_DestroyOwnedWindows(
|
|
PWND pwndParent)
|
|
{
|
|
PWND pwnd, pwndDesktop;
|
|
PDESKTOP pdeskParent;
|
|
#ifdef FE_IME
|
|
PWND pwndDefaultIme = GETPTI(pwndParent)->spwndDefaultIme;
|
|
#endif
|
|
|
|
CheckLock(pwndParent);
|
|
|
|
if ((pdeskParent = pwndParent->head.rpdesk) == NULL)
|
|
return;
|
|
pwndDesktop = pdeskParent->pDeskInfo->spwnd;
|
|
|
|
/*
|
|
* During shutdown, the desktop owner window will be
|
|
* destroyed. In this case, pwndDesktop will be NULL.
|
|
*/
|
|
if (pwndDesktop == NULL)
|
|
return;
|
|
|
|
pwnd = pwndDesktop->spwndChild;
|
|
|
|
while (pwnd != NULL) {
|
|
if (pwnd->spwndOwner == pwndParent) {
|
|
#ifdef FE_IME
|
|
/*
|
|
* We don't destroy the IME window here.
|
|
*/
|
|
if (pwnd == pwndDefaultIme) {
|
|
Unlock(&pwnd->spwndOwner);
|
|
pwnd = pwnd->spwndNext;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If the window doesn't get destroyed, set its owner to NULL.
|
|
* A good example of this is trying to destroy a window created
|
|
* by another thread or process, but there are other cases.
|
|
*/
|
|
if (!xxxDestroyWindow(pwnd)) {
|
|
Unlock(&pwnd->spwndOwner);
|
|
}
|
|
|
|
/*
|
|
* Start the search over from the beginning since the app could
|
|
* have caused other windows to be created or activation/z-order
|
|
* changes.
|
|
*/
|
|
pwnd = pwndDesktop->spwndChild;
|
|
} else {
|
|
pwnd = pwnd->spwndNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxDW_SendDestroyMessages
|
|
*
|
|
* History:
|
|
* 10-20-90 darrinm Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
void xxxDW_SendDestroyMessages(
|
|
PWND pwnd)
|
|
{
|
|
PWND pwndChild;
|
|
PWND pwndNext;
|
|
TL tlpwndNext;
|
|
TL tlpwndChild;
|
|
PWINDOWSTATION pwinsta;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* Be sure the window gets any resulting messages before being destroyed.
|
|
*/
|
|
xxxCheckFocus(pwnd);
|
|
|
|
pwinsta = _GetProcessWindowStation(NULL);
|
|
if (pwinsta != NULL && pwnd == pwinsta->spwndClipOwner)
|
|
DisownClipboard();
|
|
|
|
/*
|
|
* Send the WM_DESTROY message.
|
|
*/
|
|
xxxSendMessage(pwnd, WM_DESTROY, 0L, 0L);
|
|
|
|
/*
|
|
* Now send destroy message to all children of pwnd.
|
|
* Enumerate down (pwnd->spwndChild) and sideways (pwnd->spwndNext).
|
|
* We do it this way because parents often assume that child windows still
|
|
* exist during WM_DESTROY message processing.
|
|
*/
|
|
pwndChild = pwnd->spwndChild;
|
|
|
|
while (pwndChild != NULL) {
|
|
|
|
pwndNext = pwndChild->spwndNext;
|
|
|
|
ThreadLock(pwndNext, &tlpwndNext);
|
|
|
|
ThreadLockAlways(pwndChild, &tlpwndChild);
|
|
xxxDW_SendDestroyMessages(pwndChild);
|
|
ThreadUnlock(&tlpwndChild);
|
|
pwndChild = pwndNext;
|
|
|
|
/*
|
|
* The unlock may nuke the next window. If so, get out.
|
|
*/
|
|
if (!ThreadUnlock(&tlpwndNext))
|
|
break;
|
|
}
|
|
|
|
xxxCheckFocus(pwnd);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxFW_DestroyAllChildren
|
|
*
|
|
* History:
|
|
* 11-06-90 darrinm Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
void xxxFW_DestroyAllChildren(
|
|
PWND pwnd)
|
|
{
|
|
PWND pwndChild;
|
|
TL tlpwndChild;
|
|
PTHREADINFO pti;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
CheckLock(pwnd);
|
|
|
|
while (pwnd->spwndChild != NULL) {
|
|
pwndChild = pwnd->spwndChild;
|
|
|
|
/*
|
|
* ThreadLock prior to the unlink in case pwndChild
|
|
* is already marked as destroyed.
|
|
*/
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwndChild, &tlpwndChild);
|
|
UnlinkWindow(pwndChild, &pwnd->spwndChild);
|
|
|
|
/*
|
|
* Set the state as destroyed so any z-ordering events will be ignored.
|
|
* We cannot NULL out the owner field until WM_NCDESTROY is send or
|
|
* apps like Rumba fault (they call GetParent after every message)
|
|
*/
|
|
SetWF(pwndChild, WFDESTROYED);
|
|
|
|
/*
|
|
* If the window belongs to another thread, post
|
|
* an event to let it know it should be destroyed.
|
|
* Otherwise, free the window.
|
|
*/
|
|
pti = GETPTI(pwndChild);
|
|
if (pti != ptiCurrent) {
|
|
PostEventMessage(pti, pti->pq, QEVENT_DESTROYWINDOW,
|
|
NULL, 0,
|
|
(DWORD)HWq(pwndChild), 0);
|
|
ThreadUnlock(&tlpwndChild);
|
|
} else {
|
|
xxxFreeWindow(pwndChild, &tlpwndChild);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UnlockNotifyWindow
|
|
*
|
|
* Walk down a menu and unlock all notify windows.
|
|
*
|
|
* History:
|
|
* 18-May-1994 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
VOID UnlockNotifyWindow(
|
|
PMENU pmenu)
|
|
{
|
|
PITEM pItem;
|
|
int i;
|
|
|
|
/*
|
|
* Go down the item list and unlock submenus.
|
|
*/
|
|
pItem = pmenu->rgItems;
|
|
for (i = pmenu->cItems; i--; ++pItem) {
|
|
|
|
if (pItem->spSubMenu != NULL)
|
|
UnlockNotifyWindow(pItem->spSubMenu);
|
|
}
|
|
|
|
Unlock(&pmenu->spwndNotify);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxFreeWindow
|
|
*
|
|
* History:
|
|
* 19-Oct-1990 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxFreeWindow(
|
|
PWND pwnd,
|
|
PTL ptlpwndFree)
|
|
{
|
|
PMENU pmenu;
|
|
PDCE *ppdce;
|
|
PDCE pdce;
|
|
PQMSG pqmsg;
|
|
HDC hdcT;
|
|
PPCLS ppcls;
|
|
UINT uDCERelease;
|
|
WORD fnid;
|
|
TL tlpdesk;
|
|
PWINDOWSTATION pwinsta = _GetProcessWindowStation(NULL);
|
|
PTHREADINFO pti = PtiCurrent();
|
|
PPROCESSINFO ppi;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* If the pwnd is any of the global shell-related windows,
|
|
* then we need to unlock them from the deskinfo.
|
|
*/
|
|
if (pwnd->head.rpdesk != NULL) {
|
|
if (pwnd == pwnd->head.rpdesk->pDeskInfo->spwndShell)
|
|
Unlock(&pwnd->head.rpdesk->pDeskInfo->spwndShell);
|
|
if (pwnd == pwnd->head.rpdesk->pDeskInfo->spwndBkGnd)
|
|
Unlock(&pwnd->head.rpdesk->pDeskInfo->spwndBkGnd);
|
|
if (pwnd == pwnd->head.rpdesk->pDeskInfo->spwndTaskman)
|
|
Unlock(&pwnd->head.rpdesk->pDeskInfo->spwndTaskman);
|
|
if (pwnd == pwnd->head.rpdesk->pDeskInfo->spwndProgman)
|
|
Unlock(&pwnd->head.rpdesk->pDeskInfo->spwndProgman);
|
|
if (TestWF(pwnd,WFSHELLHOOKWND)) {
|
|
_DeregisterShellHookWindow(pwnd);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* First, if this handle has been marked for destruction, that means it
|
|
* is possible that the current thread is not its owner! (meaning we're
|
|
* being called from a handle unlock call). In this case, set the owner
|
|
* to be the current thread so inter-thread send messages don't occur.
|
|
*/
|
|
if (HMIsMarkDestroy(pwnd))
|
|
HMChangeOwnerThread(pwnd, pti);
|
|
|
|
/*
|
|
* Blow away the children.
|
|
*
|
|
* DestroyAllChildren() will still destroy windows created by other
|
|
* threads! This needs to be looked at more closely: the ultimate
|
|
* "right" thing to do is not to destroy these windows but just
|
|
* unlink them.
|
|
*/
|
|
xxxFW_DestroyAllChildren(pwnd);
|
|
xxxSendMessage(pwnd, WM_NCDESTROY, 0, 0L);
|
|
|
|
xxxRemoveFullScreen(pwnd);
|
|
|
|
/*
|
|
* If this is one of the built in controls which hasn't been cleaned
|
|
* up yet, do it now. If it lives in the kernel, call the function
|
|
* directly, otherwise call back to the client. Even if the control
|
|
* is sub- or super-classed, use the window procs associated with
|
|
* the function id.
|
|
*/
|
|
fnid = GETFNID(pwnd);
|
|
if ((fnid >= FNID_WNDPROCSTART) && !(pwnd->fnid & FNID_CLEANEDUP_BIT)) {
|
|
|
|
if (fnid <= FNID_WNDPROCEND) {
|
|
|
|
FNID(fnid)(pwnd, WM_FINALDESTROY, 0, 0, 0);
|
|
|
|
} else if (fnid <= FNID_CONTROLEND && !(pti->TIF_flags & TIF_INCLEANUP)) {
|
|
|
|
/*
|
|
* If it was a sub-classed control, it should have a worker
|
|
* proc we can call. If it was super-classed, we'll have to
|
|
* get the client-side proc from the global array.
|
|
*/
|
|
if (pwnd->pcls->lpfnWorker) {
|
|
|
|
CallClientWorkerProc(pwnd,
|
|
WM_FINALDESTROY,
|
|
0,
|
|
0,
|
|
pwnd->pcls->lpfnWorker);
|
|
} else {
|
|
|
|
CallClientProcW(pwnd,
|
|
WM_FINALDESTROY,
|
|
0,
|
|
0,
|
|
(DWORD)FNID_TO_CLIENT_PFNW(fnid));
|
|
}
|
|
}
|
|
|
|
pwnd->fnid |= FNID_CLEANEDUP_BIT;
|
|
}
|
|
|
|
/*
|
|
* We have to call back to the client side so the client DC can
|
|
* be deleted. A client DC is likely to exist if the window
|
|
* is an OWNDC window or if pwnd->cDC != 0.
|
|
* If this is a CLASSDC, other windows might be using the DC. So
|
|
* we won't clean it up unless this is the last window of its class
|
|
*/
|
|
|
|
if ((!(pti->TIF_flags & TIF_INCLEANUP))
|
|
&& (TestCF(pwnd, CFOWNDC)
|
|
|| ((pwnd->cDC != 0) && (pwnd->pcls->cWndReferenceCount == 1)))
|
|
&& !HMIsMarkDestroy(pwnd)) {
|
|
|
|
pwnd->cDC = 0;
|
|
for (pdce = gpDispInfo->pdceFirst; pdce; pdce = pdce->pdceNext) {
|
|
|
|
if ((pdce->pwndOrg == pwnd) || (pdce->pwndClip == pwnd)) {
|
|
|
|
/*
|
|
* Clean up any objects selected into this dc so the client
|
|
* doesn't try to do it when we callback.
|
|
*/
|
|
if (pdce->flags & DCX_INUSE) {
|
|
GreCleanDC(pdce->hdc);
|
|
}
|
|
|
|
hdcT = pdce->hdc;
|
|
}
|
|
}
|
|
}
|
|
|
|
pwnd->fnid |= FNID_DELETED_BIT;
|
|
|
|
/*
|
|
* Check to clear the most recently active window in owned list.
|
|
*/
|
|
if (pwnd->spwndOwner && (pwnd->spwndOwner->spwndLastActive == pwnd)) {
|
|
Lock(&(pwnd->spwndOwner->spwndLastActive), pwnd->spwndOwner);
|
|
}
|
|
|
|
/*
|
|
* The windowstation may be NULL if we are destroying a desktop
|
|
* or windowstation. If this is the case, this thread will not
|
|
* be using the clipboard.
|
|
*/
|
|
if (pwinsta != NULL) {
|
|
|
|
if (pwnd == pwinsta->spwndClipOpen) {
|
|
Unlock(&pwinsta->spwndClipOpen);
|
|
pwinsta->ptiClipLock = NULL;
|
|
}
|
|
|
|
if (pwnd == pwinsta->spwndClipViewer) {
|
|
Unlock(&pwinsta->spwndClipViewer);
|
|
}
|
|
}
|
|
|
|
#ifdef FE_IME
|
|
if (pwnd == pti->spwndDefaultIme)
|
|
Unlock(&pti->spwndDefaultIme);
|
|
#endif
|
|
|
|
if (pwnd == pti->pq->spwndLastMouseMessage) {
|
|
CancelMouseHover(pti->pq);
|
|
pti->pq->QF_flags &= ~QF_TRACKMOUSELEAVE;
|
|
Unlock(&pti->pq->spwndLastMouseMessage);
|
|
}
|
|
|
|
if (pwnd == pti->pq->spwndFocus)
|
|
Unlock(&pti->pq->spwndFocus);
|
|
|
|
if (pwnd == pti->pq->spwndActivePrev)
|
|
Unlock(&pti->pq->spwndActivePrev);
|
|
|
|
if (pwnd == gspwndActivate)
|
|
Unlock(&gspwndActivate);
|
|
|
|
if (pwnd->head.rpdesk != NULL) {
|
|
|
|
if (pwnd == pwnd->head.rpdesk->spwndForeground)
|
|
Unlock(&pwnd->head.rpdesk->spwndForeground);
|
|
|
|
if (pwnd == pwnd->head.rpdesk->spwndTray)
|
|
Unlock(&pwnd->head.rpdesk->spwndTray);
|
|
}
|
|
|
|
if (pwnd == pti->pq->spwndCapture)
|
|
xxxReleaseCapture();
|
|
|
|
/*
|
|
* This window won't be needing any more input.
|
|
*/
|
|
if (pwnd == gspwndMouseOwner)
|
|
Unlock(&gspwndMouseOwner);
|
|
|
|
/*
|
|
* It also won't have any mouse cursors over it.
|
|
*/
|
|
if (pwnd == gspwndCursor)
|
|
Unlock(&gspwndCursor);
|
|
|
|
/*
|
|
* If it was using either of the desktop system menus, unlock it
|
|
*/
|
|
if (pwnd->head.rpdesk != NULL) {
|
|
if (pwnd->head.rpdesk->spmenuSys != NULL &&
|
|
pwnd == pwnd->head.rpdesk->spmenuSys->spwndNotify)
|
|
UnlockNotifyWindow(pwnd->head.rpdesk->spmenuSys);
|
|
else if (pwnd->head.rpdesk->spmenuDialogSys != NULL &&
|
|
pwnd == pwnd->head.rpdesk->spmenuDialogSys->spwndNotify)
|
|
UnlockNotifyWindow(pwnd->head.rpdesk->spmenuDialogSys);
|
|
}
|
|
|
|
DestroyWindowsTimers(pwnd);
|
|
DestroyWindowsHotKeys(pwnd);
|
|
|
|
/*
|
|
* Make sure this window has no pending sent messages.
|
|
*/
|
|
ClearSendMessages(pwnd);
|
|
|
|
/*
|
|
* Blow away any update region lying around.
|
|
*/
|
|
if (NEEDSPAINT(pwnd)) {
|
|
|
|
DecPaintCount(pwnd);
|
|
|
|
if (pwnd->hrgnUpdate > MAXREGION)
|
|
GreDeleteObject(pwnd->hrgnUpdate);
|
|
|
|
pwnd->hrgnUpdate = NULL;
|
|
ClrWF(pwnd, WFINTERNALPAINT);
|
|
}
|
|
|
|
/*
|
|
* Decrememt queue's syncpaint count if necessary.
|
|
*/
|
|
if (NEEDSSYNCPAINT(pwnd)) {
|
|
ClrWF(pwnd, WFSENDNCPAINT);
|
|
ClrWF(pwnd, WFSENDERASEBKGND);
|
|
}
|
|
|
|
/*
|
|
* Clear both flags to ensure that the window is removed
|
|
* from the hung redraw list.
|
|
*/
|
|
ClearHungFlag(pwnd, WFREDRAWIFHUNG);
|
|
ClearHungFlag(pwnd, WFREDRAWFRAMEIFHUNG);
|
|
|
|
/*
|
|
* If there is a WM_QUIT message in this app's message queue, call
|
|
* PostQuitMessage() (this happens if the app posts itself a quit message.
|
|
* WinEdit2.0 posts a quit to a window while receiving the WM_DESTROY
|
|
* for that window - it works because we need to do a PostQuitMessage()
|
|
* automatically for this thread.
|
|
*/
|
|
if (pti->mlPost.pqmsgRead != NULL) {
|
|
|
|
if ((pqmsg = FindQMsg(pti,
|
|
&(pti->mlPost),
|
|
pwnd,
|
|
WM_QUIT,
|
|
WM_QUIT)) != NULL) {
|
|
|
|
_PostQuitMessage((int)pqmsg->msg.wParam);
|
|
}
|
|
}
|
|
|
|
if (!TestwndChild(pwnd) && pwnd->spmenu != NULL) {
|
|
pmenu = (PMENU)pwnd->spmenu;
|
|
if (Lock(&pwnd->spmenu, NULL))
|
|
_DestroyMenu(pmenu);
|
|
}
|
|
|
|
if (pwnd->spmenuSys != NULL) {
|
|
pmenu = (PMENU)pwnd->spmenuSys;
|
|
if (pmenu != pwnd->head.rpdesk->spmenuDialogSys) {
|
|
if (Lock(&pwnd->spmenuSys, NULL)) {
|
|
_DestroyMenu(pmenu);
|
|
}
|
|
} else {
|
|
Unlock(&pwnd->spmenuSys);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Tell Gdi that the window is going away.
|
|
*/
|
|
if (pwnd->pwo != NULL) {
|
|
GreLockDisplay(gpDispInfo->pDevLock);
|
|
GreDeleteWnd(pwnd->pwo);
|
|
pwnd->pwo = NULL;
|
|
gcountPWO--;
|
|
GreUnlockDisplay(gpDispInfo->pDevLock);
|
|
}
|
|
|
|
/*
|
|
* Scan the DC cache to find any DC's for this window. If any are there,
|
|
* then invalidate them. We don't need to worry about calling SpbCheckDC
|
|
* because the window has been hidden by this time.
|
|
*/
|
|
for (ppdce = &gpDispInfo->pdceFirst; *ppdce != NULL; ) {
|
|
|
|
pdce = *ppdce;
|
|
if (pdce->flags & DCX_INVALID) {
|
|
ppdce = &pdce->pdceNext;
|
|
continue;
|
|
}
|
|
|
|
if ((pdce->pwndOrg == pwnd) || (pdce->pwndClip == pwnd)) {
|
|
|
|
if (!(pdce->flags & DCX_CACHE)) {
|
|
|
|
if (TestCF(pwnd, CFCLASSDC)) {
|
|
|
|
GreLockDisplay(gpDispInfo->pDevLock);
|
|
|
|
if (pdce->flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN))
|
|
DeleteHrgnClip(pdce);
|
|
|
|
pdce->flags = DCX_INVALID;
|
|
pdce->pwndOrg = NULL;
|
|
pdce->pwndClip = NULL;
|
|
pdce->hrgnClip = NULL;
|
|
|
|
/*
|
|
* Remove the vis rgn since it is still owned - if we did
|
|
* not, gdi would not be able to clean up properly if the
|
|
* app that owns this vis rgn exist while the vis rgn is
|
|
* still selected.
|
|
*/
|
|
GreSelectVisRgn(pdce->hdc, NULL, NULL, SVR_DELETEOLD);
|
|
GreUnlockDisplay(gpDispInfo->pDevLock);
|
|
|
|
} else if (TestCF(pwnd, CFOWNDC)) {
|
|
DestroyCacheDC(ppdce, pdce->hdc);
|
|
} else {
|
|
UserAssert(FALSE);
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
* If the DC is checked out, release it before
|
|
* we invalidate. Note, that if this process is exiting
|
|
* and it has a dc checked out, gdi is going to destroy that
|
|
* dc. We need to similarly remove that dc from the dc cache.
|
|
* This is not done here, but in the exiting code.
|
|
*
|
|
* The return for ReleaseDC() could fail, which would
|
|
* indicate a delayed-free (DCE_NUKE).
|
|
*/
|
|
uDCERelease = DCE_RELEASED;
|
|
|
|
if (pdce->flags & DCX_INUSE) {
|
|
uDCERelease = ReleaseCacheDC(pdce->hdc, FALSE);
|
|
} else if (!GreSetDCOwner(pdce->hdc, OBJECT_OWNER_NONE)) {
|
|
uDCERelease = DCE_NORELEASE;
|
|
}
|
|
|
|
if (uDCERelease != DCE_FREED) {
|
|
|
|
if (uDCERelease == DCE_NORELEASE) {
|
|
|
|
/*
|
|
* We either could not release this dc or could not set
|
|
* its owner. In either case it means some other thread
|
|
* is actively using it. Since it is not too useful if
|
|
* the window it is calculated for is gone, mark it as
|
|
* INUSE (so we don't give it out again) and as
|
|
* DESTROYTHIS (so we just get rid of it since it is
|
|
* easier to do this than to release it back into the
|
|
* cache). The W32PF_OWNERDCCLEANUP bit means "look for
|
|
* DESTROYTHIS flags and destroy that dc", and the bit
|
|
* gets looked at in various strategic execution paths.
|
|
*/
|
|
pdce->flags = DCX_DESTROYTHIS | DCX_INUSE | DCX_CACHE;
|
|
pti->ppi->W32PF_Flags |= W32PF_OWNDCCLEANUP;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* We either released the DC or changed its owner
|
|
* successfully. Mark the entry as invalid so it can
|
|
* be given out again.
|
|
*/
|
|
pdce->flags = DCX_INVALID | DCX_CACHE;
|
|
pdce->pwndOrg = NULL;
|
|
pdce->pwndClip = NULL;
|
|
pdce->hrgnClip = NULL;
|
|
}
|
|
|
|
/*
|
|
* Remove the visrgn since it is still owned - if we did
|
|
* not, gdi would not be able to clean up properly if the
|
|
* app that owns this visrgn exist while the visrgn is
|
|
* still selected.
|
|
*/
|
|
GreLockDisplay(gpDispInfo->pDevLock);
|
|
GreSelectVisRgn(pdce->hdc, NULL, NULL, SVR_DELETEOLD);
|
|
GreUnlockDisplay(gpDispInfo->pDevLock);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Step to the next DC. If the DC was deleted, there
|
|
* is no need to calculate address of the next entry.
|
|
*/
|
|
if (pdce == *ppdce)
|
|
ppdce = &pdce->pdceNext;
|
|
}
|
|
|
|
/*
|
|
* Clean up the spb that may still exist - like child window spb's.
|
|
*/
|
|
if (pwnd == gspwndLockUpdate) {
|
|
FreeSpb(FindSpb(pwnd));
|
|
Unlock(&gspwndLockUpdate);
|
|
gptiLockUpdate = NULL;
|
|
}
|
|
|
|
if (TestWF(pwnd, WFHASSPB)) {
|
|
FreeSpb(FindSpb(pwnd));
|
|
}
|
|
|
|
/*
|
|
* Blow away the window clipping region
|
|
*/
|
|
if (pwnd->hrgnClip != NULL) {
|
|
GreDeleteObject(pwnd->hrgnClip);
|
|
pwnd->hrgnClip = NULL;
|
|
}
|
|
|
|
/*
|
|
* Clean up any memory allocated for scroll bars...
|
|
*/
|
|
if (pwnd->pSBInfo) {
|
|
DesktopFree(pwnd->head.rpdesk->hheapDesktop, (HANDLE)(pwnd->pSBInfo));
|
|
pwnd->pSBInfo = NULL;
|
|
}
|
|
|
|
/*
|
|
* Free any callback handles associated with this window.
|
|
* This is done outside of DeleteProperties because of the special
|
|
* nature of callback handles as opposed to normal memory handles
|
|
* allocated for a thread.
|
|
*/
|
|
|
|
/*
|
|
* Blow away the title
|
|
*/
|
|
if (pwnd->strName.Buffer != NULL) {
|
|
DesktopFree(pwnd->head.rpdesk->hheapDesktop, pwnd->strName.Buffer);
|
|
pwnd->strName.Buffer = NULL;
|
|
pwnd->strName.Length = 0;
|
|
}
|
|
|
|
/*
|
|
* Blow away any properties connected to the window.
|
|
*/
|
|
if (pwnd->ppropList != NULL) {
|
|
TL tlpDdeConv;
|
|
PDDECONV pDdeConv;
|
|
PDDEIMP pddei;
|
|
|
|
/*
|
|
* Get rid of any icon properties.
|
|
*/
|
|
DestroyWindowSmIcon(pwnd);
|
|
InternalRemoveProp(pwnd, MAKEINTATOM(gpsi->atomIconProp), PROPF_INTERNAL);
|
|
|
|
pDdeConv = (PDDECONV)_GetProp(pwnd, PROP_DDETRACK, PROPF_INTERNAL);
|
|
if (pDdeConv != NULL) {
|
|
ThreadLockAlwaysWithPti(pti, pDdeConv, &tlpDdeConv);
|
|
xxxDDETrackWindowDying(pwnd, pDdeConv);
|
|
ThreadUnlock(&tlpDdeConv);
|
|
}
|
|
pddei = (PDDEIMP)InternalRemoveProp(pwnd, PROP_DDEIMP, PROPF_INTERNAL);
|
|
if (pddei != NULL) {
|
|
pddei->cRefInit = 0;
|
|
if (pddei->cRefConv == 0) {
|
|
/*
|
|
* If this is not 0 it is referenced by one or more DdeConv
|
|
* structures so DON'T free it yet!
|
|
*/
|
|
UserFreePool(pddei);
|
|
}
|
|
}
|
|
DeleteProperties(pwnd);
|
|
}
|
|
|
|
/*
|
|
* Unlock everything that the window references.
|
|
* After we have sent the WM_DESTROY and WM_NCDESTROY message we
|
|
* can unlock & NULL the owner field so no other windows get z-ordered
|
|
* relative to this window. Rhumba faults if we NULL it before the
|
|
* destroy. (It calls GetParent after every message).
|
|
*
|
|
* We special-case the spwndParent window. In this case, if the
|
|
* window being destroyed is a desktop window, unlock the parent.
|
|
* Otherwise, we lock in the desktop-window as the parent so that
|
|
* if we aren't freed in this function, we will ensure that we
|
|
* won't fault when doing things like clipping-calculations. We'll
|
|
* unlock this once we know we're truly going to free this window.
|
|
*/
|
|
if (pwnd->head.rpdesk != NULL &&
|
|
pwnd != pwnd->head.rpdesk->pDeskInfo->spwnd)
|
|
Lock(&pwnd->spwndParent, pwnd->head.rpdesk->pDeskInfo->spwnd);
|
|
else
|
|
Unlock(&pwnd->spwndParent);
|
|
|
|
Unlock(&pwnd->spwndChild);
|
|
Unlock(&pwnd->spwndOwner);
|
|
Unlock(&pwnd->spwndLastActive);
|
|
|
|
/*
|
|
* Decrement the Window Reference Count in the Class structure.
|
|
*/
|
|
DereferenceClass(pwnd);
|
|
|
|
/*
|
|
* Mark the object for destruction before this final unlock. This way
|
|
* the WM_FINALDESTROY will get sent if this is the last thread lock.
|
|
* We're currently destroying this window, so don't allow unlock recursion
|
|
* at this point (this is what HANDLEF_INDESTROY will do for us).
|
|
*/
|
|
HMMarkObjectDestroy(pwnd);
|
|
HMPheFromObject(pwnd)->bFlags |= HANDLEF_INDESTROY;
|
|
|
|
/*
|
|
* Unlock the window... This shouldn't return FALSE because HANDLEF_DESTROY
|
|
* is set, but just in case... if it isn't around anymore, return because
|
|
* pwnd is invalid.
|
|
*/
|
|
if (!ThreadUnlock(ptlpwndFree))
|
|
return;
|
|
|
|
/*
|
|
* Try to free the object. The object won't free if it is locked - but
|
|
* it will be marked for destruction. If the window is locked, change
|
|
* it's wndproc to xxxDefWindowProc().
|
|
*
|
|
* HMMarkObjectDestroy() will clear the HANDLEF_INDESTROY flag if the
|
|
* object isn't about to go away (so it can be destroyed again!)
|
|
*/
|
|
pwnd->pcls = NULL;
|
|
if (HMMarkObjectDestroy(pwnd)) {
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* If we find the window is visible at the time we free it, then
|
|
* somehow the app was made visible on a callback (we hide it
|
|
* during xxxDestroyWindow(). This screws up our vis-window
|
|
* count for the thread, so we need to assert it.
|
|
*/
|
|
if (TestWF(pwnd, WFINDESTROY) && TestWF(pwnd, WFVISIBLE))
|
|
RIPMSG1(RIP_ERROR, "xxxFreeWindow: Window should not be visible (pwnd == %x)", pwnd);
|
|
#endif
|
|
|
|
pti->cWindows--;
|
|
|
|
/*
|
|
* Since we're freeing the memory for this window, we need
|
|
* to unlock the parent (which is the desktop for zombie windows).
|
|
*/
|
|
Unlock(&pwnd->spwndParent);
|
|
|
|
ThreadLockDesktop(pti, pwnd->head.rpdesk, &tlpdesk);
|
|
HMFreeObject(pwnd);
|
|
ThreadUnlockDesktop(pti, &tlpdesk);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Turn this into an object that the app won't see again - turn
|
|
* it into an icon title window - the window is still totally
|
|
* valid and useable by any structures that has this window locked.
|
|
*/
|
|
#ifdef LATER
|
|
LockDesktop(&pwnd->head.rpdeskParent, pti->rpdesk);
|
|
#endif
|
|
pwnd->lpfnWndProc = xxxDefWindowProc;
|
|
if (pwnd->head.rpdesk)
|
|
ppi = pwnd->head.rpdesk->rpwinstaParent->ptiDesktop->ppi;
|
|
else
|
|
ppi == PpiCurrent();
|
|
ppcls = GetClassPtr(gpsi->atomSysClass[ICLS_ICONTITLE], ppi, hModuleWin);
|
|
|
|
UserAssert(ppcls);
|
|
pwnd->pcls = *ppcls;
|
|
|
|
if (!ReferenceClass(*ppcls, pwnd)) {
|
|
RIPMSG1(RIP_WARNING, "xxxFreeWindow: Failed to reference class (pwnd == %x)", pwnd);
|
|
}
|
|
|
|
SetWF(pwnd, WFSERVERSIDEPROC);
|
|
|
|
/*
|
|
* Clear the palette bit so that WM_PALETTECHANGED will not be sent
|
|
* again when the window is finally destroyed.
|
|
*/
|
|
ClrWF(pwnd, WFHASPALETTE);
|
|
|
|
/*
|
|
* Clear its child bits so no code assumes that if the child bit
|
|
* is set, it has a parent. Change spmenu to NULL - it is only
|
|
* non-zero if this was child.
|
|
*/
|
|
ClrWF(pwnd, WFTYPEMASK);
|
|
SetWF(pwnd, WFTILED);
|
|
pwnd->spmenu = NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UnlinkWindow
|
|
*
|
|
* History:
|
|
* 19-Oct-1990 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
VOID UnlinkWindow(
|
|
PWND pwndUnlink,
|
|
PWND *ppwndFirst)
|
|
{
|
|
PWND pwnd;
|
|
|
|
pwnd = *ppwndFirst;
|
|
|
|
if (pwnd == pwndUnlink)
|
|
goto Unlock;
|
|
|
|
while (pwnd != NULL) {
|
|
|
|
if (pwnd->spwndNext == pwndUnlink) {
|
|
|
|
ppwndFirst = &pwnd->spwndNext;
|
|
Unlock:
|
|
Lock(ppwndFirst, pwndUnlink->spwndNext);
|
|
Unlock(&pwndUnlink->spwndNext);
|
|
|
|
return;
|
|
}
|
|
|
|
pwnd = pwnd->spwndNext;
|
|
}
|
|
|
|
/*
|
|
* We should never get here unless the window isn't in the list!
|
|
*/
|
|
RIPMSG1(RIP_WARNING,
|
|
"Unlinking previously unlinked window 0x%08lx\n",
|
|
pwndUnlink);
|
|
|
|
return;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DestroyCacheDCEntries
|
|
*
|
|
* Destroys all cache dc entries currently in use by this thread.
|
|
*
|
|
* 24-Feb-1992 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
VOID DestroyCacheDCEntries(
|
|
PTHREADINFO pti)
|
|
{
|
|
PDCE *ppdce;
|
|
PDCE pdce;
|
|
|
|
/*
|
|
* Before any window destruction occurs, we need to destroy any dcs
|
|
* in use in the dc cache. When a dc is checked out, it is marked owned,
|
|
* which makes gdi's process cleanup code delete it when a process
|
|
* goes away. We need to similarly destroy the cache entry of any dcs
|
|
* in use by the exiting process.
|
|
*/
|
|
for (ppdce = &gpDispInfo->pdceFirst; *ppdce != NULL; ) {
|
|
|
|
/*
|
|
* If the dc owned by this thread, remove it from the cache. Because
|
|
* DestroyCacheEntry destroys gdi objects, it is important that
|
|
* USER be called first in process destruction ordering.
|
|
*
|
|
* Only destroy this dc if it is a cache dc, because if it is either
|
|
* an owndc or a classdc, it will be destroyed for us when we destroy
|
|
* the window (for owndcs) or destroy the class (for classdcs).
|
|
*/
|
|
pdce = *ppdce;
|
|
if (pti == pdce->ptiOwner) {
|
|
|
|
if (pdce->flags & DCX_CACHE)
|
|
DestroyCacheDC(ppdce, pdce->hdc);
|
|
}
|
|
|
|
/*
|
|
* Step to the next DC. If the DC was deleted, there
|
|
* is no need to calculate address of the next entry.
|
|
*/
|
|
if (pdce == *ppdce)
|
|
ppdce = &pdce->pdceNext;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* PatchThreadWindows
|
|
*
|
|
* This patches a thread's windows so that their window procs point to
|
|
* server only windowprocs. This is used for cleanup so that app aren't
|
|
* called back while the system is cleaning up after them.
|
|
*
|
|
* 24-Feb-1992 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
VOID PatchThreadWindows(
|
|
PTHREADINFO pti)
|
|
{
|
|
PHE pheT;
|
|
PHE pheMax;
|
|
PWND pwnd;
|
|
|
|
/*
|
|
* First do any preparation work: windows need to be "patched" so that
|
|
* their window procs point to server only windowprocs, for example.
|
|
*/
|
|
pheMax = &gSharedInfo.aheList[giheLast];
|
|
for (pheT = gSharedInfo.aheList; pheT <= pheMax; pheT++) {
|
|
|
|
/*
|
|
* Make sure this object is a window, it hasn't been marked for
|
|
* destruction, and that it is owned by this thread.
|
|
*/
|
|
if (pheT->bType != TYPE_WINDOW)
|
|
continue;
|
|
|
|
if (pheT->bFlags & HANDLEF_DESTROY)
|
|
continue;
|
|
|
|
if ((PTHREADINFO)pheT->pOwner != pti)
|
|
continue;
|
|
|
|
/*
|
|
* don't patch the shared menu window
|
|
*/
|
|
if (pti->rpdesk && (PHEAD)pti->rpdesk->spwndMenu == pheT->phead) {
|
|
|
|
((PTHROBJHEAD)pheT->phead)->pti = pti->rpdesk->pDeskInfo->spwnd->head.pti;
|
|
pheT->pOwner = pti->rpdesk->pDeskInfo->spwnd->head.pti;
|
|
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Don't patch the window based on the class it was created from -
|
|
* because apps can sometimes sub-class a class - make a random class,
|
|
* then call ButtonWndProc with windows of that class by using
|
|
* the CallWindowProc() api. So patch the wndproc based on what
|
|
* wndproc this window has been calling.
|
|
*/
|
|
pwnd = (PWND)pheT->phead;
|
|
|
|
if ((pwnd->fnid >= (WORD)FNID_WNDPROCSTART) &&
|
|
(pwnd->fnid <= (WORD)FNID_WNDPROCEND)) {
|
|
|
|
pwnd->lpfnWndProc = STOCID(pwnd->fnid);
|
|
|
|
if (pwnd->lpfnWndProc == NULL)
|
|
pwnd->lpfnWndProc = xxxDefWindowProc;
|
|
|
|
} else {
|
|
|
|
pwnd->lpfnWndProc = xxxDefWindowProc;
|
|
}
|
|
|
|
/*
|
|
* This is a server side window now...
|
|
*/
|
|
SetWF(pwnd, WFSERVERSIDEPROC);
|
|
ClrWF(pwnd, WFANSIPROC);
|
|
}
|
|
}
|