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.
3139 lines
103 KiB
3139 lines
103 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: createw.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, 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
|
|
|
|
BOOL WantImeWindow(PWND pwndParent, PWND pwnd);
|
|
#if DBG
|
|
VOID VerifyWindowLink(PWND pwnd, PWND pwndParent, BOOL fLink);
|
|
#endif
|
|
|
|
/***************************************************************************\
|
|
* xxxCreateWindowEx (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 xxxCreateWindowEx(
|
|
DWORD dwExStyle,
|
|
PLARGE_STRING cczpstrNVClass,
|
|
PLARGE_STRING cczpstrClass,
|
|
PLARGE_STRING cczpstrName,
|
|
DWORD style,
|
|
int x,
|
|
int y,
|
|
int cx,
|
|
int cy,
|
|
PWND pwndParent,
|
|
PMENU pMenu,
|
|
HANDLE hInstance,
|
|
LPVOID lpCreateParams,
|
|
DWORD dwExpWinVerAndFlags,
|
|
PACTIVATION_CONTEXT pActCtx)
|
|
{
|
|
/*
|
|
* The buffers for Class and Name may be client memory, and access
|
|
* to those buffers must be protected.
|
|
*/
|
|
UINT mask = 0;
|
|
BOOL fChild;
|
|
BOOL fDefPos = FALSE;
|
|
BOOL fStartup = FALSE;
|
|
PCLS pcls;
|
|
PPCLS ppcls;
|
|
RECT rc;
|
|
int dx, dy;
|
|
SIZERECT src;
|
|
int sw = SW_SHOW;
|
|
PWND pwnd;
|
|
PWND pwndZOrder, pwndHardError;
|
|
CREATESTRUCTEX csex;
|
|
PDESKTOP pdesk;
|
|
ATOM atomT;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
TL tlpwnd;
|
|
TL tlpwndParent;
|
|
TL tlpwndParentT;
|
|
BOOL fLockParent = FALSE;
|
|
WORD wWFAnsiCreator = 0;
|
|
DWORD dw;
|
|
DWORD dwMinMax;
|
|
PMONITOR pMonitor;
|
|
BOOL fTiled;
|
|
|
|
CheckLock(pwndParent);
|
|
UserAssert(IsWinEventNotifyDeferredOK());
|
|
|
|
#ifdef LAZY_CLASS_INIT
|
|
if ((ptiCurrent->ppi->W32PF_Flags & W32PF_CLASSESREGISTERED) == 0) {
|
|
if (!LW_RegisterWindows()) {
|
|
RIPERR0(ERROR_INVALID_PARAMETER,
|
|
RIP_WARNING,
|
|
"LW_RegisterWindows failed.");
|
|
return NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
|
|
/*
|
|
* After grocking any bits, we should no longer be using any private bits.
|
|
*/
|
|
UserAssert((dwExStyle & WS_EXP_PRIVATE) == 0);
|
|
|
|
/*
|
|
* If this thread has already been in xxxDestroyThreadInfo, then this window
|
|
* is probably going to end up with a bogus pti.
|
|
*/
|
|
UserAssert(!(ptiCurrent->TIF_flags & TIF_INCLEANUP));
|
|
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_WARNING, "");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Set a flag indicating whether it is a child window.
|
|
*/
|
|
fChild = ((HIWORD(style) & MaskWF(WFTYPEMASK)) == MaskWF(WFCHILD));
|
|
|
|
/*
|
|
* The WS_EX_LAYOUT_RTL flag is set if,
|
|
*
|
|
* (1) WS_EX_LAYOUT_RTL set in dwExStyle parameter of the CreateWindow call.
|
|
*
|
|
* (2) If the window is created from DialogBox class, then it can't inherit
|
|
* from its parent and it has to specify WS_EX_LAYOUTRTL explicitly to
|
|
* enable mirroring on it.
|
|
*
|
|
* (3) If the window is an owned window then the window is left to right
|
|
* layout and the algorithm terminates. (An owned window is one created
|
|
* with an HWND passed in the hWndParent paremeter to CreateWindow(Ex),
|
|
* but without the WS_CHILD flag present in it's styles.
|
|
*
|
|
* (4) If the window is a child window, and it's parent is right to left
|
|
* layout, and it's parent does not have the WS_EX_NOINHERIT_LAYOUT flag
|
|
* set in it's extended styles, then the window is right to left layout
|
|
* and the algorithm terminates.
|
|
*
|
|
* (5) If the hWndParent parameter to Createwindow(Ex) was NULL, and the
|
|
* process calling CreateWindow(Ex) has called
|
|
* SetProcessDefaultLayout(LAYOUT_RTL), then the window is right to left
|
|
* layout and the algorithm terminates.
|
|
*
|
|
* (6) In all other cases, the layout is left to right.
|
|
*/
|
|
if (!(dwExStyle & WS_EX_LAYOUTRTL)) {
|
|
if (pwndParent != NULL) {
|
|
if (fChild && TestWF(pwndParent, WEFLAYOUTRTL) && !TestWF(pwndParent, WEFNOINHERITLAYOUT)) {
|
|
dwExStyle |= WS_EX_LAYOUTRTL;
|
|
}
|
|
} else if (!(!IS_PTR(cczpstrNVClass) && (PTR_TO_ID(cczpstrNVClass) == PTR_TO_ID(DIALOGCLASS)))) {
|
|
if ((PpiCurrent()->dwLayout & LAYOUT_RTL)) {
|
|
dwExStyle |= WS_EX_LAYOUTRTL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
if (fChild) {
|
|
/*
|
|
* Don't allow child windows without a parent handle.
|
|
*/
|
|
if (pwndParent == NULL) {
|
|
RIPERR0(ERROR_TLW_WITH_WSCHILD, RIP_WARNING, "");
|
|
return NULL;
|
|
}
|
|
|
|
if (!ValidateParentDepth(NULL, pwndParent)) {
|
|
RIPERR0(ERROR_INVALID_PARAMETER,
|
|
RIP_WARNING,
|
|
"Exceeded nested children limit");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make sure we can get the window class.
|
|
*/
|
|
if (IS_PTR(cczpstrClass)) {
|
|
/*
|
|
* UserFindAtom protects access of the string.
|
|
*/
|
|
atomT = UserFindAtom(cczpstrClass->Buffer);
|
|
} else {
|
|
atomT = PTR_TO_ID(cczpstrClass);
|
|
}
|
|
|
|
if (atomT == 0) {
|
|
CantFindClassMessageAndFail:
|
|
#if DBG
|
|
if (IS_PTR(cczpstrNVClass)) {
|
|
try {
|
|
RIPMSG1(RIP_VERBOSE,
|
|
"Couldn't find class string %ws",
|
|
cczpstrNVClass->Buffer);
|
|
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
}
|
|
} else {
|
|
RIPMSG1(RIP_VERBOSE,
|
|
"Couldn't find class atom 0x%x",
|
|
cczpstrNVClass);
|
|
}
|
|
#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, Is400Compat(dwExpWinVerAndFlags))) {
|
|
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 xxxCreateWindowEx");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Stuff in the pq, class pointer, and window style.
|
|
*/
|
|
pwnd->pcls = pcls;
|
|
pwnd->style = style & ~WS_VISIBLE;
|
|
pwnd->ExStyle = dwExStyle & ~(WS_EX_LAYERED | WS_EX_COMPOSITED);
|
|
pwnd->cbwndExtra = pcls->cbwndExtra;
|
|
|
|
/*
|
|
* Increment the window reference count in the class structure.
|
|
* Because xxxFreeWindow() decrements the count, incrementing has
|
|
* to be done now. In the case of error, xxxFreeWindow() will decrement it.
|
|
*/
|
|
if (!ReferenceClass(pcls, pwnd)) {
|
|
HMFreeObject(pwnd);
|
|
goto CantFindClassMessageAndFail;
|
|
}
|
|
|
|
/*
|
|
* Set the window active App context to be activated whenever we call
|
|
* the user WndProc.
|
|
*/
|
|
pwnd->pActCtx = pActCtx;
|
|
|
|
/*
|
|
* Button control doesn't need input context. Other windows
|
|
* will associate with the default input context.
|
|
* N.b. this comparison needs to be performed on the NV class
|
|
* name.
|
|
*/
|
|
if (pcls->atomNVClassName == gpsi->atomSysClass[ICLS_BUTTON]) {
|
|
pwnd->hImc = NULL_HIMC;
|
|
} else {
|
|
pwnd->hImc = (HIMC)PtoH(ptiCurrent->spDefaultImc);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/*
|
|
* 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 (!IS_PTR(cczpstrNVClass)) {
|
|
csex.cs.lpszClass = (LPWSTR)cczpstrNVClass;
|
|
} else {
|
|
if (wWFAnsiCreator) {
|
|
csex.cs.lpszClass = (LPWSTR)pcls->lpszAnsiClassName;
|
|
if (IS_PTR(csex.cs.lpszClass)) {
|
|
RtlInitLargeAnsiString(
|
|
(PLARGE_ANSI_STRING)&csex.strClass,
|
|
(LPSTR)csex.cs.lpszClass,
|
|
(UINT)-1);
|
|
}
|
|
} else {
|
|
csex.cs.lpszClass = cczpstrNVClass->Buffer;
|
|
csex.strClass = *cczpstrNVClass;
|
|
}
|
|
}
|
|
|
|
if (cczpstrName != NULL) {
|
|
csex.cs.lpszName = cczpstrName->Buffer;
|
|
csex.strName = *cczpstrName;
|
|
}
|
|
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.
|
|
* Child windows get their UIState bits from their parent. Top level ones
|
|
* remain with the default cleared bits.
|
|
*
|
|
* The below test is equivalent to TestwndChild().
|
|
*/
|
|
if (fChild) {
|
|
csex.cs.hMenu = (HMENU)pMenu;
|
|
|
|
pwnd->ExStyle |= pwndParent->ExStyle & WS_EXP_UIVALID;
|
|
#if WS_EXP_UIACCELHIDDEN != 0x40000000
|
|
#error Fix UISTATE bits copying if you moved the UISTATE bits from ExStyle
|
|
#endif
|
|
|
|
} else {
|
|
csex.cs.hMenu = PtoH(pMenu);
|
|
}
|
|
|
|
csex.cs.lpCreateParams = lpCreateParams;
|
|
|
|
/*
|
|
* ThreadLock: we are going to be doing multiple callbacks here.
|
|
*/
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
|
|
|
|
/*
|
|
* set the parent to be the desktop window (if exists)
|
|
* before any callback. This way we'll always have a
|
|
* pointer on spwndParent
|
|
*/
|
|
if (pwnd->head.rpdesk) {
|
|
Lock(&(pwnd->spwndParent), PWNDMESSAGE(pwnd));
|
|
}
|
|
|
|
/*
|
|
* 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->CSF_flags & CSF_SERVERSIDEPROC) {
|
|
SetWF(pwnd, WFSERVERSIDEPROC);
|
|
UserAssert(!(pcls->CSF_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->CSF_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]) ||
|
|
(atomT == gpsi->atomSysClass[ICLS_IME]) ||
|
|
(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->hMod16 = ((ptiCurrent->TIF_flags & TIF_16BIT) && !TestWF(pwnd, WFSERVERSIDEPROC))? xxxClientWOWGetProcModule(pwnd->lpfnWndProc):0;
|
|
if (Is310Compat(dwExpWinVerAndFlags)) {
|
|
SetWF(pwnd, WFWIN31COMPAT);
|
|
if (Is400Compat(dwExpWinVerAndFlags)) {
|
|
SetWF(pwnd, WFWIN40COMPAT);
|
|
if (Is500Compat(dwExpWinVerAndFlags)) {
|
|
SetWF(pwnd, WFWIN50COMPAT);
|
|
}
|
|
}
|
|
} else if (dw & GACF_ALWAYSSENDNCPAINT) {
|
|
SetWF(pwnd, WFALWAYSSENDNCPAINT);
|
|
}
|
|
|
|
/*
|
|
* If we've got a registered DefWindowProc handler, make sure it's DLL
|
|
* is loaded for this process.
|
|
*/
|
|
if (IsInsideUserApiHook()) {
|
|
xxxLoadUserApiHook();
|
|
}
|
|
|
|
/*
|
|
* 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, (WPARAM)HWq(pwnd),
|
|
(LPARAM)&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;
|
|
pwndZOrder = PWInsertAfter(cbt.hwndInsertAfter);
|
|
}
|
|
} else {
|
|
pwndZOrder = (PWND)HWND_TOP;
|
|
}
|
|
|
|
if (!(fTiled = 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.
|
|
*/
|
|
src.x = x;
|
|
src.y = y;
|
|
src.cx = cx;
|
|
src.cy = cy;
|
|
|
|
/*
|
|
* Position Child Windows
|
|
*/
|
|
if (fChild = (BOOL)TestwndChild(pwnd)) {
|
|
|
|
/*
|
|
* Child windows are offset from the parent's origin.
|
|
*/
|
|
UserAssert(pwndParent);
|
|
if (pwndParent != PWNDDESKTOP(pwnd)) {
|
|
src.x += pwndParent->rcClient.left;
|
|
src.y += 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 (fTiled) {
|
|
|
|
/*
|
|
* 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)) {
|
|
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 (src.y != CW_USEDEFAULT && src.y != CW2_USEDEFAULT) {
|
|
sw = src.y;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Allow the shell to tell us what monitor to run this app on
|
|
*/
|
|
pMonitor = NULL;
|
|
if ( x == CW_USEDEFAULT ||
|
|
x == CW2_USEDEFAULT ||
|
|
cx == CW_USEDEFAULT ||
|
|
cx == CW2_USEDEFAULT) {
|
|
|
|
if (ptiCurrent->ppi->hMonitor) {
|
|
pMonitor = ValidateHmonitor(ptiCurrent->ppi->hMonitor);
|
|
} else if (pwndParent) {
|
|
pMonitor = _MonitorFromWindow(pwndParent, MONITOR_DEFAULTTONEAREST);
|
|
}
|
|
}
|
|
|
|
if (!pMonitor) {
|
|
pMonitor = GetPrimaryMonitor();
|
|
}
|
|
|
|
SetTiledRect(pwnd, &rc, pMonitor);
|
|
|
|
/*
|
|
* 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 = src.x = ptiCurrent->ppi->usi.dwX;
|
|
y = src.y = ptiCurrent->ppi->usi.dwY;
|
|
} else {
|
|
x = src.x = rc.left;
|
|
y = src.y = rc.top;
|
|
}
|
|
fDefPos = TRUE;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Use the apps specified positioning. Undo the "stacking"
|
|
* effect caused by SetTiledRect().
|
|
*/
|
|
if (pMonitor->cWndStack) {
|
|
pMonitor->cWndStack--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Did the app ask for default sizing?
|
|
*/
|
|
if (src.cx == CW_USEDEFAULT || src.cx == CW2_USEDEFAULT) {
|
|
|
|
/*
|
|
* Use default sizing.
|
|
*/
|
|
if (ptiCurrent->ppi->usi.dwFlags & STARTF_USESIZE) {
|
|
fStartup = TRUE;
|
|
src.cx = ptiCurrent->ppi->usi.dwXSize;
|
|
src.cy = ptiCurrent->ppi->usi.dwYSize;
|
|
} else {
|
|
src.cx = rc.right - x;
|
|
src.cy = 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 by moving the
|
|
* window.
|
|
*/
|
|
dx = (src.x + src.cx) - pMonitor->rcMonitor.right;
|
|
dy = (src.y + src.cy) - pMonitor->rcMonitor.bottom;
|
|
if (dx > 0) {
|
|
x -= dx;
|
|
src.x = x;
|
|
if (src.x < pMonitor->rcMonitor.left) {
|
|
src.x = x = pMonitor->rcMonitor.left;
|
|
}
|
|
}
|
|
|
|
if (dy > 0) {
|
|
y -= dy;
|
|
src.y = y;
|
|
if (src.y < pMonitor->rcMonitor.top) {
|
|
src.y = y = pMonitor->rcMonitor.top;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
if (TestwndPopup(pwnd)) {
|
|
/*
|
|
* 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 but that's ok they will just get a window without a menu
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
LockWndMenu(pwnd, &pwnd->spmenu, pMenu);
|
|
}
|
|
|
|
/*
|
|
* 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->spwndMessage) &&
|
|
(pwndParent != pwndParent->head.rpdesk->pDeskInfo->spwnd)) {
|
|
|
|
PWND pwndOwner = GetTopLevelWindow(pwndParent);
|
|
|
|
if (!ValidateOwnerDepth(pwnd, pwndOwner)) {
|
|
RIPERR1(ERROR_INVALID_PARAMETER,
|
|
RIP_WARNING,
|
|
"Exceeded nested owner limit for pwnd %#p",
|
|
pwnd);
|
|
goto MemError;
|
|
}
|
|
|
|
#if DBG
|
|
if (pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) {
|
|
UserAssert(!TestCF(pwndOwner, CFIME));
|
|
}
|
|
#endif
|
|
Lock(&(pwnd->spwndOwner), pwndOwner);
|
|
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.
|
|
*/
|
|
if (atomT != gpsi->atomSysClass[ICLS_IME] &&
|
|
pwnd->spwndOwner != NULL &&
|
|
GETPTI(pwnd->spwndOwner) != ptiCurrent) {
|
|
/*
|
|
* No need to DeferWinEventNotify() here: pwnd and pwndParent
|
|
* are locked and because we called ReferenceClass(pcls, pwnd),
|
|
* pcls is safe until xxxFreeWindow(pwnd). (IanJa)
|
|
*/
|
|
zzzAttachThreadInput(ptiCurrent, GETPTI(pwnd->spwndOwner), TRUE);
|
|
}
|
|
|
|
} else {
|
|
pwnd->spwndOwner = NULL;
|
|
}
|
|
|
|
#if DBG
|
|
if (ptiCurrent->rpdesk != NULL) {
|
|
UserAssert(!(ptiCurrent->rpdesk->dwDTFlags & (DF_DESTROYED | DF_DESKWNDDESTROYED | DF_DYING)));
|
|
}
|
|
#endif
|
|
if ((pwndParent == NULL) ||
|
|
(pwndParent != pwndParent->head.rpdesk->spwndMessage)) {
|
|
pwndParent = _GetDesktopWindow();
|
|
|
|
ThreadLockWithPti(ptiCurrent, pwndParent, &tlpwndParent);
|
|
fLockParent = TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Store backpointer to parent.
|
|
*/
|
|
if ((pwnd->spwndNext != NULL) || (pwnd->spwndPrev != NULL)) {
|
|
RIPMSG1(RIP_WARNING, "Window %#p linked in too early (in a hook callback)", pwnd);
|
|
UnlinkWindow(pwnd, pwnd->spwndParent);
|
|
}
|
|
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)) {
|
|
#if DBG
|
|
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))) {
|
|
/*
|
|
* No need to DeferWinEventNotify() - there is an xxx call just below
|
|
*/
|
|
zzzAttachThreadInput(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 mustn't reorder the messages.
|
|
*/
|
|
xxxAdjustSize(pwnd, &src.cx, &src.cy);
|
|
|
|
/*
|
|
* 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, &src);
|
|
}
|
|
|
|
if (src.cx < 0) {
|
|
RIPMSG1(RIP_WARNING, "xxxCreateWindowEx: adjusted cx in pwnd %#p", pwnd);
|
|
src.cx = 0;
|
|
}
|
|
|
|
if (src.cy < 0) {
|
|
RIPMSG1(RIP_WARNING, "xxxCreateWindowEx: adjusted cy in pwnd %#p", pwnd);
|
|
src.cy = 0;
|
|
}
|
|
|
|
/*
|
|
* Calculate final window dimensions...
|
|
*/
|
|
RECTFromSIZERECT(&pwnd->rcWindow, &src);
|
|
|
|
if (TestCF2(pcls, CFOWNDC) || (TestCF2(pcls, CFCLASSDC) && pcls->pdce == NULL)) {
|
|
if (NULL == CreateCacheDC(pwnd, DCX_OWNDC, NULL)) {
|
|
|
|
RIPMSG1(RIP_WARNING, "xxxCreateWindowEx: pwnd %#p failed to create cached DC",
|
|
pwnd);
|
|
|
|
goto MemError;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Setup Layered and Composited windows.
|
|
*
|
|
* NOTE: This MUST be done AFTER CreateCacheDC() has built any DC's used for
|
|
* OWNDC windows, since the redirection functions need to convert these DC's
|
|
* to use redirection. In Windows 2000, this was done before calling
|
|
* CreateCacheDC(), and would Assert inside ResetOrg() when because a new
|
|
* DC was built that was not setup for redirection.
|
|
*/
|
|
if (dwExStyle & WS_EX_LAYERED) {
|
|
if (!xxxSetLayeredWindow(pwnd, FALSE)) {
|
|
RIPMSG1(RIP_WARNING, "xxxCreateWindowEx: pwnd %#p failed to setup layered window", pwnd);
|
|
goto MemError;
|
|
}
|
|
}
|
|
|
|
if (dwExStyle & WS_EX_COMPOSITED) {
|
|
/*
|
|
* We only want to turn WS_EX_COMPOSITED on if the parent-chain doesn't
|
|
* already have WS_EX_COMPOSITED turned on.
|
|
*/
|
|
|
|
if (GetStyleWindow(pwnd->spwndParent, WEFCOMPOSITED) == NULL) {
|
|
if (!SetRedirectedWindow(pwnd, REDIRECT_COMPOSITED)) {
|
|
RIPMSG1(RIP_WARNING, "xxxCreateWindowEx: pwnd %#p failed to setup composited window", pwnd);
|
|
goto MemError;
|
|
}
|
|
SetWF(pwnd, WEFCOMPOSITED);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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, (LPARAM)&csex)) {
|
|
|
|
MemError:
|
|
|
|
#if DBG
|
|
if (!IS_PTR(cczpstrNVClass)) {
|
|
RIPMSG2(RIP_WARNING,
|
|
(pwndParent) ?
|
|
"xxxCreateWindowEx failed, Class=%#.4x, ID=%d" :
|
|
"xxxCreateWindowEx failed, Class=%#.4x",
|
|
PTR_TO_ID(cczpstrNVClass),
|
|
(LONG_PTR) pMenu);
|
|
} else {
|
|
RIPMSG2(RIP_WARNING,
|
|
(pwndParent) ?
|
|
"xxxCreateWindowEx failed, Class=\"%s\", ID=%d" :
|
|
"xxxCreateWindowEx failed, Class=\"%s\"",
|
|
pcls->lpszAnsiClassName,
|
|
(LONG_PTR) 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 sent or
|
|
* apps like Rumba fault (they call GetParent after every message).
|
|
*/
|
|
SetWF(pwnd, WFDESTROYED);
|
|
|
|
/*
|
|
* Unset the visible flag so we don't think in xxxDestroyWindow that
|
|
* this window is visible.
|
|
*/
|
|
if (TestWF(pwnd, WFVISIBLE)) {
|
|
SetVisible(pwnd, SV_UNSET);
|
|
}
|
|
|
|
/*
|
|
* FreeWindow performs a ThreadUnlock.
|
|
*/
|
|
xxxFreeWindow(pwnd, &tlpwnd);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* We need to set the lame button flag before doing the CFNOCLOSE stuff
|
|
* below or the app won't get the lame button menu item in its sysmenu.
|
|
*/
|
|
#ifdef LAME_BUTTON
|
|
if (NeedsLameButton(pwnd, pwndParent)) {
|
|
SetWF(pwnd, WEFLAMEBUTTON);
|
|
}
|
|
#endif // LAME_BUTTON
|
|
|
|
/*
|
|
* Delete the Close menu item if directed.
|
|
*/
|
|
if (TestCF(pwnd, CFNOCLOSE)) {
|
|
/*
|
|
* Do this by position since the separator does not have an ID.
|
|
*/
|
|
pMenu = xxxGetSystemMenu(pwnd, FALSE);
|
|
if (pMenu != NULL) {
|
|
TL tlpMenu;
|
|
|
|
ThreadLock(pMenu, &tlpMenu);
|
|
xxxDeleteMenu(pMenu, 5
|
|
#ifdef LAME_BUTTON
|
|
+ !!TestWF(pwnd, WEFLAMEBUTTON)
|
|
#endif // LAME_BUTTON
|
|
, MF_BYPOSITION);
|
|
xxxDeleteMenu(pMenu, 5
|
|
#ifdef LAME_BUTTON
|
|
+ !!TestWF(pwnd, WEFLAMEBUTTON)
|
|
#endif // LAME_BUTTON
|
|
, MF_BYPOSITION);
|
|
ThreadUnlock(&tlpMenu);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Create per thread default IME window.
|
|
*/
|
|
if (IS_IME_ENABLED() && ptiCurrent->spwndDefaultIme == NULL) {
|
|
/*
|
|
* Avoid creating the default IME window to any of message only windows
|
|
* or windows on no I/O desktop.
|
|
*/
|
|
if (WantImeWindow(pwndParent, pwnd)) {
|
|
BOOL bReinit;
|
|
|
|
//
|
|
// Make sure we are not creating a window for Ole,
|
|
// for it does not pump messages even though
|
|
// they creates a window.
|
|
//
|
|
UserAssert(gaOleMainThreadWndClass != atomT);
|
|
|
|
Lock(&(ptiCurrent->spwndDefaultIme),
|
|
xxxCreateDefaultImeWindow(pwnd, atomT, hInstance));
|
|
|
|
|
|
/*
|
|
* If keybaord layout is switched but Imm activation was skipped
|
|
* while spwndDefaultIme was gone, do the activation now.
|
|
*/
|
|
#if _DBG
|
|
if (ptiCurrent->spDefaultImc == NULL) {
|
|
RIPMSG1(RIP_WARNING, "xxxCreateWindowEx: ptiCurrent(%08p)->spDefaultImc is NULL.", ptiCurrent);
|
|
}
|
|
ASSERT(ptiCurrent->pClientInfo);
|
|
#endif
|
|
|
|
|
|
#ifdef CUAS_ENABLE
|
|
/*
|
|
* Load IME and Activate TIM for CUAS.
|
|
* We can do this after ptiCurrent->spwndDefaultIME is valid.
|
|
*/
|
|
if (ptiCurrent->spwndDefaultIme) {
|
|
TL tlpwndIme;
|
|
|
|
ThreadLockAlways(ptiCurrent->spwndDefaultIme, &tlpwndIme);
|
|
xxxSendMessage(ptiCurrent->spwndDefaultIme, WM_IME_SYSTEM, (WPARAM)IMS_LOADTHREADLAYOUT, (LPARAM)0L);
|
|
ThreadUnlock(&tlpwndIme);
|
|
}
|
|
#endif
|
|
|
|
try {
|
|
bReinit = ((ptiCurrent->pClientInfo->CI_flags & CI_INPUTCONTEXT_REINIT) != 0);
|
|
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
|
|
goto MemError2;
|
|
}
|
|
|
|
if (ptiCurrent->spwndDefaultIme && bReinit) {
|
|
TL tlpwndIme;
|
|
|
|
TAGMSG1(DBGTAG_IMM,
|
|
"xxxCreateDefaultImeWindow: ptiCurrent(%08p)->spDefaultImc->fNeedClientImcActivate is set.",
|
|
ptiCurrent);
|
|
/*
|
|
* Make this client side callback to force the input context
|
|
* to be reinitialized appropriately (keyboard layout has
|
|
* changed since this thread was taking a nap without a
|
|
* window but still a GUI thread).
|
|
*
|
|
* Windows NT Bug #294964.
|
|
*/
|
|
ThreadLock(ptiCurrent->spwndDefaultIme, &tlpwndIme);
|
|
xxxSendMessage(ptiCurrent->spwndDefaultIme,
|
|
WM_IME_SYSTEM,
|
|
(WPARAM)IMS_ACTIVATETHREADLAYOUT,
|
|
(LPARAM)ptiCurrent->spklActive->hkl);
|
|
|
|
// Reset the flag.
|
|
try {
|
|
ptiCurrent->pClientInfo->CI_flags &= ~CI_INPUTCONTEXT_REINIT;
|
|
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
|
|
ThreadUnlock(&tlpwndIme);
|
|
goto MemError2;
|
|
}
|
|
ThreadUnlock(&tlpwndIme);
|
|
}
|
|
} else {
|
|
TAGMSG0(DBGTAG_IMM,
|
|
"xxxCreateWindowEx: default IME window not created.");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Update the Parent/Child linked list. Don't (re)link the window if a
|
|
* diffrent parent was set during a callback (e.g., WM_GETMINMAXINFO).
|
|
*/
|
|
if (pwndParent != NULL && pwnd->spwndParent == pwndParent) {
|
|
/*
|
|
* Unlink this window first, it might have been linked already by
|
|
* calling SetParent() in any of the messages that we had previously
|
|
* sent (e.g. WM_GETMINMAXINFO).
|
|
*/
|
|
UnlinkWindow(pwnd, pwnd->spwndParent);
|
|
|
|
if (!fChild && (pwndParent != pwndParent->head.rpdesk->spwndMessage)) {
|
|
|
|
/*
|
|
* 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 ||
|
|
(IS_PTR(pwndZOrder) && TestWF(pwndZOrder, WEFTOPMOST))) {
|
|
pwndZOrder = CalcForegroundInsertAfter(pwnd);
|
|
}
|
|
} else {
|
|
pwndHardError = GETTOPMOSTINSERTAFTER(pwnd);
|
|
if (pwndHardError != NULL) {
|
|
pwndZOrder = pwndHardError;
|
|
}
|
|
}
|
|
}
|
|
|
|
LinkWindow(pwnd, pwndZOrder, pwndParent);
|
|
}
|
|
|
|
#if DBG
|
|
if (pwndParent != NULL) {
|
|
VerifyWindowLink (pwnd, pwnd->spwndParent, TRUE);
|
|
|
|
if (pwnd->spwndParent != pwndParent) {
|
|
RIPMSGF1(RIP_WARNING,
|
|
"Window 0x%p re-parented during callback",
|
|
pwnd);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Send a NCCALCSIZE message to the window and have it return the official
|
|
* size of its client area.
|
|
*/
|
|
if (fChild && TestWF(pwndParent, WEFLAYOUTRTL)) {
|
|
cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
|
|
pwnd->rcWindow.right = pwndParent->rcClient.right - (pwnd->rcWindow.left - pwndParent->rcClient.left);
|
|
pwnd->rcWindow.left = pwnd->rcWindow.right - cx;
|
|
}
|
|
|
|
CopyRect(&rc, &pwnd->rcWindow);
|
|
xxxSendMessage(pwnd, WM_NCCALCSIZE, 0L, (LPARAM)&rc);
|
|
pwnd->rcClient = rc;
|
|
|
|
/*
|
|
* Send a CREATE message to the window.
|
|
*/
|
|
if (xxxSendMessage(pwnd, WM_CREATE, 0L, (LPARAM)&csex) == -1L) {
|
|
#if DBG
|
|
if (!IS_PTR(cczpstrNVClass)) {
|
|
RIPMSG1(RIP_WARNING,
|
|
"CreateWindow() send of WM_CREATE failed, Class = 0x%x",
|
|
PTR_TO_ID(cczpstrNVClass));
|
|
} else {
|
|
RIPMSG1(RIP_WARNING,
|
|
"CreateWindow() send of WM_CREATE failed, Class = \"%s\"",
|
|
pcls->lpszAnsiClassName);
|
|
}
|
|
#endif
|
|
|
|
MemError2:
|
|
if (fLockParent) {
|
|
ThreadUnlock(&tlpwndParent);
|
|
}
|
|
|
|
if (ThreadUnlock(&tlpwnd)) {
|
|
xxxDestroyWindow(pwnd);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Flag that the window is created. WoW uses this bit to determine that
|
|
* an fnid of 0 really means 0.
|
|
*/
|
|
SetWF(pwnd, WFISINITIALIZED);
|
|
|
|
/*
|
|
* Notify anyone who is listening that the window is created. Do this
|
|
* before we size/move/max/min/show it so that event observers can count
|
|
* on getting notifications for those things also.
|
|
*
|
|
* But do this AFTER WM_CREATE is sent. The window and its data will not
|
|
* be fully initialized until then. Since the purpose of an event is to
|
|
* let watchers turn around and do querying, we want their queries to
|
|
* succeed and not fault.
|
|
*/
|
|
xxxWindowEvent(EVENT_OBJECT_CREATE, pwnd, OBJID_WINDOW, INDEXID_OBJECT, 0);
|
|
|
|
/*
|
|
* 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 && PWNDDESKTOP(pwnd) != pwndParent) {
|
|
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.
|
|
*/
|
|
dwMinMax = MINMAX_KEEPHIDDEN | TEST_PUDF(PUDF_ANIMATE);
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
SetMinimize(pwnd, SMIN_CLEAR);
|
|
xxxMinMaximize(pwnd, SW_SHOWMINNOACTIVE, dwMinMax);
|
|
} else if (TestWF(pwnd, WFMAXIMIZED)) {
|
|
ClrWF(pwnd, WFMAXIMIZED);
|
|
xxxMinMaximize(pwnd, SW_SHOWMAXIMIZED, dwMinMax);
|
|
}
|
|
|
|
/*
|
|
* Send notification if it's a child.
|
|
*/
|
|
if (fChild && !TestWF(pwnd, WEFNOPARENTNOTIFY) &&
|
|
(pwnd->spwndParent != NULL)) {
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwnd->spwndParent, &tlpwndParentT);
|
|
xxxSendMessage(pwnd->spwndParent, WM_PARENTNOTIFY,
|
|
MAKELONG(WM_CREATE, PTR_TO_ID(pwnd->spmenu)), (LPARAM)HWq(pwnd));
|
|
ThreadUnlock(&tlpwndParentT);
|
|
}
|
|
|
|
/*
|
|
* Show the Window
|
|
*/
|
|
if (style & WS_VISIBLE) {
|
|
xxxShowWindow(pwnd, sw | TEST_PUDF(PUDF_ANIMATE));
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fLockParent)
|
|
ThreadUnlock(&tlpwndParent);
|
|
|
|
return ThreadUnlock(&tlpwnd);
|
|
}
|
|
|
|
BOOL WantImeWindow(
|
|
PWND pwndParent,
|
|
PWND pwnd)
|
|
{
|
|
PDESKTOP pdesk;
|
|
|
|
UserAssert(pwnd);
|
|
|
|
if (PtiCurrent()->TIF_flags & TIF_DISABLEIME) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (TestWF(pwnd, WFSERVERSIDEPROC)) {
|
|
return FALSE;
|
|
}
|
|
|
|
pdesk = pwnd->head.rpdesk;
|
|
if (pdesk == NULL || pdesk->rpwinstaParent == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Check whether pwnd's desktop has I/O.
|
|
if (pdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Check if the owner window is message-only window.
|
|
if (pwndParent) {
|
|
PWND pwndT = pwndParent;
|
|
|
|
while (pwndT && pdesk == pwndT->head.rpdesk) {
|
|
if (pwndT == pdesk->spwndMessage) {
|
|
return FALSE;
|
|
}
|
|
pwndT = pwndT->spwndParent;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* SetTiledRect
|
|
*
|
|
* History:
|
|
* 10-19-90 darrinm Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
VOID SetTiledRect(
|
|
PWND pwnd,
|
|
LPRECT lprc,
|
|
PMONITOR pMonitor)
|
|
{
|
|
POINT pt;
|
|
RECT rcT;
|
|
|
|
UserAssert(pMonitor->cWndStack >= 0);
|
|
|
|
/*
|
|
* Get available desktop area, minus minimized spacing area.
|
|
*/
|
|
GetRealClientRect(PWNDDESKTOP(pwnd), &rcT, GRC_MINWNDS, pMonitor);
|
|
|
|
/*
|
|
* Increment the count of stacked windows.
|
|
*/
|
|
pMonitor->cWndStack++;
|
|
/*
|
|
* 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.
|
|
*/
|
|
#define X_TILED (SYSMET(CXSIZEFRAME) + SYSMET(CXSIZE))
|
|
#define Y_TILED (SYSMET(CYSIZEFRAME) + SYSMET(CYSIZE))
|
|
|
|
pt.x = pMonitor->cWndStack * X_TILED;
|
|
pt.y = pMonitor->cWndStack * Y_TILED;
|
|
|
|
/*
|
|
* If below upper top left 1/4 of free area, reset.
|
|
*/
|
|
if ( (pt.x > ((rcT.right-rcT.left) / 4)) ||
|
|
(pt.y > ((rcT.bottom-rcT.top) / 4)) ) {
|
|
|
|
pMonitor->cWndStack = 0;
|
|
pt.x = X_TILED;
|
|
pt.y = Y_TILED;
|
|
}
|
|
|
|
#undef X_TILED
|
|
#undef Y_TILED
|
|
/*
|
|
* Get starting position
|
|
*/
|
|
pt.x += rcT.left;
|
|
pt.y += rcT.top;
|
|
|
|
lprc->left = pt.x;
|
|
lprc->top = pt.y;
|
|
lprc->right = pt.x + MultDiv(rcT.right-rcT.left, 3, 4);
|
|
lprc->bottom = pt.y + MultDiv(rcT.bottom-rcT.top, 3, 4);
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* 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;
|
|
MINMAXINFO mmi;
|
|
|
|
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, &mmi);
|
|
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
ptmin = mmi.ptReserved;
|
|
ptmax = mmi.ptMaxSize;
|
|
} else {
|
|
ptmin = mmi.ptMinTrackSize;
|
|
ptmax = mmi.ptMaxTrackSize;
|
|
}
|
|
|
|
//
|
|
// 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));
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
/***************************************************************************\
|
|
* VerifyWindowLink
|
|
*
|
|
* History:
|
|
* 10/28/96 GerardoB Added
|
|
\***************************************************************************/
|
|
VOID VerifyWindowLink(
|
|
PWND pwnd,
|
|
PWND pwndParent,
|
|
BOOL fLink)
|
|
{
|
|
BOOL fFirstFound = FALSE;
|
|
BOOL fInFound = FALSE;
|
|
PWND pwndNext = pwndParent->spwndChild;
|
|
PWND pwndFirst = pwndNext;
|
|
|
|
while (pwndNext != NULL) {
|
|
if (pwndFirst == pwndNext) {
|
|
if (fFirstFound) {
|
|
RIPMSG1(RIP_ERROR, "Loop in %#p spwndNext chain", pwnd);
|
|
return;
|
|
} else {
|
|
fFirstFound = TRUE;
|
|
}
|
|
}
|
|
|
|
if (pwndNext == pwnd) fInFound = TRUE;
|
|
pwndNext = pwndNext->spwndNext;
|
|
}
|
|
|
|
if (fLink && !fInFound) {
|
|
RIPMSG1(RIP_ERROR, "pwnd 0x%p not found in spwndNext chain", pwnd);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************\
|
|
* LinkWindow
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
VOID LinkWindow(
|
|
PWND pwnd,
|
|
PWND pwndInsert,
|
|
PWND pwndParent)
|
|
{
|
|
if (pwndParent->spwndChild == pwnd) {
|
|
RIPMSG0(RIP_WARNING, "Attempting to link a window to itself");
|
|
return;
|
|
}
|
|
|
|
UserAssert(pwnd != pwndInsert);
|
|
UserAssert((pwnd->spwndParent == NULL) || (pwnd->spwndParent == pwndParent));
|
|
|
|
if (pwndInsert == PWND_TOP) {
|
|
|
|
/*
|
|
* We are at the top of the list.
|
|
*/
|
|
LinkTop:
|
|
#if DBG
|
|
/*
|
|
* If the first child is topmost, so must be pwnd, but only for
|
|
* top-level windows.
|
|
*
|
|
* IME or IME related windows are the exceptions, because ImeSetTopmost
|
|
* and its friends do most of the relinking on its own: When LinkWindow
|
|
* is called, it's possible TOPMOST flags are left in intermediate
|
|
* state. By the time the all window relinking finishes, TOPMOST flags
|
|
* have been taken care of and they are just fine.
|
|
*/
|
|
if (pwndParent == PWNDDESKTOP(pwndParent) &&
|
|
pwndParent->spwndChild &&
|
|
FSwpTopmost(pwndParent->spwndChild) &&
|
|
pwndParent != PWNDMESSAGE(pwndParent) &&
|
|
// Check if the target is IME related window
|
|
!TestCF(pwnd, CFIME) && pwnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME]) {
|
|
|
|
/*
|
|
* There are few cases that cause the z-ordering code to leave the
|
|
* WFTOGGLETOPMOST bit set. One is when SWP_NOOWNERZORDER is used
|
|
* when changing the topmost state of a window; in this case,
|
|
* ZOrderByOwner2 doesn't add ownees to the psmwp list, still
|
|
* SetTopMost sets the bit on all the ownees.
|
|
*
|
|
* Another case is when SetWindowPos gets re-entered on the same
|
|
* window. It's too late to attempt to fix this ancient behavior
|
|
* (2/24/99) so let's turn off the assert for now.
|
|
*/
|
|
if (!FSwpTopmost(pwnd)) {
|
|
RIPMSG1(RIP_WARNING, "LinkWindow pwnd:%p is not FSwpTopmost", pwnd);
|
|
}
|
|
}
|
|
#endif // DBG
|
|
|
|
if (pwndParent->spwndChild != NULL) {
|
|
Lock(&pwndParent->spwndChild->spwndPrev, pwnd);
|
|
Lock(&pwnd->spwndNext, pwndParent->spwndChild);
|
|
}
|
|
Lock(&(pwndParent->spwndChild), pwnd);
|
|
UserAssert(pwnd->spwndPrev == NULL);
|
|
} else {
|
|
if (pwndInsert == PWND_BOTTOM) {
|
|
|
|
/*
|
|
* Find bottom-most window.
|
|
*/
|
|
if (((pwndInsert = pwndParent->spwndChild) == 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)) {
|
|
#if DBG
|
|
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(TestWF(pwnd, WFCHILD) || !TestWF(pwnd, WEFTOPMOST) || TestWF(pwndInsert, WEFTOPMOST) || TestWF(pwnd, WFTOGGLETOPMOST) || (pwndParent != PWNDDESKTOP(pwndInsert)));
|
|
UserAssert(pwnd->spwndParent == pwndInsert->spwndParent);
|
|
|
|
if (pwndInsert->spwndNext != NULL) {
|
|
Lock(&pwndInsert->spwndNext->spwndPrev, pwnd);
|
|
Lock(&pwnd->spwndNext, pwndInsert->spwndNext);
|
|
}
|
|
Lock(&pwnd->spwndPrev, pwndInsert);
|
|
Lock(&pwndInsert->spwndNext, pwnd);
|
|
}
|
|
|
|
if (TestWF(pwnd, WEFLAYERED))
|
|
TrackLayeredZorder(pwnd);
|
|
|
|
#if DBG
|
|
VerifyWindowLink (pwnd, pwndParent, TRUE);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* 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, pmnsEnd;
|
|
PTHREADINFO pti = PtiCurrent();
|
|
TL tlpwnd, tlpwndFocus, tlpwndParent;
|
|
PWND pwndFocus;
|
|
BOOL fAlreadyDestroyed;
|
|
DWORD dwDisableHooks;
|
|
|
|
dwDisableHooks = 0;
|
|
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.
|
|
*/
|
|
fAlreadyDestroyed = HMIsMarkDestroy(pwnd);
|
|
if (fAlreadyDestroyed) {
|
|
/*
|
|
* UserAssert(dwInAtomicOperation > 0);
|
|
* This Assert ensures that we are here only because of an unlock
|
|
* on a previously destroyed window. We BEGIN/ENDATOMICHCHECK in
|
|
* HMDestroyUnlockedObject to ensure we don't leave the crit sect
|
|
* unexpectedly, which gives us dwInAtomicCheck > 0. We set
|
|
* TIF_DISABLEHOOKS to prevent a callback in Unlock
|
|
* However, it is currently possible destroy the same window handle
|
|
* twice, since we don't (yet) fail to revalidate zombie handles:
|
|
* GerardoB may change this, at which time we should probably restore
|
|
* this Assert, and test #76902 (close winmsd.exe) again. (preventing
|
|
* hooks in a second destroy of a zombie window should be OK) - IanJa
|
|
*/
|
|
// UserAssert(dwInAtomicOperation > 0);
|
|
|
|
if (HMPheFromObject(pwnd)->pOwner != pti) {
|
|
UserAssert(PsGetCurrentThreadWin32Thread());
|
|
HMChangeOwnerThread(pwnd, pti);
|
|
}
|
|
dwDisableHooks = pti->TIF_flags & TIF_DISABLEHOOKS;
|
|
pti->TIF_flags |= TIF_DISABLEHOOKS;
|
|
} else {
|
|
/*
|
|
* 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, (WPARAM)HWq(pwnd), 0, WH_CBT)) {
|
|
goto FalseReturn;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the window we are destroying is in menu mode, end the menu
|
|
*/
|
|
pMenuState = GetpMenuState(pwnd);
|
|
if ((pMenuState != NULL)
|
|
&& (pwnd == pMenuState->pGlobalPopupMenu->spwndNotify)) {
|
|
|
|
MNEndMenuStateNotify(pMenuState);
|
|
/*
|
|
* Signal all states to end. The window(s) will be unlocked when
|
|
* the menu exits; we cannot unlock it now because the menu
|
|
* code could fault.
|
|
*/
|
|
pmnsEnd = pMenuState;
|
|
do {
|
|
UserAssert(pwnd == pMenuState->pGlobalPopupMenu->spwndNotify);
|
|
pMenuState->fInsideMenuLoop = FALSE;
|
|
pMenuState = pMenuState->pmnsPrev;
|
|
} while (pMenuState != NULL) ;
|
|
|
|
/*
|
|
* All states have been signaled to exit, so once we callback
|
|
* we cannot count on pmnsEnd->pmnsPrev to be valid. Thus
|
|
* we simply end the current menu here and let the others go
|
|
* on their own. No state points to pwnd anymore so that
|
|
* should be OK.
|
|
*/
|
|
if (!pmnsEnd->fModelessMenu) {
|
|
xxxEndMenu(pmnsEnd);
|
|
}
|
|
}
|
|
|
|
if (ghwndSwitch == HWq(pwnd)) {
|
|
ghwndSwitch = NULL;
|
|
}
|
|
|
|
if (!TestWF(pwnd, WFCHILD) && (pwnd->spwndOwner == NULL)) {
|
|
if (TestWF(pwnd, WFHASPALETTE)) {
|
|
xxxFlushPalette(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.
|
|
*/
|
|
if (pwnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME] &&
|
|
!TestwndChild(pwnd) && pwnd->spwndOwner != NULL &&
|
|
GETPTI(pwnd->spwndOwner) != GETPTI(pwnd)) {
|
|
/*
|
|
* No need to zzzDeferWinEventNotify() - there is an xxx call just below
|
|
*/
|
|
zzzAttachThreadInput(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, PTR_TO_ID(pwnd->spmenu)), (LPARAM)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 coming 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 messed 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, SW_HIDE | TEST_PUDF(PUDF_ANIMATE));
|
|
} 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 |
|
|
(fAlreadyDestroyed ? SWP_DEFERDRAWING : 0));
|
|
}
|
|
|
|
/*
|
|
* Under low memory conditions, the above attempt to hide could fail.
|
|
*/
|
|
if (TestWF(pwnd, WFVISIBLE)) {
|
|
RIPMSG0(RIP_WARNING, "xxxDestroyWindow: normal hide failed");
|
|
SetVisible(pwnd, SV_UNSET);
|
|
|
|
/*
|
|
* Invalidate windows below so they redraw properly.
|
|
*/
|
|
xxxRedrawWindow(NULL, &pwnd->rcWindow, NULL, RDW_INVALIDATE |
|
|
RDW_ERASE | RDW_ALLCHILDREN);
|
|
|
|
}
|
|
} else if (IsTrayWindow(pwnd)) {
|
|
PostShellHookMessages(HSHELL_WINDOWDESTROYED,
|
|
(LPARAM)PtoHq( pwnd ));
|
|
}
|
|
|
|
/*
|
|
* Destroy any owned windows.
|
|
* [msadek], the check for FNID_GHOST is to fix bug# 380208, 382758
|
|
* where we recieve QEVENT_HUNGTHREAD for the owner window first.
|
|
* since we post the event multiple times (once for each owned window)
|
|
* We are guranteed to go through xxxDestroyWindow for owned windows too.
|
|
*/
|
|
if (!TestWF(pwnd, WFCHILD) && (GETFNID(pwnd) != FNID_GHOST)) {
|
|
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) {
|
|
PWND pwndActivate = NULL;
|
|
TL tlpwndActivate;
|
|
UINT cmdActivate;
|
|
|
|
/*
|
|
* 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) {
|
|
pwndActivate = pwnd->spwndOwner;
|
|
cmdActivate = AW_TRY;
|
|
} else {
|
|
pwndActivate = pwnd;
|
|
cmdActivate = AW_SKIP;
|
|
}
|
|
} else if ((pti->pq->spwndActive == NULL) && (gpqForeground == pti->pq)) {
|
|
pwndActivate = pwnd;
|
|
cmdActivate = AW_SKIP;
|
|
}
|
|
|
|
if (pwndActivate) {
|
|
ThreadLockAlwaysWithPti(pti, pwndActivate, &tlpwndActivate);
|
|
|
|
if (!xxxActivateWindow(pwndActivate, cmdActivate) ||
|
|
((cmdActivate == AW_SKIP) && (pwnd == pti->pq->spwndActive))) {
|
|
if ((cmdActivate == AW_SKIP) || (pwnd == pti->pq->spwndActive)) {
|
|
Unlock(&pti->pq->spwndActive);
|
|
pwndFocus = Unlock(&pti->pq->spwndFocus);
|
|
if (IS_IME_ENABLED() && pwndFocus != NULL) {
|
|
ThreadLockAlwaysWithPti(pti, pwndFocus, &tlpwndFocus);
|
|
xxxFocusSetInputContext(pwndFocus, FALSE, FALSE);
|
|
ThreadUnlock(&tlpwndFocus);
|
|
}
|
|
if (pti->pq == gpqForeground) {
|
|
xxxWindowEvent(EVENT_OBJECT_FOCUS, NULL, OBJID_CLIENT,
|
|
INDEXID_CONTAINER, 0);
|
|
xxxWindowEvent(EVENT_SYSTEM_FOREGROUND, NULL, OBJID_WINDOW,
|
|
INDEXID_CONTAINER, WEF_USEPWNDTHREAD);
|
|
}
|
|
zzzInternalDestroyCaret();
|
|
}
|
|
}
|
|
|
|
ThreadUnlock(&tlpwndActivate);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fix last active popup.
|
|
*/
|
|
{
|
|
PWND pwndOwner = pwnd->spwndOwner;
|
|
|
|
if (pwndOwner != NULL) {
|
|
while (pwndOwner->spwndOwner != NULL) {
|
|
pwndOwner = pwndOwner->spwndOwner;
|
|
}
|
|
|
|
if (pwnd == pwndOwner->spwndLastActive) {
|
|
/*
|
|
* If pwndOwner is marked for destruction, locking it here
|
|
* will prevent it from ever being freed, thus preventing
|
|
* the associated session memory from going away. Just
|
|
* unlock pwndOwner->spwndLastActive in this case.
|
|
* [msadek- 03/02/2002]
|
|
*/
|
|
if (HMIsMarkDestroy(pwndOwner)) {
|
|
Unlock(&pwndOwner->spwndLastActive);
|
|
} else {
|
|
Lock(&(pwndOwner->spwndLastActive), pwnd->spwndOwner);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!fAlreadyDestroyed) {
|
|
/*
|
|
* Note we do this BEFORE telling the app the window is dying. Note
|
|
* also that we do NOT loop through the children generating DESTROY
|
|
* events. DESTROY of a parent implies DESTROY of all children (see
|
|
* Windows NT Bug #71846).
|
|
*/
|
|
if (!TestWF(pwnd, WFDESTROYED)) {
|
|
xxxWindowEvent(EVENT_OBJECT_DESTROY, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, 0);
|
|
}
|
|
|
|
/*
|
|
* Send destroy messages before the WindowLockStart in case
|
|
* he tries to destroy windows as a result.
|
|
*/
|
|
xxxDW_SendDestroyMessages(pwnd);
|
|
}
|
|
|
|
/*
|
|
* Check the owner of IME window again.
|
|
* If thread is destroying, don't bother to check.
|
|
*/
|
|
if (IS_IME_ENABLED() && !(pti->TIF_flags & TIF_INCLEANUP) &&
|
|
pti->spwndDefaultIme != NULL &&
|
|
!TestCF(pwnd, CFIME) &&
|
|
pwnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME]) {
|
|
|
|
if (fAlreadyDestroyed) {
|
|
RIPMSG2(RIP_VERBOSE,
|
|
"xxxDestroyWindow: in final destruction of 0x%p, ime=0x%p",
|
|
pwnd,
|
|
pti->spwndDefaultIme);
|
|
} else {
|
|
if (!TestWF(pwnd, WFCHILD)) {
|
|
if (ImeCanDestroyDefIME(pti->spwndDefaultIme, pwnd)) {
|
|
TAGMSG1(DBGTAG_IMM, "xxxDestroyWindow: destroying (1) the default IME window=%p", pti->spwndDefaultIme);
|
|
xxxDestroyWindow(pti->spwndDefaultIme);
|
|
}
|
|
} else if (pwnd->spwndParent != NULL) {
|
|
if (ImeCanDestroyDefIMEforChild(pti->spwndDefaultIme, pwnd)) {
|
|
TAGMSG1(DBGTAG_IMM, "xxxDestroyWindow: destroying (2) the default IME window=%p", pti->spwndDefaultIme);
|
|
xxxDestroyWindow(pti->spwndDefaultIme);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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))) {
|
|
/*
|
|
* pwnd is threadlocked, so no need to DeferWinEventNotify()
|
|
*/
|
|
CheckLock(pwnd);
|
|
zzzAttachThreadInput(GETPTI(pwnd), GETPTI(pwnd->spwndParent), FALSE);
|
|
}
|
|
|
|
UnlinkWindow(pwnd, pwnd->spwndParent);
|
|
}
|
|
|
|
/*
|
|
* 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).
|
|
*/
|
|
#if DBG
|
|
if (pwnd == pti->pq->spwndActive) {
|
|
RIPMSG1(RIP_WARNING, "xxxDestroyWindow: pwnd == pti->pq->spwndActive (%#p)", 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);
|
|
|
|
/*
|
|
* FreeWindow performs a ThreadUnlock.
|
|
*/
|
|
xxxFreeWindow(pwnd, &tlpwnd);
|
|
|
|
if (fAlreadyDestroyed) {
|
|
pti->TIF_flags = (pti->TIF_flags & ~TIF_DISABLEHOOKS) | dwDisableHooks;
|
|
}
|
|
return TRUE;
|
|
|
|
FalseReturn:
|
|
if (fAlreadyDestroyed) {
|
|
pti->TIF_flags = (pti->TIF_flags & ~TIF_DISABLEHOOKS) | dwDisableHooks;
|
|
}
|
|
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;
|
|
PWND pwndDefaultIme = GETPTI(pwndParent)->spwndDefaultIme;
|
|
|
|
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) {
|
|
/*
|
|
* We don't destroy the IME window here
|
|
* unless the thread is doing cleanup.
|
|
*/
|
|
if (IS_IME_ENABLED() && !(GETPTI(pwndParent)->TIF_flags & TIF_INCLEANUP) &&
|
|
pwnd == pwndDefaultIme) {
|
|
Unlock(&pwnd->spwndOwner);
|
|
pwnd = pwnd->spwndNext;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
/*
|
|
* Pass along the pwnd which is the reason we are dismissing the
|
|
* clipboard. We want to later make sure the owner is still this
|
|
* window after we make callbacks and clear the owner
|
|
*/
|
|
xxxDisownClipboard(pwnd);
|
|
}
|
|
|
|
/*
|
|
* Send the WM_DESTROY message.
|
|
*/
|
|
#if _DBG
|
|
if (pwnd == PtiCurrent()->spwndDefaultIme) {
|
|
TAGMSG2(DBGTAG_IMM, "xxxDW_SendDestroyMessages: sending WM_DESTROY message to def IME=%p, pti=%p", pwnd, PtiCurrent());
|
|
}
|
|
#endif
|
|
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);
|
|
|
|
/*
|
|
* Propagate the VISIBLE flag. We need to do this so that
|
|
* when a child window gets destroyed we don't try to hide it
|
|
* if the WFVISIBLE flag is set.
|
|
*/
|
|
if (TestWF(pwndChild, WFVISIBLE)) {
|
|
SetVisible(pwndChild, SV_UNSET);
|
|
}
|
|
|
|
UnlinkWindow(pwndChild, pwnd);
|
|
|
|
/*
|
|
* 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,
|
|
(WPARAM)HWq(pwndChild), 0);
|
|
ThreadUnlock(&tlpwndChild);
|
|
} else {
|
|
/*
|
|
* FreeWindow performs a ThreadUnlock.
|
|
*/
|
|
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)
|
|
{
|
|
PDCE *ppdce;
|
|
PDCE pdce;
|
|
UINT uDCERelease;
|
|
PMENU pmenu;
|
|
PQMSG pqmsg;
|
|
PPCLS ppcls;
|
|
WORD fnid;
|
|
TL tlpdesk;
|
|
PWINDOWSTATION pwinsta = _GetProcessWindowStation(NULL);
|
|
PTHREADINFO pti = PtiCurrent();
|
|
PPROCESSINFO ppi;
|
|
PMONITOR pMonitor;
|
|
TL tlpMonitor;
|
|
|
|
UNREFERENCED_PARAMETER(ptlpwndFree);
|
|
|
|
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);
|
|
}
|
|
|
|
if (TestWF(pwnd, WFMSGBOX)) {
|
|
pwnd->head.rpdesk->pDeskInfo->cntMBox--;
|
|
ClrWF(pwnd, WFMSGBOX);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
|
|
pMonitor = _MonitorFromWindow(pwnd, MONITOR_DEFAULTTOPRIMARY);
|
|
ThreadLockAlwaysWithPti(pti, pMonitor, &tlpMonitor);
|
|
xxxRemoveFullScreen(pwnd, pMonitor);
|
|
ThreadUnlock(&tlpMonitor);
|
|
|
|
/*
|
|
* 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)) {
|
|
|
|
CallClientWorkerProc(pwnd,
|
|
WM_FINALDESTROY,
|
|
0,
|
|
0,
|
|
(PROC)FNID_TO_CLIENT_PFNWORKER(fnid));
|
|
}
|
|
|
|
pwnd->fnid |= FNID_CLEANEDUP_BIT;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (IS_IME_ENABLED() && pwnd == pti->spwndDefaultIme)
|
|
Unlock(&pti->spwndDefaultIme);
|
|
|
|
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 == pwnd->head.rpdesk->spwndTrack) {
|
|
/*
|
|
* Remove tooltip, if any
|
|
*/
|
|
if (GETPDESK(pwnd)->dwDTFlags & DF_TOOLTIPSHOWING) {
|
|
PWND pwndTooltip = GETPDESK(pwnd)->spwndTooltip;
|
|
TL tlpwndTooltip;
|
|
|
|
ThreadLockAlways(pwndTooltip, &tlpwndTooltip);
|
|
xxxResetTooltip((PTOOLTIPWND)pwndTooltip);
|
|
ThreadUnlock(&tlpwndTooltip);
|
|
}
|
|
|
|
Unlock(&pwnd->head.rpdesk->spwndTrack);
|
|
pwnd->head.rpdesk->dwDTFlags &= ~DF_MOUSEMOVETRK;
|
|
}
|
|
}
|
|
|
|
if (pwnd == pti->pq->spwndCapture)
|
|
xxxReleaseCapture();
|
|
|
|
if (FAnyShadows()) {
|
|
if (TestCF(pwnd, CFDROPSHADOW)) {
|
|
xxxRemoveShadow(pwnd);
|
|
} else if (pwnd->pcls->atomClassName == gatomShadow) {
|
|
CleanupShadow(pwnd);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
|
|
DestroyWindowsTimers(pwnd);
|
|
DestroyWindowsHotKeys(pwnd);
|
|
|
|
/*
|
|
* Make sure this window has no pending sent messages.
|
|
*/
|
|
ClearSendMessages(pwnd);
|
|
|
|
/*
|
|
* Remove the associated GDI sprite.
|
|
*/
|
|
if (TestWF(pwnd, WEFLAYERED)) {
|
|
UnsetLayeredWindow(pwnd);
|
|
}
|
|
|
|
if (TestWF(pwnd, WEFCOMPOSITED)) {
|
|
UnsetRedirectedWindow(pwnd, REDIRECT_COMPOSITED);
|
|
}
|
|
|
|
#ifdef REDIRECTION
|
|
if (TestWF(pwnd, WEFEXTREDIRECTED)) {
|
|
UnsetRedirectedWindow(pwnd, REDIRECT_EXTREDIRECTED);
|
|
}
|
|
#endif // REDIRECTION
|
|
|
|
/*
|
|
* Blow away any update region lying around.
|
|
*/
|
|
if (NEEDSPAINT(pwnd)) {
|
|
|
|
DecPaintCount(pwnd);
|
|
|
|
DeleteMaybeSpecialRgn(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) {
|
|
|
|
/*
|
|
* try to get rid of WM_DDE_ACK too.
|
|
*/
|
|
if ((pqmsg = FindQMsg(pti,
|
|
&(pti->mlPost),
|
|
pwnd,
|
|
WM_QUIT,
|
|
WM_QUIT, TRUE)) != NULL) {
|
|
|
|
_PostQuitMessage((int)pqmsg->msg.wParam);
|
|
}
|
|
}
|
|
|
|
if (!TestwndChild(pwnd) && pwnd->spmenu != NULL) {
|
|
pmenu = (PMENU)pwnd->spmenu;
|
|
if (UnlockWndMenu(pwnd, &pwnd->spmenu))
|
|
_DestroyMenu(pmenu);
|
|
}
|
|
|
|
if (pwnd->spmenuSys != NULL) {
|
|
pmenu = (PMENU)pwnd->spmenuSys;
|
|
if (pmenu != pwnd->head.rpdesk->spmenuDialogSys) {
|
|
if (UnlockWndMenu(pwnd, &pwnd->spmenuSys)) {
|
|
_DestroyMenu(pmenu);
|
|
}
|
|
} else {
|
|
UnlockWndMenu(pwnd, &pwnd->spmenuSys);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Tell Gdi that the window is going away.
|
|
*/
|
|
if (gcountPWO != 0) {
|
|
PVOID pwo = InternalRemoveProp(pwnd, PROP_WNDOBJ, TRUE);
|
|
if (pwo != NULL) {
|
|
GreLockDisplay(gpDispInfo->hDev);
|
|
GreDeleteWnd(pwo);
|
|
gcountPWO--;
|
|
GreUnlockDisplay(gpDispInfo->hDev);
|
|
}
|
|
}
|
|
|
|
#ifdef HUNGAPP_GHOSTING
|
|
|
|
/*
|
|
* RemoveGhost handles the case when pwnd is the hung window that has a
|
|
* corresponding ghost window and the case when pwnd is the ghost itself.
|
|
*/
|
|
RemoveGhost(pwnd);
|
|
|
|
#endif // HUNGAPP_GHOSTING
|
|
|
|
/*
|
|
* 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->DCX_flags & DCX_INVALID) {
|
|
goto NextEntry;
|
|
}
|
|
|
|
if ((pdce->pwndOrg == pwnd) || (pdce->pwndClip == pwnd)) {
|
|
|
|
if (!(pdce->DCX_flags & DCX_CACHE)) {
|
|
|
|
if (TestCF(pwnd, CFCLASSDC)) {
|
|
|
|
GreLockDisplay(gpDispInfo->hDev);
|
|
|
|
if (pdce->DCX_flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN))
|
|
DeleteHrgnClip(pdce);
|
|
|
|
MarkDCEInvalid(pdce);
|
|
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, SVR_DELETEOLD);
|
|
GreUnlockDisplay(gpDispInfo->hDev);
|
|
|
|
} 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->DCX_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->DCX_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.
|
|
*/
|
|
MarkDCEInvalid(pdce);
|
|
pdce->hrgnClip = NULL;
|
|
}
|
|
|
|
/*
|
|
* We shouldn't reference this window anymore. Setting
|
|
* these to NULL here will make sure that even if we were
|
|
* not able to release the DC here, we won't return this
|
|
* window from one of the DC matching functions.
|
|
*/
|
|
pdce->pwndOrg = NULL;
|
|
pdce->pwndClip = 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->hDev);
|
|
GreSelectVisRgn(pdce->hdc, NULL, SVR_DELETEOLD);
|
|
GreUnlockDisplay(gpDispInfo->hDev);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Step to the next DC. If the DC was deleted, there
|
|
* is no need to calculate address of the next entry.
|
|
*/
|
|
if (pdce == *ppdce)
|
|
NextEntry:
|
|
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 the window is maximized, don't
|
|
* blow away the monitor region. If the window is the desktop, don't blow
|
|
* away the screen region.
|
|
*/
|
|
if ( pwnd->hrgnClip != NULL &&
|
|
!TestWF(pwnd, WFMAXFAKEREGIONAL) &&
|
|
GETFNID(pwnd) != FNID_DESKTOP) {
|
|
|
|
GreDeleteObject(pwnd->hrgnClip);
|
|
pwnd->hrgnClip = NULL;
|
|
}
|
|
|
|
/*
|
|
* Clean up any memory allocated for scroll bars...
|
|
*/
|
|
if (pwnd->pSBInfo) {
|
|
DesktopFree(pwnd->head.rpdesk, (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, 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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!)
|
|
*/
|
|
if (HMMarkObjectDestroy(pwnd)) {
|
|
|
|
/*
|
|
* Delete the window's property list. Wait until now in case some
|
|
* thread keeps a property pointer around across a callback.
|
|
*/
|
|
if (pwnd->ppropList != NULL) {
|
|
DeleteProperties(pwnd);
|
|
}
|
|
|
|
#if DBG
|
|
/*
|
|
* 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_WARNING, "xxxFreeWindow: Window should not be visible (pwnd == %#p)", 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, LDLT_FN_FREEWINDOW);
|
|
HMFreeObject(pwnd);
|
|
ThreadUnlockDesktop(pti, &tlpdesk, LDUT_FN_FREEWINDOW);
|
|
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.
|
|
*/
|
|
pwnd->lpfnWndProc = xxxDefWindowProc;
|
|
if (pwnd->head.rpdesk)
|
|
ppi = pwnd->head.rpdesk->rpwinstaParent->pTerm->ptiDesktop->ppi;
|
|
else
|
|
ppi = PpiCurrent();
|
|
ppcls = GetClassPtr(gpsi->atomSysClass[ICLS_ICONTITLE], ppi, hModuleWin);
|
|
|
|
UserAssert(ppcls);
|
|
pwnd->pcls = *ppcls;
|
|
|
|
/*
|
|
* Since pwnd is marked as destroyed, there should be no client-side
|
|
* code which can validate it. So we do not need to search for a clone
|
|
* class of the right desktop -- just use the base class and bump the
|
|
* WndReferenceCount. This also helps if we are in a low-memory situation
|
|
* and cannot alloc another clone.
|
|
*/
|
|
|
|
pwnd->pcls->cWndReferenceCount++;
|
|
|
|
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 pwnd,
|
|
PWND pwndParent)
|
|
{
|
|
if (pwndParent->spwndChild == pwnd) {
|
|
UserAssert(pwnd->spwndPrev == NULL);
|
|
Lock(&pwndParent->spwndChild, pwnd->spwndNext);
|
|
} else if (pwnd->spwndPrev != NULL) {
|
|
Lock(&pwnd->spwndPrev->spwndNext, pwnd->spwndNext);
|
|
}
|
|
|
|
if (pwnd->spwndNext != NULL) {
|
|
Lock(&pwnd->spwndNext->spwndPrev, pwnd->spwndPrev);
|
|
Unlock(&pwnd->spwndNext);
|
|
}
|
|
|
|
Unlock(&pwnd->spwndPrev);
|
|
|
|
#if DBG
|
|
VerifyWindowLink(pwnd, pwndParent, FALSE);
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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->DCX_flags & DCX_CACHE)
|
|
DestroyCacheDC(ppdce, pdce->hdc);
|
|
}
|
|
|
|
/*
|
|
* Step to the next DC. If the DC was deleted, there's 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 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);
|
|
}
|
|
}
|