reactos/win32ss/user/ntuser/window.c
Timo Kreuzer 4661bc006d [WIN32K] Prevent dereferencing NULL pointer
Initialize a window's ThreadListEntry as an empty list on creation and only remove the window from the list on destruction, when the entry is not an empty list. Previously the window creation could fail before the list entry was initialized and the window would get destroyed after that, resulting in a NULL pointer dereference.
2023-09-21 23:45:31 +03:00

4730 lines
126 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Win32k subsystem
* PURPOSE: Windows
* FILE: win32ss/user/ntuser/window.c
* PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
*/
#include <win32k.h>
#include <ddk/immdev.h>
DBG_DEFAULT_CHANNEL(UserWnd);
INT gNestedWindowLimit = 50;
PWINDOWLIST gpwlList = NULL;
PWINDOWLIST gpwlCache = NULL;
/* HELPER FUNCTIONS ***********************************************************/
PVOID FASTCALL
IntReAllocatePoolWithTag(
POOL_TYPE PoolType,
PVOID pOld,
SIZE_T cbOld,
SIZE_T cbNew,
ULONG Tag)
{
PVOID pNew = ExAllocatePoolWithTag(PoolType, cbNew, Tag);
if (!pNew)
return NULL;
RtlCopyMemory(pNew, pOld, min(cbOld, cbNew));
ExFreePoolWithTag(pOld, Tag);
return pNew;
}
BOOL FASTCALL UserUpdateUiState(PWND Wnd, WPARAM wParam)
{
WORD Action = LOWORD(wParam);
WORD Flags = HIWORD(wParam);
if (Flags & ~(UISF_HIDEFOCUS | UISF_HIDEACCEL | UISF_ACTIVE))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
switch (Action)
{
case UIS_INITIALIZE:
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
case UIS_SET:
if (Flags & UISF_HIDEFOCUS)
Wnd->HideFocus = TRUE;
if (Flags & UISF_HIDEACCEL)
Wnd->HideAccel = TRUE;
break;
case UIS_CLEAR:
if (Flags & UISF_HIDEFOCUS)
Wnd->HideFocus = FALSE;
if (Flags & UISF_HIDEACCEL)
Wnd->HideAccel = FALSE;
break;
}
return TRUE;
}
PWND FASTCALL IntGetWindowObject(HWND hWnd)
{
PWND Window;
if (!hWnd) return NULL;
Window = UserGetWindowObject(hWnd);
if (Window)
Window->head.cLockObj++;
return Window;
}
PWND FASTCALL VerifyWnd(PWND pWnd)
{
ULONG Error;
if (!pWnd ||
(pWnd->state & WNDS_DESTROYED) ||
(pWnd->state2 & WNDS2_INDESTROY))
{
return NULL;
}
Error = EngGetLastError();
if (UserObjectInDestroy(UserHMGetHandle(pWnd)))
pWnd = NULL;
EngSetLastError(Error);
return pWnd;
}
PWND FASTCALL ValidateHwndNoErr(HWND hWnd)
{
PWND Window;
if (!hWnd)
return NULL;
Window = (PWND)UserGetObjectNoErr(gHandleTable, hWnd, TYPE_WINDOW);
if (!Window || (Window->state & WNDS_DESTROYED))
return NULL;
return Window;
}
/* Temp HACK */
// Win: ValidateHwnd
PWND FASTCALL UserGetWindowObject(HWND hWnd)
{
PWND Window;
if (!hWnd)
{
EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
return NULL;
}
Window = (PWND)UserGetObject(gHandleTable, hWnd, TYPE_WINDOW);
if (!Window || 0 != (Window->state & WNDS_DESTROYED))
{
EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
return NULL;
}
return Window;
}
ULONG FASTCALL
IntSetStyle( PWND pwnd, ULONG set_bits, ULONG clear_bits )
{
ULONG styleOld, styleNew;
styleOld = pwnd->style;
styleNew = (pwnd->style | set_bits) & ~clear_bits;
if (styleNew == styleOld) return styleNew;
pwnd->style = styleNew;
if ((styleOld ^ styleNew) & WS_VISIBLE) // State Change.
{
if (styleOld & WS_VISIBLE) pwnd->head.pti->cVisWindows--;
if (styleNew & WS_VISIBLE) pwnd->head.pti->cVisWindows++;
DceResetActiveDCEs( pwnd );
}
return styleOld;
}
/*
* IntIsWindow
*
* The function determines whether the specified window handle identifies
* an existing window.
*
* Parameters
* hWnd
* Handle to the window to test.
*
* Return Value
* If the window handle identifies an existing window, the return value
* is TRUE. If the window handle does not identify an existing window,
* the return value is FALSE.
*/
BOOL FASTCALL
IntIsWindow(HWND hWnd)
{
PWND Window;
if (!(Window = UserGetWindowObject(hWnd)))
{
return FALSE;
}
return TRUE;
}
BOOL FASTCALL
IntIsWindowVisible(PWND Wnd)
{
PWND Temp = Wnd;
for (;;)
{
if (!Temp) return TRUE;
if (!(Temp->style & WS_VISIBLE)) break;
if (Temp->style & WS_MINIMIZE && Temp != Wnd) break;
if (Temp->fnid == FNID_DESKTOP) return TRUE;
Temp = Temp->spwndParent;
}
return FALSE;
}
PWND FASTCALL
IntGetParent(PWND Wnd)
{
if (Wnd->style & WS_POPUP)
{
return Wnd->spwndOwner;
}
else if (Wnd->style & WS_CHILD)
{
return Wnd->spwndParent;
}
return NULL;
}
BOOL
FASTCALL
IntEnableWindow( HWND hWnd, BOOL bEnable )
{
BOOL Update;
PWND pWnd;
UINT bIsDisabled;
if(!(pWnd = UserGetWindowObject(hWnd)))
{
return FALSE;
}
/* check if updating is needed */
bIsDisabled = !!(pWnd->style & WS_DISABLED);
Update = bIsDisabled;
if (bEnable)
{
IntSetStyle( pWnd, 0, WS_DISABLED );
}
else
{
Update = !bIsDisabled;
co_IntSendMessage( hWnd, WM_CANCELMODE, 0, 0);
/* Remove keyboard focus from that window if it had focus */
if (hWnd == IntGetThreadFocusWindow())
{
TRACE("IntEnableWindow SF NULL\n");
co_UserSetFocus(NULL);
}
IntSetStyle( pWnd, WS_DISABLED, 0 );
}
if (Update)
{
IntNotifyWinEvent(EVENT_OBJECT_STATECHANGE, pWnd, OBJID_WINDOW, CHILDID_SELF, 0);
co_IntSendMessage(hWnd, WM_ENABLE, (LPARAM)bEnable, 0);
}
// Return nonzero if it was disabled, or zero if it wasn't:
return bIsDisabled;
}
/*
* IntWinListChildren
*
* Compile a list of all child window handles from given window.
*
* Remarks
* This function is similar to Wine WIN_ListChildren. The caller
* must free the returned list with ExFreePool.
*/
HWND* FASTCALL
IntWinListChildren(PWND Window)
{
PWND Child;
HWND *List;
UINT Index, NumChildren = 0;
if (!Window) return NULL;
for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
{
++NumChildren;
}
List = ExAllocatePoolWithTag(PagedPool, (NumChildren + 1) * sizeof(HWND), USERTAG_WINDOWLIST);
if(!List)
{
ERR("Failed to allocate memory for children array\n");
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
Index = 0;
for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
{
List[Index++] = Child->head.h;
}
List[Index] = NULL;
return List;
}
static BOOL
IntWndIsDefaultIme(_In_ PWND Window)
{
PTHREADINFO pti = Window->head.pti;
return (IS_IMM_MODE() && !(pti->TIF_flags & TIF_INCLEANUP) &&
Window == pti->spwndDefaultIme);
}
HWND* FASTCALL
IntWinListOwnedPopups(PWND Window)
{
PWND Child, Desktop;
HWND *List;
UINT Index, NumOwned = 0;
Desktop = co_GetDesktopWindow(Window);
if (!Desktop)
return NULL;
for (Child = Desktop->spwndChild; Child; Child = Child->spwndNext)
{
if (Child->spwndOwner == Window && !IntWndIsDefaultIme(Child))
++NumOwned;
}
List = ExAllocatePoolWithTag(PagedPool, (NumOwned + 1) * sizeof(HWND), USERTAG_WINDOWLIST);
if (!List)
{
ERR("Failed to allocate memory for children array\n");
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
Index = 0;
for (Child = Desktop->spwndChild; Child; Child = Child->spwndNext)
{
if (Child->spwndOwner == Window && !IntWndIsDefaultIme(Child))
List[Index++] = Child->head.h;
}
List[Index] = NULL;
return List;
}
PWND FASTCALL
IntGetNonChildAncestor(PWND pWnd)
{
while(pWnd && (pWnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
pWnd = pWnd->spwndParent;
return pWnd;
}
BOOL FASTCALL
IntIsTopLevelWindow(PWND pWnd)
{
if ( pWnd->spwndParent &&
pWnd->spwndParent == co_GetDesktopWindow(pWnd) ) return TRUE;
return FALSE;
}
BOOL FASTCALL
IntValidateOwnerDepth(PWND Wnd, PWND Owner)
{
INT Depth = 1;
for (;;)
{
if ( !Owner ) return gNestedWindowLimit >= Depth;
if (Owner == Wnd) break;
Owner = Owner->spwndOwner;
Depth++;
}
return FALSE;
}
HWND FASTCALL
IntGetWindow(HWND hWnd,
UINT uCmd)
{
PWND Wnd, FoundWnd;
HWND Ret = NULL;
Wnd = ValidateHwndNoErr(hWnd);
if (!Wnd)
return NULL;
FoundWnd = NULL;
switch (uCmd)
{
case GW_OWNER:
if (Wnd->spwndOwner != NULL)
FoundWnd = Wnd->spwndOwner;
break;
case GW_HWNDFIRST:
if(Wnd->spwndParent != NULL)
{
FoundWnd = Wnd->spwndParent;
if (FoundWnd->spwndChild != NULL)
FoundWnd = FoundWnd->spwndChild;
}
break;
case GW_HWNDNEXT:
if (Wnd->spwndNext != NULL)
FoundWnd = Wnd->spwndNext;
break;
case GW_HWNDPREV:
if (Wnd->spwndPrev != NULL)
FoundWnd = Wnd->spwndPrev;
break;
case GW_CHILD:
if (Wnd->spwndChild != NULL)
FoundWnd = Wnd->spwndChild;
break;
case GW_HWNDLAST:
FoundWnd = Wnd;
while ( FoundWnd->spwndNext != NULL)
FoundWnd = FoundWnd->spwndNext;
break;
default:
Wnd = NULL;
break;
}
if (FoundWnd != NULL)
Ret = UserHMGetHandle(FoundWnd);
return Ret;
}
DWORD FASTCALL IntGetWindowContextHelpId( PWND pWnd )
{
DWORD HelpId;
do
{
HelpId = (DWORD)(DWORD_PTR)UserGetProp(pWnd, gpsi->atomContextHelpIdProp, TRUE);
if (!HelpId) break;
pWnd = IntGetParent(pWnd);
}
while (pWnd && pWnd->fnid != FNID_DESKTOP);
return HelpId;
}
VOID
FASTCALL
IntRemoveTrackMouseEvent(
PDESKTOP pDesk);
/***********************************************************************
* IntSendDestroyMsg
*/
static void IntSendDestroyMsg(HWND hWnd)
{
PTHREADINFO ti;
PWND Window;
ti = PsGetCurrentThreadWin32Thread();
Window = UserGetWindowObject(hWnd);
if (Window)
{
/*
* Look whether the focus is within the tree of windows
* we will be destroying.
*/
// Rule #1
if ( ti->MessageQueue->spwndActive == Window || // Fixes CORE-106 RegSvr32 exit and return focus to CMD.
(ti->MessageQueue->spwndActive == NULL && ti->MessageQueue == IntGetFocusMessageQueue()) )
{
co_WinPosActivateOtherWindow(Window);
}
/* Fixes CMD properties closing and returning focus to CMD */
if (ti->MessageQueue->spwndFocus == Window)
{
if ((Window->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
{
co_UserSetFocus(Window->spwndParent);
}
else
{
co_UserSetFocus(NULL);
}
}
if (ti->MessageQueue->CaretInfo.hWnd == UserHMGetHandle(Window))
{
co_IntDestroyCaret(ti);
}
/* If the window being destroyed is currently tracked... */
if (ti->rpdesk && ti->rpdesk->spwndTrack == Window)
{
IntRemoveTrackMouseEvent(ti->rpdesk);
}
}
/* If the window being destroyed is the current clipboard owner... */
if (ti->ppi->prpwinsta != NULL && Window == ti->ppi->prpwinsta->spwndClipOwner)
{
/* ... make it release the clipboard */
UserClipboardRelease(Window);
}
/* Send the WM_DESTROY to the window */
co_IntSendMessage(hWnd, WM_DESTROY, 0, 0);
/*
* This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
* make sure that the window still exists when we come back.
*/
if (IntIsWindow(hWnd))
{
HWND* pWndArray;
int i;
if (!(pWndArray = IntWinListChildren( Window ))) return;
for (i = 0; pWndArray[i]; i++)
{
if (IntIsWindow( pWndArray[i] )) IntSendDestroyMsg( pWndArray[i] );
}
ExFreePoolWithTag(pWndArray, USERTAG_WINDOWLIST);
}
else
{
TRACE("destroyed itself while in WM_DESTROY!\n");
}
}
static VOID
UserFreeWindowInfo(PTHREADINFO ti, PWND Wnd)
{
PCLIENTINFO ClientInfo = GetWin32ClientInfo();
if (!Wnd) return;
if (ClientInfo->CallbackWnd.pWnd == DesktopHeapAddressToUser(Wnd))
{
ClientInfo->CallbackWnd.hWnd = NULL;
ClientInfo->CallbackWnd.pWnd = NULL;
}
if (Wnd->strName.Buffer != NULL)
{
Wnd->strName.Length = 0;
Wnd->strName.MaximumLength = 0;
DesktopHeapFree(Wnd->head.rpdesk,
Wnd->strName.Buffer);
Wnd->strName.Buffer = NULL;
}
// DesktopHeapFree(Wnd->head.rpdesk, Wnd);
// WindowObject->hWnd = NULL;
}
/***********************************************************************
* co_UserFreeWindow
*
* Destroy storage associated to a window. "Internals" p.358
*
* This is the "functional" DestroyWindows function i.e. all stuff
* done in CreateWindow is undone here and not in DestroyWindow :-P
*/
LRESULT co_UserFreeWindow(PWND Window,
PPROCESSINFO ProcessData,
PTHREADINFO ThreadData,
BOOLEAN SendMessages)
{
HWND *Children;
HWND *ChildHandle;
PWND Child;
PMENU Menu;
BOOLEAN BelongsToThreadData;
ASSERT(Window);
if(Window->state2 & WNDS2_INDESTROY)
{
TRACE("Tried to call co_UserFreeWindow() twice\n");
return 0;
}
Window->state2 |= WNDS2_INDESTROY;
Window->style &= ~WS_VISIBLE;
Window->head.pti->cVisWindows--;
/* remove the window already at this point from the thread window list so we
don't get into trouble when destroying the thread windows while we're still
in co_UserFreeWindow() */
if (!IsListEmpty(&Window->ThreadListEntry))
RemoveEntryList(&Window->ThreadListEntry);
BelongsToThreadData = IntWndBelongsToThread(Window, ThreadData);
IntDeRegisterShellHookWindow(UserHMGetHandle(Window));
/* free child windows */
Children = IntWinListChildren(Window);
if (Children)
{
for (ChildHandle = Children; *ChildHandle; ++ChildHandle)
{
if ((Child = IntGetWindowObject(*ChildHandle)))
{
if (!IntWndBelongsToThread(Child, ThreadData))
{
/* send WM_DESTROY messages to windows not belonging to the same thread */
co_IntSendMessage( UserHMGetHandle(Child), WM_ASYNC_DESTROYWINDOW, 0, 0 );
}
else
co_UserFreeWindow(Child, ProcessData, ThreadData, SendMessages);
UserDereferenceObject(Child);
}
}
ExFreePoolWithTag(Children, USERTAG_WINDOWLIST);
}
if (SendMessages)
{
/*
* Clear the update region to make sure no WM_PAINT messages will be
* generated for this window while processing the WM_NCDESTROY.
*/
co_UserRedrawWindow(Window, NULL, 0,
RDW_VALIDATE | RDW_NOFRAME | RDW_NOERASE |
RDW_NOINTERNALPAINT | RDW_NOCHILDREN);
if (BelongsToThreadData)
co_IntSendMessage(UserHMGetHandle(Window), WM_NCDESTROY, 0, 0);
}
UserClipboardFreeWindow(Window);
DestroyTimersForWindow(ThreadData, Window);
/* Unregister hot keys */
UnregisterWindowHotKeys(Window);
/* flush the message queue */
MsqRemoveWindowMessagesFromQueue(Window);
/* from now on no messages can be sent to this window anymore */
Window->state |= WNDS_DESTROYED;
Window->fnid |= FNID_FREED;
/* don't remove the WINDOWSTATUS_DESTROYING bit */
/* reset shell window handles */
if (ThreadData->rpdesk)
{
if (Window->head.h == ThreadData->rpdesk->rpwinstaParent->ShellWindow)
ThreadData->rpdesk->rpwinstaParent->ShellWindow = NULL;
if (Window->head.h == ThreadData->rpdesk->rpwinstaParent->ShellListView)
ThreadData->rpdesk->rpwinstaParent->ShellListView = NULL;
}
if (ThreadData->spwndDefaultIme &&
ThreadData->spwndDefaultIme->spwndOwner == Window)
{
WndSetOwner(ThreadData->spwndDefaultIme, NULL);
}
if (IS_IMM_MODE() && Window == ThreadData->spwndDefaultIme)
{
UserAssignmentUnlock((PVOID*)&(ThreadData->spwndDefaultIme));
}
/* Fixes dialog test_focus breakage due to r66237. */
if (ThreadData->MessageQueue->spwndFocus == Window)
ThreadData->MessageQueue->spwndFocus = NULL;
if (ThreadData->MessageQueue->spwndActive == Window)
ThreadData->MessageQueue->spwndActive = NULL;
if (ThreadData->MessageQueue->spwndCapture == Window)
{
IntReleaseCapture();
}
//// Now kill those remaining "PAINTING BUG: Thread marked as containing dirty windows" spam!!!
if ( Window->hrgnUpdate != NULL || Window->state & WNDS_INTERNALPAINT )
{
MsqDecPaintCountQueue(Window->head.pti);
if (Window->hrgnUpdate > HRGN_WINDOW && GreIsHandleValid(Window->hrgnUpdate))
{
IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(Window->hrgnUpdate);
}
Window->hrgnUpdate = NULL;
Window->state &= ~WNDS_INTERNALPAINT;
}
if (Window->state & (WNDS_SENDERASEBACKGROUND|WNDS_SENDNCPAINT))
{
Window->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_SENDNCPAINT);
}
if ( ((Window->style & (WS_CHILD|WS_POPUP)) != WS_CHILD) &&
Window->IDMenu &&
(Menu = UserGetMenuObject((HMENU)Window->IDMenu)))
{
TRACE("UFW: IDMenu %p\n",Window->IDMenu);
IntDestroyMenuObject(Menu, TRUE);
Window->IDMenu = 0;
}
if (Window->SystemMenu
&& (Menu = UserGetMenuObject(Window->SystemMenu)))
{
IntDestroyMenuObject(Menu, TRUE);
Window->SystemMenu = (HMENU)0;
}
DceFreeWindowDCE(Window); /* Always do this to catch orphaned DCs */
IntUnlinkWindow(Window);
if (Window->PropListItems)
{
UserRemoveWindowProps(Window);
TRACE("Window->PropListItems %lu\n",Window->PropListItems);
ASSERT(Window->PropListItems==0);
}
/* Kill any reference to linked windows. Prev & Next are taken care of in IntUnlinkWindow */
WndSetOwner(Window, NULL);
WndSetParent(Window, NULL);
WndSetChild(Window, NULL);
WndSetLastActive(Window, NULL);
UserReferenceObject(Window);
UserMarkObjectDestroy(Window);
IntDestroyScrollBars(Window);
if (Window->pcls->atomClassName == gaGuiConsoleWndClass)
{
/* Count only console windows manually */
co_IntUserManualGuiCheck(FALSE);
}
/* dereference the class */
NT_ASSERT(Window->head.pti != NULL);
IntDereferenceClass(Window->pcls,
Window->head.pti->pDeskInfo,
Window->head.pti->ppi);
Window->pcls = NULL;
if (Window->hrgnClip)
{
IntGdiSetRegionOwner(Window->hrgnClip, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(Window->hrgnClip);
Window->hrgnClip = NULL;
}
Window->head.pti->cWindows--;
// ASSERT(Window != NULL);
UserFreeWindowInfo(Window->head.pti, Window);
UserDereferenceObject(Window);
UserDeleteObject(UserHMGetHandle(Window), TYPE_WINDOW);
return 0;
}
//
// Same as User32:IntGetWndProc.
//
WNDPROC FASTCALL
IntGetWindowProc(PWND pWnd,
BOOL Ansi)
{
INT i;
PCLS Class;
WNDPROC gcpd, Ret = 0;
ASSERT(UserIsEnteredExclusive());
Class = pWnd->pcls;
if (pWnd->state & WNDS_SERVERSIDEWINDOWPROC)
{
for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
{
if (GETPFNSERVER(i) == pWnd->lpfnWndProc)
{
if (Ansi)
Ret = GETPFNCLIENTA(i);
else
Ret = GETPFNCLIENTW(i);
}
}
return Ret;
}
if (Class->fnid == FNID_EDIT)
Ret = pWnd->lpfnWndProc;
else
{
Ret = pWnd->lpfnWndProc;
if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON)
{
if (Ansi)
{
if (GETPFNCLIENTW(Class->fnid) == pWnd->lpfnWndProc)
Ret = GETPFNCLIENTA(Class->fnid);
}
else
{
if (GETPFNCLIENTA(Class->fnid) == pWnd->lpfnWndProc)
Ret = GETPFNCLIENTW(Class->fnid);
}
}
if ( Ret != pWnd->lpfnWndProc)
return Ret;
}
if ( Ansi == !!(pWnd->state & WNDS_ANSIWINDOWPROC) )
return Ret;
gcpd = (WNDPROC)UserGetCPD(
pWnd,
(Ansi ? UserGetCPDA2U : UserGetCPDU2A )|UserGetCPDWindow,
(ULONG_PTR)Ret);
return (gcpd ? gcpd : Ret);
}
static WNDPROC
IntSetWindowProc(PWND pWnd,
WNDPROC NewWndProc,
BOOL Ansi)
{
INT i;
PCALLPROCDATA CallProc;
PCLS Class;
WNDPROC Ret, chWndProc = NULL;
// Retrieve previous window proc.
Ret = IntGetWindowProc(pWnd, Ansi);
Class = pWnd->pcls;
if (IsCallProcHandle(NewWndProc))
{
CallProc = UserGetObject(gHandleTable, NewWndProc, TYPE_CALLPROC);
if (CallProc)
{ // Reset new WndProc.
NewWndProc = CallProc->pfnClientPrevious;
// Reset Ansi from CallProc handle. This is expected with wine "deftest".
Ansi = !!(CallProc->wType & UserGetCPDU2A);
}
}
// Switch from Client Side call to Server Side call if match. Ref: "deftest".
for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
{
if (GETPFNCLIENTW(i) == NewWndProc)
{
chWndProc = GETPFNSERVER(i);
break;
}
if (GETPFNCLIENTA(i) == NewWndProc)
{
chWndProc = GETPFNSERVER(i);
break;
}
}
// If match, set/reset to Server Side and clear ansi.
if (chWndProc)
{
pWnd->lpfnWndProc = chWndProc;
pWnd->Unicode = TRUE;
pWnd->state &= ~WNDS_ANSIWINDOWPROC;
pWnd->state |= WNDS_SERVERSIDEWINDOWPROC;
}
else
{
pWnd->Unicode = !Ansi;
// Handle the state change in here.
if (Ansi)
pWnd->state |= WNDS_ANSIWINDOWPROC;
else
pWnd->state &= ~WNDS_ANSIWINDOWPROC;
if (pWnd->state & WNDS_SERVERSIDEWINDOWPROC)
pWnd->state &= ~WNDS_SERVERSIDEWINDOWPROC;
if (!NewWndProc) NewWndProc = pWnd->lpfnWndProc;
if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON)
{
if (Ansi)
{
if (GETPFNCLIENTW(Class->fnid) == NewWndProc)
chWndProc = GETPFNCLIENTA(Class->fnid);
}
else
{
if (GETPFNCLIENTA(Class->fnid) == NewWndProc)
chWndProc = GETPFNCLIENTW(Class->fnid);
}
}
// Now set the new window proc.
pWnd->lpfnWndProc = (chWndProc ? chWndProc : NewWndProc);
}
return Ret;
}
/* INTERNAL ******************************************************************/
////
// This fixes a check for children messages that need paint while searching the parents messages!
// Fixes wine msg:test_paint_messages:WmParentErasePaint ..
////
BOOL FASTCALL
IntIsChildWindow(PWND Parent, PWND BaseWindow)
{
PWND Window = BaseWindow;
do
{
if ( Window == NULL || (Window->style & (WS_POPUP|WS_CHILD)) != WS_CHILD )
return FALSE;
Window = Window->spwndParent;
}
while(Parent != Window);
return TRUE;
}
////
/* Link the window into siblings list. Children and parent are kept in place. */
VOID FASTCALL
IntLinkWindow(
PWND Wnd,
PWND WndInsertAfter /* Set to NULL if top sibling */
)
{
if (Wnd == WndInsertAfter)
{
ERR("Trying to link window 0x%p to itself\n", Wnd);
ASSERT(WndInsertAfter != Wnd);
return;
}
WndSetPrev(Wnd, WndInsertAfter);
if (Wnd->spwndPrev)
{
/* Link after WndInsertAfter */
ASSERT(Wnd != WndInsertAfter->spwndNext);
WndSetNext(Wnd, WndInsertAfter->spwndNext);
if (Wnd->spwndNext)
WndSetPrev(Wnd->spwndNext, Wnd);
ASSERT(Wnd != Wnd->spwndPrev);
WndSetNext(Wnd->spwndPrev, Wnd);
}
else
{
/* Link at the top */
ASSERT(Wnd != Wnd->spwndParent->spwndChild);
WndSetNext(Wnd, Wnd->spwndParent->spwndChild);
if (Wnd->spwndNext)
WndSetPrev(Wnd->spwndNext, Wnd);
WndSetChild(Wnd->spwndParent, Wnd);
}
}
/*
Note: Wnd->spwndParent can be null if it is the desktop.
*/
VOID FASTCALL IntLinkHwnd(PWND Wnd, HWND hWndPrev)
{
if (hWndPrev == HWND_NOTOPMOST)
{
if (!(Wnd->ExStyle & WS_EX_TOPMOST) && (Wnd->ExStyle2 & WS_EX2_LINKED))
return; /* nothing to do */
Wnd->ExStyle &= ~WS_EX_TOPMOST;
hWndPrev = HWND_TOP; /* fallback to the HWND_TOP case */
}
IntUnlinkWindow(Wnd); /* unlink it from the previous location */
if (hWndPrev == HWND_BOTTOM)
{
/* Link in the bottom of the list */
PWND WndInsertAfter;
WndInsertAfter = Wnd->spwndParent->spwndChild;
while (WndInsertAfter && WndInsertAfter->spwndNext)
{
WndInsertAfter = WndInsertAfter->spwndNext;
}
IntLinkWindow(Wnd, WndInsertAfter);
Wnd->ExStyle &= ~WS_EX_TOPMOST;
}
else if (hWndPrev == HWND_TOPMOST)
{
/* Link in the top of the list */
IntLinkWindow(Wnd, NULL);
Wnd->ExStyle |= WS_EX_TOPMOST;
}
else if (hWndPrev == HWND_TOP)
{
/* Link it after the last topmost window */
PWND WndInsertBefore;
WndInsertBefore = Wnd->spwndParent->spwndChild;
if (!(Wnd->ExStyle & WS_EX_TOPMOST)) /* put it above the first non-topmost window */
{
while (WndInsertBefore != NULL && WndInsertBefore->spwndNext != NULL)
{
if (!(WndInsertBefore->ExStyle & WS_EX_TOPMOST))
break;
if (WndInsertBefore == Wnd->spwndOwner) /* keep it above owner */
{
Wnd->ExStyle |= WS_EX_TOPMOST;
break;
}
WndInsertBefore = WndInsertBefore->spwndNext;
}
}
IntLinkWindow(Wnd, WndInsertBefore ? WndInsertBefore->spwndPrev : NULL);
}
else
{
/* Link it after hWndPrev */
PWND WndInsertAfter;
WndInsertAfter = UserGetWindowObject(hWndPrev);
/* Are we called with an erroneous handle */
if (WndInsertAfter == NULL)
{
/* Link in a default position */
IntLinkHwnd(Wnd, HWND_TOP);
return;
}
if (Wnd == WndInsertAfter)
{
ERR("Trying to link window 0x%p to itself\n", Wnd);
ASSERT(WndInsertAfter != Wnd);
// FIXME: IntUnlinkWindow(Wnd) was already called. Continuing as is seems wrong!
}
else
{
IntLinkWindow(Wnd, WndInsertAfter);
}
/* Fix the WS_EX_TOPMOST flag */
if (!(WndInsertAfter->ExStyle & WS_EX_TOPMOST))
{
Wnd->ExStyle &= ~WS_EX_TOPMOST;
}
else
{
if (WndInsertAfter->spwndNext &&
(WndInsertAfter->spwndNext->ExStyle & WS_EX_TOPMOST))
{
Wnd->ExStyle |= WS_EX_TOPMOST;
}
}
}
Wnd->ExStyle2 |= WS_EX2_LINKED;
}
VOID FASTCALL
IntProcessOwnerSwap(PWND Wnd, PWND WndNewOwner, PWND WndOldOwner)
{
if (WndOldOwner)
{
if (Wnd->head.pti != WndOldOwner->head.pti)
{
if (!WndNewOwner ||
Wnd->head.pti == WndNewOwner->head.pti ||
WndOldOwner->head.pti != WndNewOwner->head.pti )
{
//ERR("ProcessOwnerSwap Old out.\n");
UserAttachThreadInput(Wnd->head.pti, WndOldOwner->head.pti, FALSE);
}
}
}
if (WndNewOwner)
{
if (Wnd->head.pti != WndNewOwner->head.pti)
{
if (!WndOldOwner ||
WndOldOwner->head.pti != WndNewOwner->head.pti )
{
//ERR("ProcessOwnerSwap New in.\n");
UserAttachThreadInput(Wnd->head.pti, WndNewOwner->head.pti, TRUE);
}
}
}
// FIXME: System Tray checks.
}
static
HWND FASTCALL
IntSetOwner(HWND hWnd, HWND hWndNewOwner)
{
PWND Wnd, WndOldOwner, WndNewOwner;
HWND ret;
Wnd = IntGetWindowObject(hWnd);
if(!Wnd)
return NULL;
WndOldOwner = Wnd->spwndOwner;
ret = WndOldOwner ? UserHMGetHandle(WndOldOwner) : 0;
WndNewOwner = UserGetWindowObject(hWndNewOwner);
if (!WndNewOwner && hWndNewOwner)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
ret = NULL;
goto Error;
}
/* if parent belongs to a different thread and the window isn't */
/* top-level, attach the two threads */
IntProcessOwnerSwap(Wnd, WndNewOwner, WndOldOwner);
if (IntValidateOwnerDepth(Wnd, WndNewOwner))
{
WndSetOwner(Wnd, WndNewOwner);
}
else
{
IntProcessOwnerSwap(Wnd, WndOldOwner, WndNewOwner);
EngSetLastError(ERROR_INVALID_PARAMETER);
ret = NULL;
}
Error:
UserDereferenceObject(Wnd);
return ret;
}
PWND FASTCALL
co_IntSetParent(PWND Wnd, PWND WndNewParent)
{
PWND WndOldParent, pWndExam;
BOOL WasVisible;
POINT pt;
int swFlags = SWP_NOSIZE|SWP_NOZORDER;
ASSERT(Wnd);
ASSERT(WndNewParent);
ASSERT_REFS_CO(Wnd);
ASSERT_REFS_CO(WndNewParent);
if (Wnd == Wnd->head.rpdesk->spwndMessage)
{
EngSetLastError(ERROR_ACCESS_DENIED);
return( NULL);
}
/* Some applications try to set a child as a parent */
if (IntIsChildWindow(Wnd, WndNewParent))
{
TRACE("IntSetParent try to set a child as a parent.\n");
EngSetLastError( ERROR_INVALID_PARAMETER );
return NULL;
}
pWndExam = WndNewParent; // Load parent Window to examine.
// Now test for set parent to parent hit.
while (pWndExam)
{
if (Wnd == pWndExam)
{
TRACE("IntSetParent Failed Test for set parent to parent!\n");
EngSetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
pWndExam = pWndExam->spwndParent;
}
/*
* Windows hides the window first, then shows it again
* including the WM_SHOWWINDOW messages and all
*/
WasVisible = co_WinPosShowWindow(Wnd, SW_HIDE);
/* Window must belong to current process */
if (Wnd->head.pti->ppi != PsGetCurrentProcessWin32Process())
{
ERR("IntSetParent Window must belong to current process!\n");
return NULL;
}
WndOldParent = Wnd->spwndParent;
if ( WndOldParent &&
WndOldParent->ExStyle & WS_EX_LAYOUTRTL)
pt.x = Wnd->rcWindow.right;
else
pt.x = Wnd->rcWindow.left;
pt.y = Wnd->rcWindow.top;
IntScreenToClient(WndOldParent, &pt);
if (WndOldParent) UserReferenceObject(WndOldParent); /* Caller must deref */
if (WndNewParent != WndOldParent)
{
/* Unlink the window from the siblings list */
IntUnlinkWindow(Wnd);
Wnd->ExStyle2 &= ~WS_EX2_LINKED;
/* Set the new parent */
WndSetParent(Wnd, WndNewParent);
if ( Wnd->style & WS_CHILD &&
Wnd->spwndOwner &&
Wnd->spwndOwner->ExStyle & WS_EX_TOPMOST )
{
ERR("SetParent Top Most from Pop up!\n");
Wnd->ExStyle |= WS_EX_TOPMOST;
}
/* Link the window with its new siblings */
IntLinkHwnd( Wnd,
((0 == (Wnd->ExStyle & WS_EX_TOPMOST) &&
UserIsDesktopWindow(WndNewParent) ) ? HWND_TOP : HWND_TOPMOST ) );
}
if ( WndNewParent == co_GetDesktopWindow(Wnd) &&
!(Wnd->style & WS_CLIPSIBLINGS) )
{
Wnd->style |= WS_CLIPSIBLINGS;
DceResetActiveDCEs(Wnd);
}
/* if parent belongs to a different thread and the window isn't */
/* top-level, attach the two threads */
if ((Wnd->style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
{
if ( Wnd->spwndParent != co_GetDesktopWindow(Wnd))
{
if (WndOldParent && (Wnd->head.pti != WndOldParent->head.pti))
{
//ERR("SetParent Old out.\n");
UserAttachThreadInput(Wnd->head.pti, WndOldParent->head.pti, FALSE);
}
}
if ( WndNewParent != co_GetDesktopWindow(Wnd))
{
if (Wnd->head.pti != WndNewParent->head.pti)
{
//ERR("SetParent New in.\n");
UserAttachThreadInput(Wnd->head.pti, WndNewParent->head.pti, TRUE);
}
}
}
if (UserIsMessageWindow(WndOldParent) || UserIsMessageWindow(WndNewParent))
swFlags |= SWP_NOACTIVATE;
IntNotifyWinEvent(EVENT_OBJECT_PARENTCHANGE, Wnd ,OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI);
/*
* SetParent additionally needs to make hwnd the top window
* in the z-order and send the expected WM_WINDOWPOSCHANGING and
* WM_WINDOWPOSCHANGED notification messages.
*/
//ERR("IntSetParent SetWindowPos 1\n");
co_WinPosSetWindowPos( Wnd,
(0 == (Wnd->ExStyle & WS_EX_TOPMOST) ? HWND_TOP : HWND_TOPMOST),
pt.x, pt.y, 0, 0, swFlags);
//ERR("IntSetParent SetWindowPos 2 X %d Y %d\n",pt.x, pt.y);
if (WasVisible) co_WinPosShowWindow(Wnd, SW_SHOWNORMAL);
return WndOldParent;
}
// Win: xxxSetParent
HWND FASTCALL
co_UserSetParent(HWND hWndChild, HWND hWndNewParent)
{
PWND Wnd = NULL, WndParent = NULL, WndOldParent;
HWND hWndOldParent = NULL;
USER_REFERENCE_ENTRY Ref, ParentRef;
if (IntIsBroadcastHwnd(hWndChild) || IntIsBroadcastHwnd(hWndNewParent))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return( NULL);
}
if (hWndChild == IntGetDesktopWindow())
{
ERR("UserSetParent Access Denied!\n");
EngSetLastError(ERROR_ACCESS_DENIED);
return( NULL);
}
if (hWndNewParent)
{
if (!(WndParent = UserGetWindowObject(hWndNewParent)))
{
ERR("UserSetParent Bad New Parent!\n");
return( NULL);
}
}
else
{
if (!(WndParent = UserGetWindowObject(IntGetDesktopWindow())))
{
return( NULL);
}
}
if (!(Wnd = UserGetWindowObject(hWndChild)))
{
ERR("UserSetParent Bad Child!\n");
return( NULL);
}
UserRefObjectCo(Wnd, &Ref);
UserRefObjectCo(WndParent, &ParentRef);
//ERR("Enter co_IntSetParent\n");
WndOldParent = co_IntSetParent(Wnd, WndParent);
//ERR("Leave co_IntSetParent\n");
UserDerefObjectCo(WndParent);
UserDerefObjectCo(Wnd);
if (WndOldParent)
{
hWndOldParent = WndOldParent->head.h;
UserDereferenceObject(WndOldParent);
}
return( hWndOldParent);
}
/* Unlink the window from siblings. Children and parent are kept in place. */
VOID FASTCALL
IntUnlinkWindow(PWND Wnd)
{
ASSERT(Wnd != Wnd->spwndNext);
ASSERT(Wnd != Wnd->spwndPrev);
if (Wnd->spwndNext)
WndSetPrev(Wnd->spwndNext, Wnd->spwndPrev);
if (Wnd->spwndPrev)
WndSetNext(Wnd->spwndPrev, Wnd->spwndNext);
if (Wnd->spwndParent && Wnd->spwndParent->spwndChild == Wnd)
WndSetChild(Wnd->spwndParent, Wnd->spwndNext);
WndSetPrev(Wnd, NULL);
WndSetNext(Wnd, NULL);
}
// Win: ExpandWindowList
BOOL FASTCALL IntGrowHwndList(PWINDOWLIST *ppwl)
{
PWINDOWLIST pwlOld, pwlNew;
SIZE_T ibOld, ibNew;
#define GROW_COUNT 8
pwlOld = *ppwl;
ibOld = (LPBYTE)pwlOld->phwndLast - (LPBYTE)pwlOld;
ibNew = ibOld + GROW_COUNT * sizeof(HWND);
#undef GROW_COUNT
pwlNew = IntReAllocatePoolWithTag(PagedPool, pwlOld, ibOld, ibNew, USERTAG_WINDOWLIST);
if (!pwlNew)
return FALSE;
pwlNew->phwndLast = (HWND *)((LPBYTE)pwlNew + ibOld);
pwlNew->phwndEnd = (HWND *)((LPBYTE)pwlNew + ibNew);
*ppwl = pwlNew;
return TRUE;
}
// Win: InternalBuildHwndList
PWINDOWLIST FASTCALL IntPopulateHwndList(PWINDOWLIST pwl, PWND pwnd, DWORD dwFlags)
{
ASSERT(!WL_IS_BAD(pwl));
for (; pwnd; pwnd = pwnd->spwndNext)
{
if (!pwl->pti || pwl->pti == pwnd->head.pti)
{
*(pwl->phwndLast) = UserHMGetHandle(pwnd);
++(pwl->phwndLast);
if (pwl->phwndLast == pwl->phwndEnd && !IntGrowHwndList(&pwl))
break;
}
if ((dwFlags & IACE_CHILDREN) && pwnd->spwndChild)
{
pwl = IntPopulateHwndList(pwl, pwnd->spwndChild, IACE_CHILDREN | IACE_LIST);
if (WL_IS_BAD(pwl))
break;
}
if (!(dwFlags & IACE_LIST))
break;
}
return pwl;
}
// Win: BuildHwndList
PWINDOWLIST FASTCALL IntBuildHwndList(PWND pwnd, DWORD dwFlags, PTHREADINFO pti)
{
PWINDOWLIST pwl;
DWORD cbWL;
if (gpwlCache)
{
pwl = gpwlCache;
gpwlCache = NULL;
}
else
{
#define INITIAL_COUNT 32
cbWL = sizeof(WINDOWLIST) + (INITIAL_COUNT - 1) * sizeof(HWND);
pwl = ExAllocatePoolWithTag(PagedPool, cbWL, USERTAG_WINDOWLIST);
if (!pwl)
return NULL;
pwl->phwndEnd = &pwl->ahwnd[INITIAL_COUNT];
#undef INITIAL_COUNT
}
pwl->pti = pti;
pwl->phwndLast = pwl->ahwnd;
pwl = IntPopulateHwndList(pwl, pwnd, dwFlags);
if (WL_IS_BAD(pwl))
{
ExFreePoolWithTag(pwl, USERTAG_WINDOWLIST);
return NULL;
}
*(pwl->phwndLast) = HWND_TERMINATOR;
if (dwFlags & 0x8)
{
// TODO:
}
pwl->pti = GetW32ThreadInfo();
pwl->pNextList = gpwlList;
gpwlList = pwl;
return pwl;
}
// Win: FreeHwndList
VOID FASTCALL IntFreeHwndList(PWINDOWLIST pwlTarget)
{
PWINDOWLIST pwl, *ppwl;
for (ppwl = &gpwlList; *ppwl; ppwl = &(*ppwl)->pNextList)
{
if (*ppwl != pwlTarget)
continue;
*ppwl = pwlTarget->pNextList;
if (gpwlCache)
{
if (WL_CAPACITY(pwlTarget) > WL_CAPACITY(gpwlCache))
{
pwl = gpwlCache;
gpwlCache = pwlTarget;
ExFreePoolWithTag(pwl, USERTAG_WINDOWLIST);
}
else
{
ExFreePoolWithTag(pwlTarget, USERTAG_WINDOWLIST);
}
}
else
{
gpwlCache = pwlTarget;
}
break;
}
}
/* FUNCTIONS *****************************************************************/
/*
* As best as I can figure, this function is used by EnumWindows,
* EnumChildWindows, EnumDesktopWindows, & EnumThreadWindows.
*
* It's supposed to build a list of HWNDs to return to the caller.
* We can figure out what kind of list by what parameters are
* passed to us.
*/
/*
* @implemented
*/
NTSTATUS
NTAPI
NtUserBuildHwndList(
HDESK hDesktop,
HWND hwndParent,
BOOLEAN bChildren,
ULONG dwThreadId,
ULONG cHwnd,
HWND* phwndList,
ULONG* pcHwndNeeded)
{
NTSTATUS Status;
ULONG dwCount = 0;
if (pcHwndNeeded == NULL)
return STATUS_INVALID_PARAMETER;
UserEnterExclusive();
if (hwndParent || !dwThreadId)
{
PDESKTOP Desktop;
PWND Parent, Window;
if(!hwndParent)
{
if(hDesktop == NULL && !(Desktop = IntGetActiveDesktop()))
{
Status = STATUS_INVALID_HANDLE;
goto Quit;
}
if(hDesktop)
{
Status = IntValidateDesktopHandle(hDesktop,
UserMode,
0,
&Desktop);
if(!NT_SUCCESS(Status))
{
Status = STATUS_INVALID_HANDLE;
goto Quit;
}
}
hwndParent = Desktop->DesktopWindow;
}
else
{
hDesktop = 0;
}
if((Parent = UserGetWindowObject(hwndParent)) &&
(Window = Parent->spwndChild))
{
BOOL bGoDown = TRUE;
Status = STATUS_SUCCESS;
while(TRUE)
{
if (bGoDown)
{
if (dwCount++ < cHwnd && phwndList)
{
_SEH2_TRY
{
ProbeForWrite(phwndList, sizeof(HWND), 1);
*phwndList = Window->head.h;
phwndList++;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if(!NT_SUCCESS(Status))
{
break;
}
}
if (Window->spwndChild && bChildren)
{
Window = Window->spwndChild;
continue;
}
bGoDown = FALSE;
}
if (Window->spwndNext)
{
Window = Window->spwndNext;
bGoDown = TRUE;
continue;
}
Window = Window->spwndParent;
if (Window == Parent)
{
break;
}
}
}
if(hDesktop)
{
ObDereferenceObject(Desktop);
}
}
else // Build EnumThreadWindows list!
{
PETHREAD Thread;
PTHREADINFO W32Thread;
PWND Window;
HWND *List = NULL;
Status = PsLookupThreadByThreadId(UlongToHandle(dwThreadId), &Thread);
if (!NT_SUCCESS(Status))
{
ERR("Thread Id is not valid!\n");
Status = STATUS_INVALID_PARAMETER;
goto Quit;
}
if (!(W32Thread = (PTHREADINFO)Thread->Tcb.Win32Thread))
{
ObDereferenceObject(Thread);
TRACE("Tried to enumerate windows of a non gui thread\n");
Status = STATUS_INVALID_PARAMETER;
goto Quit;
}
// Do not use Thread link list due to co_UserFreeWindow!!!
// Current = W32Thread->WindowListHead.Flink;
// Fixes Api:CreateWindowEx tests!!!
List = IntWinListChildren(UserGetDesktopWindow());
if (List)
{
int i;
for (i = 0; List[i]; i++)
{
Window = ValidateHwndNoErr(List[i]);
if (Window && Window->head.pti == W32Thread)
{
if (dwCount < cHwnd && phwndList)
{
_SEH2_TRY
{
ProbeForWrite(phwndList, sizeof(HWND), 1);
*phwndList = Window->head.h;
phwndList++;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
ERR("Failure to build window list!\n");
break;
}
}
dwCount++;
}
}
ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
}
ObDereferenceObject(Thread);
}
*pcHwndNeeded = dwCount;
Status = STATUS_SUCCESS;
Quit:
SetLastNtError(Status);
UserLeave();
return Status;
}
static void IntSendParentNotify( PWND pWindow, UINT msg )
{
if ( (pWindow->style & (WS_CHILD | WS_POPUP)) == WS_CHILD &&
!(pWindow->ExStyle & WS_EX_NOPARENTNOTIFY))
{
if (VerifyWnd(pWindow->spwndParent) && !UserIsDesktopWindow(pWindow->spwndParent))
{
USER_REFERENCE_ENTRY Ref;
UserRefObjectCo(pWindow->spwndParent, &Ref);
co_IntSendMessage( pWindow->spwndParent->head.h,
WM_PARENTNOTIFY,
MAKEWPARAM( msg, pWindow->IDMenu),
(LPARAM)pWindow->head.h );
UserDerefObjectCo(pWindow->spwndParent);
}
}
}
void FASTCALL
IntFixWindowCoordinates(CREATESTRUCTW* Cs, PWND ParentWindow, DWORD* dwShowMode)
{
#define IS_DEFAULT(x) ((x) == CW_USEDEFAULT || (x) == (SHORT)0x8000)
/* default positioning for overlapped windows */
if(!(Cs->style & (WS_POPUP | WS_CHILD)))
{
PMONITOR pMonitor;
PRTL_USER_PROCESS_PARAMETERS ProcessParams;
pMonitor = UserGetPrimaryMonitor();
/* Check if we don't have a monitor attached yet */
if(pMonitor == NULL)
{
Cs->x = Cs->y = 0;
Cs->cx = 800;
Cs->cy = 600;
return;
}
ProcessParams = PsGetCurrentProcess()->Peb->ProcessParameters;
if (IS_DEFAULT(Cs->x))
{
if (!IS_DEFAULT(Cs->y)) *dwShowMode = Cs->y;
if(ProcessParams->WindowFlags & STARTF_USEPOSITION)
{
Cs->x = ProcessParams->StartingX;
Cs->y = ProcessParams->StartingY;
}
else
{
Cs->x = pMonitor->cWndStack * (UserGetSystemMetrics(SM_CXSIZE) + UserGetSystemMetrics(SM_CXFRAME));
Cs->y = pMonitor->cWndStack * (UserGetSystemMetrics(SM_CYSIZE) + UserGetSystemMetrics(SM_CYFRAME));
if (Cs->x > ((pMonitor->rcWork.right - pMonitor->rcWork.left) / 4) ||
Cs->y > ((pMonitor->rcWork.bottom - pMonitor->rcWork.top) / 4))
{
/* reset counter and position */
Cs->x = 0;
Cs->y = 0;
pMonitor->cWndStack = 0;
}
pMonitor->cWndStack++;
}
}
if (IS_DEFAULT(Cs->cx))
{
if (ProcessParams->WindowFlags & STARTF_USEPOSITION)
{
Cs->cx = ProcessParams->CountX;
Cs->cy = ProcessParams->CountY;
}
else
{
Cs->cx = (pMonitor->rcWork.right - pMonitor->rcWork.left) * 3 / 4;
Cs->cy = (pMonitor->rcWork.bottom - pMonitor->rcWork.top) * 3 / 4;
}
}
/* neither x nor cx are default. Check the y values .
* In the trace we see Outlook and Outlook Express using
* cy set to CW_USEDEFAULT when opening the address book.
*/
else if (IS_DEFAULT(Cs->cy))
{
TRACE("Strange use of CW_USEDEFAULT in nHeight\n");
Cs->cy = (pMonitor->rcWork.bottom - pMonitor->rcWork.top) * 3 / 4;
}
}
else
{
/* if CW_USEDEFAULT is set for non-overlapped windows, both values are set to zero */
if(IS_DEFAULT(Cs->x))
{
Cs->x = 0;
Cs->y = 0;
}
if(IS_DEFAULT(Cs->cx))
{
Cs->cx = 0;
Cs->cy = 0;
}
}
#undef IS_DEFAULT
}
/* Allocates and initializes a window */
PWND FASTCALL IntCreateWindow(CREATESTRUCTW* Cs,
PLARGE_STRING WindowName,
PCLS Class,
PWND ParentWindow,
PWND OwnerWindow,
PVOID acbiBuffer,
PDESKTOP pdeskCreated,
DWORD dwVer )
{
PWND pWnd = NULL;
HWND hWnd;
PTHREADINFO pti;
BOOL MenuChanged;
BOOL bUnicodeWindow;
PCALLPROCDATA pcpd;
pti = pdeskCreated ? gptiDesktopThread : GetW32ThreadInfo();
if (!(Cs->dwExStyle & WS_EX_LAYOUTRTL))
{ // Need both here for wine win.c test_CreateWindow.
//if (Cs->hwndParent && ParentWindow)
if (ParentWindow) // It breaks more tests..... WIP.
{
if ( (Cs->style & (WS_CHILD|WS_POPUP)) == WS_CHILD &&
ParentWindow->ExStyle & WS_EX_LAYOUTRTL &&
!(ParentWindow->ExStyle & WS_EX_NOINHERITLAYOUT) )
Cs->dwExStyle |= WS_EX_LAYOUTRTL;
}
else
{ /*
* Note from MSDN <http://msdn.microsoft.com/en-us/library/aa913269.aspx>:
*
* Dialog boxes and message boxes do not inherit layout, so you must
* set the layout explicitly.
*/
if ( Class->fnid != FNID_DIALOG )
{
if (pti->ppi->dwLayout & LAYOUT_RTL)
{
Cs->dwExStyle |= WS_EX_LAYOUTRTL;
}
}
}
}
/* Automatically add WS_EX_WINDOWEDGE */
if ((Cs->dwExStyle & WS_EX_DLGMODALFRAME) ||
((!(Cs->dwExStyle & WS_EX_STATICEDGE)) &&
(Cs->style & (WS_DLGFRAME | WS_THICKFRAME))))
Cs->dwExStyle |= WS_EX_WINDOWEDGE;
else
Cs->dwExStyle &= ~WS_EX_WINDOWEDGE;
/* Is it a unicode window? */
bUnicodeWindow =!(Cs->dwExStyle & WS_EX_SETANSICREATOR);
Cs->dwExStyle &= ~WS_EX_SETANSICREATOR;
/* Allocate the new window */
pWnd = (PWND) UserCreateObject( gHandleTable,
pdeskCreated ? pdeskCreated : pti->rpdesk,
pti,
(PHANDLE)&hWnd,
TYPE_WINDOW,
sizeof(WND) + Class->cbwndExtra);
if (!pWnd)
{
goto AllocError;
}
TRACE("Created window object with handle %p\n", hWnd);
if (pdeskCreated && pdeskCreated->DesktopWindow == NULL )
{ /* HACK: Helper for win32csr/desktopbg.c */
/* If there is no desktop window yet, we must be creating it */
TRACE("CreateWindow setting desktop.\n");
pdeskCreated->DesktopWindow = hWnd;
pdeskCreated->pDeskInfo->spwnd = pWnd;
}
/*
* Fill out the structure describing it.
*/
/* Remember, pWnd->head is setup in object.c ... */
WndSetParent(pWnd, ParentWindow);
WndSetOwner(pWnd, OwnerWindow);
pWnd->fnid = 0;
WndSetLastActive(pWnd, pWnd);
// Ramp up compatible version sets.
if ( dwVer >= WINVER_WIN31 )
{
pWnd->state2 |= WNDS2_WIN31COMPAT;
if ( dwVer >= WINVER_WINNT4 )
{
pWnd->state2 |= WNDS2_WIN40COMPAT;
if ( dwVer >= WINVER_WIN2K )
{
pWnd->state2 |= WNDS2_WIN50COMPAT;
}
}
}
pWnd->pcls = Class;
pWnd->hModule = Cs->hInstance;
pWnd->style = Cs->style & ~WS_VISIBLE;
pWnd->ExStyle = Cs->dwExStyle;
pWnd->cbwndExtra = pWnd->pcls->cbwndExtra;
pWnd->pActCtx = acbiBuffer;
if (pti->spDefaultImc && Class->atomClassName != gpsi->atomSysClass[ICLS_BUTTON])
pWnd->hImc = UserHMGetHandle(pti->spDefaultImc);
pWnd->InternalPos.MaxPos.x = pWnd->InternalPos.MaxPos.y = -1;
pWnd->InternalPos.IconPos.x = pWnd->InternalPos.IconPos.y = -1;
if (pWnd->spwndParent != NULL && Cs->hwndParent != 0)
{
pWnd->HideFocus = pWnd->spwndParent->HideFocus;
pWnd->HideAccel = pWnd->spwndParent->HideAccel;
}
InitializeListHead(&pWnd->ThreadListEntry);
pWnd->head.pti->cWindows++;
if (Class->spicn && !Class->spicnSm)
{
HICON IconSmHandle = NULL;
if((Class->spicn->CURSORF_flags & (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
== (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
{
IconSmHandle = co_IntCopyImage(
UserHMGetHandle(Class->spicn),
IMAGE_ICON,
UserGetSystemMetrics( SM_CXSMICON ),
UserGetSystemMetrics( SM_CYSMICON ),
LR_COPYFROMRESOURCE);
}
if (!IconSmHandle)
{
/* Retry without copying from resource */
IconSmHandle = co_IntCopyImage(
UserHMGetHandle(Class->spicn),
IMAGE_ICON,
UserGetSystemMetrics( SM_CXSMICON ),
UserGetSystemMetrics( SM_CYSMICON ),
0);
}
if (IconSmHandle)
{
Class->spicnSm = UserGetCurIconObject(IconSmHandle);
Class->CSF_flags |= CSF_CACHEDSMICON;
}
}
if (pWnd->pcls->CSF_flags & CSF_SERVERSIDEPROC)
pWnd->state |= WNDS_SERVERSIDEWINDOWPROC;
/* BugBoy Comments: Comment below say that System classes are always created
as UNICODE. In windows, creating a window with the ANSI version of CreateWindow
sets the window to ansi as verified by testing with IsUnicodeWindow API.
No where can I see in code or through testing does the window change back
to ANSI after being created as UNICODE in ROS. I didnt do more testing to
see what problems this would cause. */
// Set WndProc from Class.
if (IsCallProcHandle(pWnd->pcls->lpfnWndProc))
{
pcpd = UserGetObject(gHandleTable, pWnd->pcls->lpfnWndProc, TYPE_CALLPROC);
if (pcpd)
pWnd->lpfnWndProc = pcpd->pfnClientPrevious;
}
else
{
pWnd->lpfnWndProc = pWnd->pcls->lpfnWndProc;
}
// GetWindowProc, test for non server side default classes and set WndProc.
if ( pWnd->pcls->fnid <= FNID_GHOST && pWnd->pcls->fnid >= FNID_BUTTON )
{
if (bUnicodeWindow)
{
if (GETPFNCLIENTA(pWnd->pcls->fnid) == pWnd->lpfnWndProc)
pWnd->lpfnWndProc = GETPFNCLIENTW(pWnd->pcls->fnid);
}
else
{
if (GETPFNCLIENTW(pWnd->pcls->fnid) == pWnd->lpfnWndProc)
pWnd->lpfnWndProc = GETPFNCLIENTA(pWnd->pcls->fnid);
}
}
// If not an Unicode caller, set Ansi creator bit.
if (!bUnicodeWindow) pWnd->state |= WNDS_ANSICREATOR;
// Clone Class Ansi/Unicode proc type.
if (pWnd->pcls->CSF_flags & CSF_ANSIPROC)
{
pWnd->state |= WNDS_ANSIWINDOWPROC;
pWnd->Unicode = FALSE;
}
else
{ /*
* It seems there can be both an Ansi creator and Unicode Class Window
* WndProc, unless the following overriding conditions occur:
*/
if ( !bUnicodeWindow &&
( Class->atomClassName == gpsi->atomSysClass[ICLS_BUTTON] ||
Class->atomClassName == gpsi->atomSysClass[ICLS_COMBOBOX] ||
Class->atomClassName == gpsi->atomSysClass[ICLS_COMBOLBOX] ||
Class->atomClassName == gpsi->atomSysClass[ICLS_DIALOG] ||
Class->atomClassName == gpsi->atomSysClass[ICLS_EDIT] ||
Class->atomClassName == gpsi->atomSysClass[ICLS_IME] ||
Class->atomClassName == gpsi->atomSysClass[ICLS_LISTBOX] ||
Class->atomClassName == gpsi->atomSysClass[ICLS_MDICLIENT] ||
Class->atomClassName == gpsi->atomSysClass[ICLS_STATIC] ) )
{ // Override Class and set the window Ansi WndProc.
pWnd->state |= WNDS_ANSIWINDOWPROC;
pWnd->Unicode = FALSE;
}
else
{ // Set the window Unicode WndProc.
pWnd->state &= ~WNDS_ANSIWINDOWPROC;
pWnd->Unicode = TRUE;
}
}
/* BugBoy Comments: if the window being created is a edit control, ATOM 0xCxxx,
then my testing shows that windows (2k and XP) creates a CallProc for it immediately
Dont understand why it does this. */
if (Class->atomClassName == gpsi->atomSysClass[ICLS_EDIT])
{
PCALLPROCDATA CallProc;
CallProc = CreateCallProc(pWnd->head.rpdesk, pWnd->lpfnWndProc, pWnd->Unicode , pWnd->head.pti->ppi);
if (!CallProc)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
ERR("Warning: Unable to create CallProc for edit control. Control may not operate correctly! hwnd %p\n", hWnd);
}
else
{
UserAddCallProcToClass(pWnd->pcls, CallProc);
}
}
InitializeListHead(&pWnd->PropListHead);
pWnd->PropListItems = 0;
if ( WindowName->Buffer != NULL && WindowName->Length > 0 )
{
pWnd->strName.Buffer = DesktopHeapAlloc(pWnd->head.rpdesk,
WindowName->Length + sizeof(UNICODE_NULL));
if (pWnd->strName.Buffer == NULL)
{
goto AllocError;
}
RtlCopyMemory(pWnd->strName.Buffer, WindowName->Buffer, WindowName->Length);
pWnd->strName.Buffer[WindowName->Length / sizeof(WCHAR)] = L'\0';
pWnd->strName.Length = WindowName->Length;
pWnd->strName.MaximumLength = WindowName->Length + sizeof(UNICODE_NULL);
}
/* Correct the window style. */
if ((pWnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
{
pWnd->style |= WS_CLIPSIBLINGS;
if (!(pWnd->style & WS_POPUP))
{
pWnd->style |= WS_CAPTION;
}
}
/* WS_EX_WINDOWEDGE depends on some other styles */
if (pWnd->ExStyle & WS_EX_DLGMODALFRAME)
pWnd->ExStyle |= WS_EX_WINDOWEDGE;
else if (pWnd->style & (WS_DLGFRAME | WS_THICKFRAME))
{
if (!((pWnd->ExStyle & WS_EX_STATICEDGE) &&
(pWnd->style & (WS_CHILD | WS_POPUP))))
pWnd->ExStyle |= WS_EX_WINDOWEDGE;
}
else
pWnd->ExStyle &= ~WS_EX_WINDOWEDGE;
if (!(pWnd->style & (WS_CHILD | WS_POPUP)))
pWnd->state |= WNDS_SENDSIZEMOVEMSGS;
/* Set the window menu */
if ((Cs->style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
{
if (Cs->hMenu)
{
IntSetMenu(pWnd, Cs->hMenu, &MenuChanged);
}
else if (pWnd->pcls->lpszMenuName) // Take it from the parent.
{
UNICODE_STRING MenuName;
HMENU hMenu;
if (IS_INTRESOURCE(pWnd->pcls->lpszMenuName))
{
MenuName.Length = 0;
MenuName.MaximumLength = 0;
MenuName.Buffer = pWnd->pcls->lpszMenuName;
}
else
{
RtlInitUnicodeString( &MenuName, pWnd->pcls->lpszMenuName);
}
hMenu = co_IntCallLoadMenu( pWnd->pcls->hModule, &MenuName);
if (hMenu) IntSetMenu(pWnd, hMenu, &MenuChanged);
}
}
else // Not a child
pWnd->IDMenu = (UINT_PTR)Cs->hMenu;
if ( ParentWindow &&
ParentWindow != ParentWindow->head.rpdesk->spwndMessage &&
ParentWindow != ParentWindow->head.rpdesk->pDeskInfo->spwnd )
{
PWND Owner = IntGetNonChildAncestor(ParentWindow);
if (!IntValidateOwnerDepth(pWnd, Owner))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
goto Error;
}
if ( pWnd->spwndOwner &&
pWnd->spwndOwner->ExStyle & WS_EX_TOPMOST )
{
pWnd->ExStyle |= WS_EX_TOPMOST;
}
if ( pWnd->spwndOwner &&
Class->atomClassName != gpsi->atomSysClass[ICLS_IME] &&
pti != pWnd->spwndOwner->head.pti)
{
//ERR("CreateWindow Owner in.\n");
UserAttachThreadInput(pti, pWnd->spwndOwner->head.pti, TRUE);
}
}
/* Insert the window into the thread's window list. */
InsertTailList (&pti->WindowListHead, &pWnd->ThreadListEntry);
/* Handle "CS_CLASSDC", it is tested first. */
if ( (pWnd->pcls->style & CS_CLASSDC) && !(pWnd->pcls->pdce) )
{ /* One DCE per class to have CLASS. */
pWnd->pcls->pdce = DceAllocDCE( pWnd, DCE_CLASS_DC );
}
else if ( pWnd->pcls->style & CS_OWNDC)
{ /* Allocate a DCE for this window. */
DceAllocDCE(pWnd, DCE_WINDOW_DC);
}
return pWnd;
AllocError:
ERR("IntCreateWindow Allocation Error.\n");
SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
Error:
if(pWnd)
UserDereferenceObject(pWnd);
return NULL;
}
/*
* @implemented
*/
PWND FASTCALL
co_UserCreateWindowEx(CREATESTRUCTW* Cs,
PUNICODE_STRING ClassName,
PLARGE_STRING WindowName,
PVOID acbiBuffer,
DWORD dwVer )
{
ULONG style;
PWND Window = NULL, ParentWindow = NULL, OwnerWindow;
HWND hWnd, hWndParent, hWndOwner, hwndInsertAfter;
PWINSTATION_OBJECT WinSta;
PCLS Class = NULL;
SIZE Size;
POINT MaxSize, MaxPos, MinTrack, MaxTrack;
CBT_CREATEWNDW * pCbtCreate;
LRESULT Result;
USER_REFERENCE_ENTRY ParentRef, Ref;
PTHREADINFO pti;
DWORD dwShowMode = SW_SHOW;
CREATESTRUCTW *pCsw = NULL;
PVOID pszClass = NULL, pszName = NULL;
PWND ret = NULL;
/* Get the current window station and reference it */
pti = GetW32ThreadInfo();
if (pti == NULL || pti->rpdesk == NULL)
{
ERR("Thread is not attached to a desktop! Cannot create window (%wZ)\n", ClassName);
return NULL; // There is nothing to cleanup.
}
WinSta = pti->rpdesk->rpwinstaParent;
ObReferenceObjectByPointer(WinSta, KernelMode, ExWindowStationObjectType, 0);
pCsw = NULL;
pCbtCreate = NULL;
/* Get the class and reference it */
Class = IntGetAndReferenceClass(ClassName, Cs->hInstance, FALSE);
if(!Class)
{
EngSetLastError(ERROR_CANNOT_FIND_WND_CLASS);
ERR("Failed to find class %wZ\n", ClassName);
goto cleanup;
}
/* Now find the parent and the owner window */
hWndParent = pti->rpdesk->pDeskInfo->spwnd->head.h;
hWndOwner = NULL;
if (Cs->hwndParent == HWND_MESSAGE)
{
Cs->hwndParent = hWndParent = pti->rpdesk->spwndMessage->head.h;
}
else if (Cs->hwndParent)
{
if ((Cs->style & (WS_CHILD|WS_POPUP)) != WS_CHILD)
hWndOwner = Cs->hwndParent;
else
hWndParent = Cs->hwndParent;
}
else if ((Cs->style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
{
ERR("Cannot create a child window (%wZ) without a parent\n", ClassName);
EngSetLastError(ERROR_TLW_WITH_WSCHILD);
goto cleanup; /* WS_CHILD needs a parent, but WS_POPUP doesn't */
}
else if (Cs->lpszClass != (LPCWSTR)MAKEINTATOM(gpsi->atomSysClass[ICLS_DESKTOP]) &&
(IS_INTRESOURCE(Cs->lpszClass) ||
Cs->lpszClass != (LPCWSTR)MAKEINTATOM(gpsi->atomSysClass[ICLS_HWNDMESSAGE]) ||
_wcsicmp(Cs->lpszClass, L"Message") != 0))
{
if (pti->ppi->dwLayout & LAYOUT_RTL)
{
Cs->dwExStyle |= WS_EX_LAYOUTRTL;
}
}
ParentWindow = hWndParent ? UserGetWindowObject(hWndParent): NULL;
OwnerWindow = hWndOwner ? UserGetWindowObject(hWndOwner): NULL;
if (hWndParent && !ParentWindow)
{
ERR("Got invalid parent window handle for %wZ\n", ClassName);
goto cleanup;
}
else if (hWndOwner && !OwnerWindow)
{
ERR("Got invalid owner window handle for %wZ\n", ClassName);
ParentWindow = NULL;
goto cleanup;
}
if(OwnerWindow)
{
if (IntIsDesktopWindow(OwnerWindow)) OwnerWindow = NULL;
else if (ParentWindow && !IntIsDesktopWindow(ParentWindow))
{
ERR("an owned window must be created as top-level\n");
EngSetLastError( STATUS_ACCESS_DENIED );
goto cleanup;
}
else /* owner must be a top-level window */
{
while ((OwnerWindow->style & (WS_POPUP|WS_CHILD)) == WS_CHILD && !IntIsDesktopWindow(OwnerWindow->spwndParent))
OwnerWindow = OwnerWindow->spwndParent;
}
}
/* Fix the position and the size of the window */
if (ParentWindow)
{
UserRefObjectCo(ParentWindow, &ParentRef);
IntFixWindowCoordinates(Cs, ParentWindow, &dwShowMode);
}
/* Allocate and initialize the new window */
Window = IntCreateWindow(Cs,
WindowName,
Class,
ParentWindow,
OwnerWindow,
acbiBuffer,
NULL,
dwVer );
if(!Window)
{
ERR("IntCreateWindow(%wZ) failed\n", ClassName);
goto cleanup;
}
hWnd = UserHMGetHandle(Window);
hwndInsertAfter = HWND_TOP;
UserRefObjectCo(Window, &Ref);
UserDereferenceObject(Window);
ObDereferenceObject(WinSta);
/* NCCREATE, WM_NCCALCSIZE and Hooks need the original values */
Cs->lpszName = (LPCWSTR) WindowName;
Cs->lpszClass = (LPCWSTR) ClassName;
//// Check for a hook to eliminate overhead. ////
if ( ISITHOOKED(WH_CBT) || (pti->rpdesk->pDeskInfo->fsHooks & HOOKID_TO_FLAG(WH_CBT)) )
{
// Allocate the calling structures Justin Case this goes Global.
pCsw = ExAllocatePoolWithTag(NonPagedPool, sizeof(CREATESTRUCTW), TAG_HOOK);
pCbtCreate = ExAllocatePoolWithTag(NonPagedPool, sizeof(CBT_CREATEWNDW), TAG_HOOK);
if (!pCsw || !pCbtCreate)
{
ERR("UserHeapAlloc() failed!\n");
goto cleanup;
}
if (!IntMsgCreateStructW( Window, pCsw, Cs, &pszClass, &pszName ) )
{
ERR("IntMsgCreateStructW() failed!\n");
goto cleanup;
}
pCbtCreate->lpcs = pCsw;
pCbtCreate->hwndInsertAfter = hwndInsertAfter;
//// Call the WH_CBT hook ////
Result = co_HOOK_CallHooks(WH_CBT, HCBT_CREATEWND, (WPARAM) hWnd, (LPARAM) pCbtCreate);
if (Result != 0)
{
ERR("WH_CBT HCBT_CREATEWND hook failed! 0x%x\n", Result);
goto cleanup;
}
// Write back changes.
Cs->cx = pCsw->cx;
Cs->cy = pCsw->cy;
Cs->x = pCsw->x;
Cs->y = pCsw->y;
hwndInsertAfter = pCbtCreate->hwndInsertAfter;
}
if ((Cs->style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
{
if (ParentWindow != co_GetDesktopWindow(Window))
{
Cs->x += ParentWindow->rcClient.left;
Cs->y += ParentWindow->rcClient.top;
}
}
/* Send the WM_GETMINMAXINFO message */
Size.cx = Cs->cx;
Size.cy = Cs->cy;
if ((Cs->style & WS_THICKFRAME) || !(Cs->style & (WS_POPUP | WS_CHILD)))
{
co_WinPosGetMinMaxInfo(Window, &MaxSize, &MaxPos, &MinTrack, &MaxTrack);
if (Size.cx > MaxTrack.x) Size.cx = MaxTrack.x;
if (Size.cy > MaxTrack.y) Size.cy = MaxTrack.y;
if (Size.cx < MinTrack.x) Size.cx = MinTrack.x;
if (Size.cy < MinTrack.y) Size.cy = MinTrack.y;
}
Window->rcWindow.left = Cs->x;
Window->rcWindow.top = Cs->y;
Window->rcWindow.right = Cs->x + Size.cx;
Window->rcWindow.bottom = Cs->y + Size.cy;
/*
if (0 != (Window->style & WS_CHILD) && ParentWindow)
{
ERR("co_UserCreateWindowEx(): Offset rcWindow\n");
RECTL_vOffsetRect(&Window->rcWindow,
ParentWindow->rcClient.left,
ParentWindow->rcClient.top);
}
*/
/* correct child window coordinates if mirroring on parent is enabled */
if (ParentWindow != NULL)
{
if ( ((Cs->style & WS_CHILD) == WS_CHILD) &&
((ParentWindow->ExStyle & WS_EX_LAYOUTRTL) == WS_EX_LAYOUTRTL))
{
Window->rcWindow.right = ParentWindow->rcClient.right - (Window->rcWindow.left - ParentWindow->rcClient.left);
Window->rcWindow.left = Window->rcWindow.right - Size.cx;
}
}
Window->rcClient = Window->rcWindow;
if (Window->spwndNext || Window->spwndPrev)
{
ERR("Window 0x%p has been linked too early!\n", Window);
}
if (!(Window->state2 & WNDS2_WIN31COMPAT))
{
if (Class->style & CS_PARENTDC && !(ParentWindow->style & WS_CLIPCHILDREN))
Window->style &= ~(WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
}
if ((Window->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
{
if ( !IntIsTopLevelWindow(Window) )
{
if (pti != ParentWindow->head.pti)
{
//ERR("CreateWindow Parent in.\n");
UserAttachThreadInput(pti, ParentWindow->head.pti, TRUE);
}
}
}
/* Send the NCCREATE message */
Result = co_IntSendMessage(UserHMGetHandle(Window), WM_NCCREATE, 0, (LPARAM) Cs);
if (!Result)
{
ERR("co_UserCreateWindowEx(%wZ): NCCREATE message failed\n", ClassName);
goto cleanup;
}
/* Link the window */
if (ParentWindow != NULL)
{
/* Link the window into the siblings list */
if ((Cs->style & (WS_CHILD | WS_MAXIMIZE)) == WS_CHILD)
IntLinkHwnd(Window, HWND_BOTTOM);
else
IntLinkHwnd(Window, hwndInsertAfter);
}
/* Create the IME window for pWnd */
if (IS_IMM_MODE() && !pti->spwndDefaultIme && IntWantImeWindow(Window))
{
PWND pwndDefaultIme = co_IntCreateDefaultImeWindow(Window, Window->hModule);
UserAssignmentLock((PVOID*)&pti->spwndDefaultIme, pwndDefaultIme);
if (pwndDefaultIme)
{
HWND hImeWnd;
USER_REFERENCE_ENTRY Ref;
UserRefObjectCo(pwndDefaultIme, &Ref);
hImeWnd = UserHMGetHandle(pwndDefaultIme);
co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_LOADTHREADLAYOUT, 0);
if (pti->pClientInfo->CI_flags & CI_IMMACTIVATE)
{
HKL hKL = pti->KeyboardLayout->hkl;
co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_ACTIVATELAYOUT, (LPARAM)hKL);
pti->pClientInfo->CI_flags &= ~CI_IMMACTIVATE;
}
UserDerefObjectCo(pwndDefaultIme);
}
}
/* Send the WM_NCCALCSIZE message */
{
// RECT rc;
MaxPos.x = Window->rcWindow.left;
MaxPos.y = Window->rcWindow.top;
Result = co_WinPosGetNonClientSize(Window, &Window->rcWindow, &Window->rcClient);
//rc = Window->rcWindow;
//Result = co_IntSendMessageNoWait(Window->head.h, WM_NCCALCSIZE, FALSE, (LPARAM)&rc);
//Window->rcClient = rc;
RECTL_vOffsetRect(&Window->rcWindow, MaxPos.x - Window->rcWindow.left,
MaxPos.y - Window->rcWindow.top);
}
/* Send the WM_CREATE message. */
Result = co_IntSendMessage(UserHMGetHandle(Window), WM_CREATE, 0, (LPARAM) Cs);
if (Result == (LRESULT)-1)
{
ERR("co_UserCreateWindowEx(%wZ): WM_CREATE message failed\n", ClassName);
goto cleanup;
}
/* Send the EVENT_OBJECT_CREATE event */
IntNotifyWinEvent(EVENT_OBJECT_CREATE, Window, OBJID_WINDOW, CHILDID_SELF, 0);
/* By setting the flag below it can be examined to determine if the window
was created successfully and a valid pwnd was passed back to caller since
from here the function has to succeed. */
Window->state2 |= WNDS2_WMCREATEMSGPROCESSED;
/* Send the WM_SIZE and WM_MOVE messages. */
if (!(Window->state & WNDS_SENDSIZEMOVEMSGS))
{
co_WinPosSendSizeMove(Window);
}
/* Show or maybe minimize or maximize the window. */
style = IntSetStyle( Window, 0, WS_MAXIMIZE | WS_MINIMIZE );
if (style & (WS_MINIMIZE | WS_MAXIMIZE))
{
RECTL NewPos;
UINT SwFlag = (style & WS_MINIMIZE) ? SW_MINIMIZE : SW_MAXIMIZE;
SwFlag = co_WinPosMinMaximize(Window, SwFlag, &NewPos);
SwFlag |= SWP_NOZORDER|SWP_FRAMECHANGED; /* Frame always gets changed */
if (!(style & WS_VISIBLE) || (style & WS_CHILD) || UserGetActiveWindow() ||
(Window->ExStyle & WS_EX_NOACTIVATE))
{
SwFlag |= SWP_NOACTIVATE;
}
co_WinPosSetWindowPos(Window, 0, NewPos.left, NewPos.top,
NewPos.right, NewPos.bottom, SwFlag);
}
/* Send the WM_PARENTNOTIFY message */
IntSendParentNotify(Window, WM_CREATE);
/* Notify the shell that a new window was created */
if (Window->spwndOwner == NULL ||
!(Window->spwndOwner->style & WS_VISIBLE) ||
(Window->spwndOwner->ExStyle & WS_EX_TOOLWINDOW))
{
if (UserIsDesktopWindow(Window->spwndParent) &&
(Window->style & WS_VISIBLE) &&
(!(Window->ExStyle & WS_EX_TOOLWINDOW) ||
(Window->ExStyle & WS_EX_APPWINDOW)))
{
co_IntShellHookNotify(HSHELL_WINDOWCREATED, (WPARAM)hWnd, 0);
}
}
/* Initialize and show the window's scrollbars */
if (Window->style & WS_VSCROLL)
{
co_UserShowScrollBar(Window, SB_VERT, FALSE, TRUE);
}
if (Window->style & WS_HSCROLL)
{
co_UserShowScrollBar(Window, SB_HORZ, TRUE, FALSE);
}
/* Show the new window */
if (Cs->style & WS_VISIBLE)
{
if (Window->style & WS_MAXIMIZE)
dwShowMode = SW_SHOW;
else if (Window->style & WS_MINIMIZE)
dwShowMode = SW_SHOWMINIMIZED;
co_WinPosShowWindow(Window, dwShowMode);
if (Window->ExStyle & WS_EX_MDICHILD)
{
ASSERT(ParentWindow);
if(!ParentWindow)
goto cleanup;
co_IntSendMessage(UserHMGetHandle(ParentWindow), WM_MDIREFRESHMENU, 0, 0);
/* ShowWindow won't activate child windows */
co_WinPosSetWindowPos(Window, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
}
}
if (Class->atomClassName == gaGuiConsoleWndClass)
{
/* Count only console windows manually */
co_IntUserManualGuiCheck(TRUE);
}
TRACE("co_UserCreateWindowEx(%wZ): Created window %p\n", ClassName, hWnd);
ret = Window;
cleanup:
if (!ret)
{
TRACE("co_UserCreateWindowEx(): Error Created window!\n");
/* If the window was created, the class will be dereferenced by co_UserDestroyWindow */
if (Window)
co_UserDestroyWindow(Window);
else if (Class)
IntDereferenceClass(Class, pti->pDeskInfo, pti->ppi);
}
if (pCsw) ExFreePoolWithTag(pCsw, TAG_HOOK);
if (pCbtCreate) ExFreePoolWithTag(pCbtCreate, TAG_HOOK);
if (pszName) UserHeapFree(pszName);
if (pszClass) UserHeapFree(pszClass);
if (Window)
{
UserDerefObjectCo(Window);
}
if (ParentWindow) UserDerefObjectCo(ParentWindow);
// See CORE-13717, not setting error on success.
if (ret)
EngSetLastError(ERROR_SUCCESS);
return ret;
}
NTSTATUS
NTAPI
ProbeAndCaptureLargeString(
OUT PLARGE_STRING plstrSafe,
IN PLARGE_STRING plstrUnsafe)
{
LARGE_STRING lstrTemp;
PVOID pvBuffer = NULL;
_SEH2_TRY
{
/* Probe and copy the string */
ProbeForRead(plstrUnsafe, sizeof(LARGE_STRING), sizeof(ULONG));
lstrTemp = *plstrUnsafe;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Fail */
_SEH2_YIELD(return _SEH2_GetExceptionCode();)
}
_SEH2_END
if (lstrTemp.Length != 0)
{
/* Allocate a buffer from paged pool */
pvBuffer = ExAllocatePoolWithTag(PagedPool, lstrTemp.Length, TAG_STRING);
if (!pvBuffer)
{
return STATUS_NO_MEMORY;
}
_SEH2_TRY
{
/* Probe and copy the buffer */
ProbeForRead(lstrTemp.Buffer, lstrTemp.Length, sizeof(WCHAR));
RtlCopyMemory(pvBuffer, lstrTemp.Buffer, lstrTemp.Length);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Cleanup and fail */
ExFreePoolWithTag(pvBuffer, TAG_STRING);
_SEH2_YIELD(return _SEH2_GetExceptionCode();)
}
_SEH2_END
}
/* Set the output string */
plstrSafe->Buffer = pvBuffer;
plstrSafe->Length = lstrTemp.Length;
plstrSafe->MaximumLength = lstrTemp.Length;
return STATUS_SUCCESS;
}
/**
* \todo Allow passing plstrClassName as ANSI.
*/
HWND
NTAPI
NtUserCreateWindowEx(
DWORD dwExStyle,
PLARGE_STRING plstrClassName,
PLARGE_STRING plstrClsVersion,
PLARGE_STRING plstrWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam,
DWORD dwFlags,
PVOID acbiBuffer)
{
NTSTATUS Status;
LARGE_STRING lstrWindowName;
LARGE_STRING lstrClassName;
LARGE_STRING lstrClsVersion;
UNICODE_STRING ustrClassName;
UNICODE_STRING ustrClsVersion;
CREATESTRUCTW Cs;
HWND hwnd = NULL;
PWND pwnd;
lstrWindowName.Buffer = NULL;
lstrClassName.Buffer = NULL;
lstrClsVersion.Buffer = NULL;
if ( (dwStyle & (WS_POPUP|WS_CHILD)) != WS_CHILD)
{
/* check hMenu is valid handle */
if (hMenu && !UserGetMenuObject(hMenu))
{
ERR("NtUserCreateWindowEx: Got an invalid menu handle!\n");
EngSetLastError(ERROR_INVALID_MENU_HANDLE);
return NULL;
}
}
/* Copy the window name to kernel mode */
Status = ProbeAndCaptureLargeString(&lstrWindowName, plstrWindowName);
if (!NT_SUCCESS(Status))
{
ERR("NtUserCreateWindowEx: failed to capture plstrWindowName\n");
SetLastNtError(Status);
return NULL;
}
plstrWindowName = &lstrWindowName;
/* Check if the class is an atom */
if (IS_ATOM(plstrClassName))
{
/* It is, pass the atom in the UNICODE_STRING */
ustrClassName.Buffer = (PVOID)plstrClassName;
ustrClassName.Length = 0;
ustrClassName.MaximumLength = 0;
}
else
{
/* It's not, capture the class name */
Status = ProbeAndCaptureLargeString(&lstrClassName, plstrClassName);
if (!NT_SUCCESS(Status))
{
ERR("NtUserCreateWindowEx: failed to capture plstrClassName\n");
/* Set last error, cleanup and return */
SetLastNtError(Status);
goto cleanup;
}
/* We pass it on as a UNICODE_STRING */
ustrClassName.Buffer = lstrClassName.Buffer;
ustrClassName.Length = (USHORT)min(lstrClassName.Length, MAXUSHORT); // FIXME: LARGE_STRING truncated
ustrClassName.MaximumLength = (USHORT)min(lstrClassName.MaximumLength, MAXUSHORT);
}
/* Check if the class version is an atom */
if (IS_ATOM(plstrClsVersion))
{
/* It is, pass the atom in the UNICODE_STRING */
ustrClsVersion.Buffer = (PVOID)plstrClsVersion;
ustrClsVersion.Length = 0;
ustrClsVersion.MaximumLength = 0;
}
else
{
/* It's not, capture the class name */
Status = ProbeAndCaptureLargeString(&lstrClsVersion, plstrClsVersion);
if (!NT_SUCCESS(Status))
{
ERR("NtUserCreateWindowEx: failed to capture plstrClsVersion\n");
/* Set last error, cleanup and return */
SetLastNtError(Status);
goto cleanup;
}
/* We pass it on as a UNICODE_STRING */
ustrClsVersion.Buffer = lstrClsVersion.Buffer;
ustrClsVersion.Length = (USHORT)min(lstrClsVersion.Length, MAXUSHORT); // FIXME: LARGE_STRING truncated
ustrClsVersion.MaximumLength = (USHORT)min(lstrClsVersion.MaximumLength, MAXUSHORT);
}
/* Fill the CREATESTRUCTW */
/* we will keep here the original parameters */
Cs.style = dwStyle;
Cs.lpCreateParams = lpParam;
Cs.hInstance = hInstance;
Cs.hMenu = hMenu;
Cs.hwndParent = hWndParent;
Cs.cx = nWidth;
Cs.cy = nHeight;
Cs.x = x;
Cs.y = y;
Cs.lpszName = (LPCWSTR) plstrWindowName->Buffer;
Cs.lpszClass = ustrClassName.Buffer;
Cs.dwExStyle = dwExStyle;
UserEnterExclusive();
/* Call the internal function */
pwnd = co_UserCreateWindowEx(&Cs, &ustrClsVersion, plstrWindowName, acbiBuffer, dwFlags);
if(!pwnd)
{
ERR("co_UserCreateWindowEx failed!\n");
}
hwnd = pwnd ? UserHMGetHandle(pwnd) : NULL;
UserLeave();
cleanup:
if (lstrWindowName.Buffer)
{
ExFreePoolWithTag(lstrWindowName.Buffer, TAG_STRING);
}
if (lstrClassName.Buffer)
{
ExFreePoolWithTag(lstrClassName.Buffer, TAG_STRING);
}
if (lstrClsVersion.Buffer)
{
ExFreePoolWithTag(lstrClsVersion.Buffer, TAG_STRING);
}
return hwnd;
}
// Win: xxxDW_DestroyOwnedWindows
VOID FASTCALL IntDestroyOwnedWindows(PWND Window)
{
HWND* List;
HWND* phWnd;
PWND pWnd;
USER_REFERENCE_ENTRY Ref;
List = IntWinListOwnedPopups(Window);
if (!List)
return;
for (phWnd = List; *phWnd; ++phWnd)
{
pWnd = ValidateHwndNoErr(*phWnd);
if (pWnd == NULL)
continue;
ASSERT(pWnd->spwndOwner == Window);
ASSERT(pWnd != Window);
WndSetOwner(pWnd, NULL);
if (IntWndBelongsToThread(pWnd, PsGetCurrentThreadWin32Thread()))
{
UserRefObjectCo(pWnd, &Ref); // Temp HACK?
co_UserDestroyWindow(pWnd);
UserDerefObjectCo(pWnd); // Temp HACK?
}
else
{
ERR("IntWndBelongsToThread(0x%p) is FALSE, ignoring.\n", pWnd);
}
}
ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
}
// Win: xxxDestroyWindow
BOOLEAN co_UserDestroyWindow(PVOID Object)
{
HWND hWnd;
PWND pwndTemp;
PTHREADINFO ti;
MSG msg;
PWND Window = Object;
ASSERT_REFS_CO(Window); // FIXME: Temp HACK?
/* NtUserDestroyWindow does check if the window has already been destroyed
but co_UserDestroyWindow can be called from more paths which means
that it can also be called for a window that has already been destroyed. */
if (!IntIsWindow(UserHMGetHandle(Window)))
{
TRACE("Tried to destroy a window twice\n");
return TRUE;
}
hWnd = Window->head.h;
ti = PsGetCurrentThreadWin32Thread();
TRACE("co_UserDestroyWindow(Window = 0x%p, hWnd = 0x%p)\n", Window, hWnd);
/* Check for owner thread */
if (Window->head.pti != ti)
{
/* Check if we are destroying the desktop window */
if (! ((Window->head.rpdesk->dwDTFlags & DF_DESTROYED) && Window == Window->head.rpdesk->pDeskInfo->spwnd))
{
EngSetLastError(ERROR_ACCESS_DENIED);
return FALSE;
}
}
/* If window was created successfully and it is hooked */
if ((Window->state2 & WNDS2_WMCREATEMSGPROCESSED))
{
if (co_HOOK_CallHooks(WH_CBT, HCBT_DESTROYWND, (WPARAM) hWnd, 0))
{
ERR("Destroy Window WH_CBT Call Hook return!\n");
return FALSE;
}
}
if (Window->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME])
{
if ((Window->style & (WS_POPUP|WS_CHILD)) != WS_CHILD)
{
if (Window->spwndOwner)
{
//ERR("DestroyWindow Owner out.\n");
UserAttachThreadInput(Window->head.pti, Window->spwndOwner->head.pti, FALSE);
}
}
}
/* Inform the parent */
if (Window->style & WS_CHILD)
{
IntSendParentNotify(Window, WM_DESTROY);
}
if (!Window->spwndOwner && !IntGetParent(Window))
{
co_IntShellHookNotify(HSHELL_WINDOWDESTROYED, (WPARAM) hWnd, 0);
}
/* Hide the window */
if (Window->style & WS_VISIBLE)
{
if (Window->style & WS_CHILD)
{
/* Only child windows receive WM_SHOWWINDOW in DestroyWindow() */
co_WinPosShowWindow(Window, SW_HIDE);
}
else
{
co_WinPosSetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_HIDEWINDOW );
}
}
/* Adjust last active */
if ((pwndTemp = Window->spwndOwner))
{
while (pwndTemp->spwndOwner)
pwndTemp = pwndTemp->spwndOwner;
if (pwndTemp->spwndLastActive == Window)
WndSetLastActive(pwndTemp, Window->spwndOwner);
}
if (Window->spwndParent && IntIsWindow(UserHMGetHandle(Window)))
{
if ((Window->style & (WS_POPUP | WS_CHILD)) == WS_CHILD)
{
if (!IntIsTopLevelWindow(Window))
{
//ERR("DestroyWindow Parent out.\n");
UserAttachThreadInput(Window->head.pti, Window->spwndParent->head.pti, FALSE);
}
}
}
if (Window->head.pti->MessageQueue->spwndActive == Window)
Window->head.pti->MessageQueue->spwndActive = NULL;
if (Window->head.pti->MessageQueue->spwndFocus == Window)
Window->head.pti->MessageQueue->spwndFocus = NULL;
if (Window->head.pti->MessageQueue->spwndActivePrev == Window)
Window->head.pti->MessageQueue->spwndActivePrev = NULL;
if (Window->head.pti->MessageQueue->spwndCapture == Window)
Window->head.pti->MessageQueue->spwndCapture = NULL;
/*
* Check if this window is the Shell's Desktop Window. If so set hShellWindow to NULL
*/
if (ti->pDeskInfo != NULL)
{
if (ti->pDeskInfo->hShellWindow == hWnd)
{
ERR("Destroying the ShellWindow!\n");
ti->pDeskInfo->hShellWindow = NULL;
}
}
IntEngWindowChanged(Window, WOC_DELETE);
if (!IntIsWindow(UserHMGetHandle(Window)))
{
return TRUE;
}
/* Recursively destroy owned windows */
if (!(Window->style & WS_CHILD))
{
IntDestroyOwnedWindows(Window);
}
/* Generate mouse move message for the next window */
msg.message = WM_MOUSEMOVE;
msg.wParam = UserGetMouseButtonsState();
msg.lParam = MAKELPARAM(gpsi->ptCursor.x, gpsi->ptCursor.y);
msg.pt = gpsi->ptCursor;
co_MsqInsertMouseMessage(&msg, 0, 0, TRUE);
IntNotifyWinEvent(EVENT_OBJECT_DESTROY, Window, OBJID_WINDOW, CHILDID_SELF, 0);
/* Send destroy messages */
IntSendDestroyMsg(UserHMGetHandle(Window));
/* Destroy the default IME window if necessary */
if (IS_IMM_MODE() && !(ti->TIF_flags & TIF_INCLEANUP) &&
ti->spwndDefaultIme && (ti->spwndDefaultIme != Window) &&
!(Window->state & WNDS_DESTROYED) && !IS_WND_IMELIKE(Window))
{
if (IS_WND_CHILD(Window))
{
if (IntImeCanDestroyDefIMEforChild(ti->spwndDefaultIme, Window))
co_UserDestroyWindow(ti->spwndDefaultIme);
}
else
{
if (IntImeCanDestroyDefIME(ti->spwndDefaultIme, Window))
co_UserDestroyWindow(ti->spwndDefaultIme);
}
}
if (!IntIsWindow(UserHMGetHandle(Window)))
{
return TRUE;
}
/* Destroy the window storage */
co_UserFreeWindow(Window, PsGetCurrentProcessWin32Process(), PsGetCurrentThreadWin32Thread(), TRUE);
return TRUE;
}
/*
* @implemented
*/
BOOLEAN APIENTRY
NtUserDestroyWindow(HWND Wnd)
{
PWND Window;
DECLARE_RETURN(BOOLEAN);
BOOLEAN ret;
USER_REFERENCE_ENTRY Ref;
TRACE("Enter NtUserDestroyWindow\n");
UserEnterExclusive();
if (!(Window = UserGetWindowObject(Wnd)))
{
RETURN(FALSE);
}
UserRefObjectCo(Window, &Ref); // FIXME: Dunno if win should be reffed during destroy...
ret = co_UserDestroyWindow(Window);
UserDerefObjectCo(Window); // FIXME: Dunno if win should be reffed during destroy...
RETURN(ret);
CLEANUP:
TRACE("Leave NtUserDestroyWindow, ret=%u\n", _ret_);
UserLeave();
END_CLEANUP;
}
HWND FASTCALL
IntFindWindow(PWND Parent,
PWND ChildAfter,
RTL_ATOM ClassAtom,
PUNICODE_STRING WindowName)
{
BOOL CheckWindowName;
HWND *List, *phWnd;
HWND Ret = NULL;
UNICODE_STRING CurrentWindowName;
ASSERT(Parent);
CheckWindowName = WindowName->Buffer != 0;
if((List = IntWinListChildren(Parent)))
{
phWnd = List;
if(ChildAfter)
{
/* skip handles before and including ChildAfter */
while(*phWnd && (*(phWnd++) != ChildAfter->head.h))
;
}
/* search children */
while(*phWnd)
{
PWND Child;
if(!(Child = UserGetWindowObject(*(phWnd++))))
{
continue;
}
/* Do not send WM_GETTEXT messages in the kernel mode version!
The user mode version however calls GetWindowText() which will
send WM_GETTEXT messages to windows belonging to its processes */
if (!ClassAtom || Child->pcls->atomNVClassName == ClassAtom)
{
// FIXME: LARGE_STRING truncated
CurrentWindowName.Buffer = Child->strName.Buffer;
CurrentWindowName.Length = (USHORT)min(Child->strName.Length, MAXUSHORT);
CurrentWindowName.MaximumLength = (USHORT)min(Child->strName.MaximumLength, MAXUSHORT);
if(!CheckWindowName ||
(Child->strName.Length < 0xFFFF &&
!RtlCompareUnicodeString(WindowName, &CurrentWindowName, TRUE)))
{
Ret = Child->head.h;
break;
}
}
}
ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
}
return Ret;
}
/*
* FUNCTION:
* Searches a window's children for a window with the specified
* class and name
* ARGUMENTS:
* hwndParent = The window whose childs are to be searched.
* NULL = desktop
* HWND_MESSAGE = message-only windows
*
* hwndChildAfter = Search starts after this child window.
* NULL = start from beginning
*
* ucClassName = Class name to search for
* Reguired parameter.
*
* ucWindowName = Window name
* ->Buffer == NULL = don't care
*
* RETURNS:
* The HWND of the window if it was found, otherwise NULL
*/
/*
* @implemented
*/
HWND APIENTRY
NtUserFindWindowEx(HWND hwndParent,
HWND hwndChildAfter,
PUNICODE_STRING ucClassName,
PUNICODE_STRING ucWindowName,
DWORD dwUnknown)
{
PWND Parent, ChildAfter;
UNICODE_STRING ClassName = {0}, WindowName = {0};
HWND Desktop, Ret = NULL;
BOOL DoMessageWnd = FALSE;
RTL_ATOM ClassAtom = (RTL_ATOM)0;
DECLARE_RETURN(HWND);
TRACE("Enter NtUserFindWindowEx\n");
UserEnterShared();
if (ucClassName != NULL || ucWindowName != NULL)
{
_SEH2_TRY
{
if (ucClassName != NULL)
{
ClassName = ProbeForReadUnicodeString(ucClassName);
if (ClassName.Length != 0)
{
ProbeForRead(ClassName.Buffer,
ClassName.Length,
sizeof(WCHAR));
}
else if (!IS_ATOM(ClassName.Buffer))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
_SEH2_LEAVE;
}
if (!IntGetAtomFromStringOrAtom(&ClassName,
&ClassAtom))
{
EngSetLastError(ERROR_CANNOT_FIND_WND_CLASS);
_SEH2_LEAVE;
}
}
if (ucWindowName != NULL)
{
WindowName = ProbeForReadUnicodeString(ucWindowName);
if (WindowName.Length != 0)
{
ProbeForRead(WindowName.Buffer,
WindowName.Length,
sizeof(WCHAR));
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(RETURN(NULL));
}
_SEH2_END;
if (ucClassName != NULL)
{
if (ClassName.Length == 0 && ClassName.Buffer != NULL &&
!IS_ATOM(ClassName.Buffer))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
RETURN(NULL);
}
else if (ClassAtom == (RTL_ATOM)0)
{
/* LastError code was set by IntGetAtomFromStringOrAtom */
RETURN(NULL);
}
}
}
Desktop = IntGetCurrentThreadDesktopWindow();
if(hwndParent == NULL)
{
hwndParent = Desktop;
DoMessageWnd = TRUE;
}
else if(hwndParent == HWND_MESSAGE)
{
hwndParent = IntGetMessageWindow();
}
if(!(Parent = UserGetWindowObject(hwndParent)))
{
RETURN( NULL);
}
ChildAfter = NULL;
if(hwndChildAfter && !(ChildAfter = UserGetWindowObject(hwndChildAfter)))
{
RETURN( NULL);
}
_SEH2_TRY
{
if(Parent->head.h == Desktop)
{
HWND *List, *phWnd;
PWND TopLevelWindow;
BOOLEAN CheckWindowName;
BOOLEAN WindowMatches;
BOOLEAN ClassMatches;
/* windows searches through all top-level windows if the parent is the desktop
window */
if((List = IntWinListChildren(Parent)))
{
phWnd = List;
if(ChildAfter)
{
/* skip handles before and including ChildAfter */
while(*phWnd && (*(phWnd++) != ChildAfter->head.h))
;
}
CheckWindowName = WindowName.Buffer != 0;
/* search children */
while(*phWnd)
{
UNICODE_STRING ustr;
if(!(TopLevelWindow = UserGetWindowObject(*(phWnd++))))
{
continue;
}
/* Do not send WM_GETTEXT messages in the kernel mode version!
The user mode version however calls GetWindowText() which will
send WM_GETTEXT messages to windows belonging to its processes */
ustr.Buffer = TopLevelWindow->strName.Buffer;
ustr.Length = (USHORT)min(TopLevelWindow->strName.Length, MAXUSHORT); // FIXME:LARGE_STRING truncated
ustr.MaximumLength = (USHORT)min(TopLevelWindow->strName.MaximumLength, MAXUSHORT);
WindowMatches = !CheckWindowName ||
(TopLevelWindow->strName.Length < 0xFFFF &&
!RtlCompareUnicodeString(&WindowName, &ustr, TRUE));
ClassMatches = (ClassAtom == (RTL_ATOM)0) ||
ClassAtom == TopLevelWindow->pcls->atomNVClassName;
if (WindowMatches && ClassMatches)
{
Ret = TopLevelWindow->head.h;
break;
}
if (IntFindWindow(TopLevelWindow, NULL, ClassAtom, &WindowName))
{
/* window returns the handle of the top-level window, in case it found
the child window */
Ret = TopLevelWindow->head.h;
break;
}
}
ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
}
}
else
{
TRACE("FindWindowEx: Not Desktop Parent!\n");
Ret = IntFindWindow(Parent, ChildAfter, ClassAtom, &WindowName);
}
if (Ret == NULL && DoMessageWnd)
{
PWND MsgWindows;
if((MsgWindows = UserGetWindowObject(IntGetMessageWindow())))
{
Ret = IntFindWindow(MsgWindows, ChildAfter, ClassAtom, &WindowName);
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
Ret = NULL;
}
_SEH2_END;
RETURN( Ret);
CLEANUP:
TRACE("Leave NtUserFindWindowEx, ret %p\n", _ret_);
UserLeave();
END_CLEANUP;
}
/*
* @implemented
*/
PWND FASTCALL UserGetAncestor(PWND Wnd, UINT Type)
{
PWND WndAncestor, Parent;
if (Wnd->head.h == IntGetDesktopWindow())
{
return NULL;
}
switch (Type)
{
case GA_PARENT:
{
WndAncestor = Wnd->spwndParent;
break;
}
case GA_ROOT:
{
WndAncestor = Wnd;
Parent = NULL;
for(;;)
{
if(!(Parent = WndAncestor->spwndParent))
{
break;
}
if(IntIsDesktopWindow(Parent))
{
break;
}
WndAncestor = Parent;
}
break;
}
case GA_ROOTOWNER:
{
WndAncestor = Wnd;
for (;;)
{
Parent = IntGetParent(WndAncestor);
if (!Parent)
{
break;
}
WndAncestor = Parent;
}
break;
}
default:
{
return NULL;
}
}
return WndAncestor;
}
/*
* @implemented
*/
HWND APIENTRY
NtUserGetAncestor(HWND hWnd, UINT Type)
{
PWND Window, Ancestor;
DECLARE_RETURN(HWND);
TRACE("Enter NtUserGetAncestor\n");
UserEnterExclusive();
if (!(Window = UserGetWindowObject(hWnd)))
{
RETURN(NULL);
}
Ancestor = UserGetAncestor(Window, Type);
/* faxme: can UserGetAncestor ever return NULL for a valid window? */
RETURN(Ancestor ? Ancestor->head.h : NULL);
CLEANUP:
TRACE("Leave NtUserGetAncestor, ret=%p\n", _ret_);
UserLeave();
END_CLEANUP;
}
////
//// ReactOS work around! Keep it the sames as in Combo.c and Controls.h
////
/* combo state struct */
typedef struct
{
HWND self;
HWND owner;
UINT dwStyle;
HWND hWndEdit;
HWND hWndLBox;
UINT wState;
HFONT hFont;
RECT textRect;
RECT buttonRect;
RECT droppedRect;
INT droppedIndex;
INT fixedOwnerDrawHeight;
INT droppedWidth; /* last two are not used unless set */
INT editHeight; /* explicitly */
LONG UIState;
} HEADCOMBO,*LPHEADCOMBO;
// Window Extra data container.
typedef struct _WND2CBOX
{
WND;
LPHEADCOMBO pCBox;
} WND2CBOX, *PWND2CBOX;
#define CBF_BUTTONDOWN 0x0002
////
////
////
BOOL
APIENTRY
NtUserGetComboBoxInfo(
HWND hWnd,
PCOMBOBOXINFO pcbi)
{
PWND Wnd;
PPROCESSINFO ppi;
BOOL NotSameppi = FALSE;
BOOL Ret = TRUE;
DECLARE_RETURN(BOOL);
TRACE("Enter NtUserGetComboBoxInfo\n");
UserEnterShared();
if (!(Wnd = UserGetWindowObject(hWnd)))
{
RETURN( FALSE );
}
_SEH2_TRY
{
ProbeForWrite(pcbi, sizeof(COMBOBOXINFO), 1);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(RETURN(FALSE));
}
_SEH2_END;
if (pcbi->cbSize < sizeof(COMBOBOXINFO))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
RETURN(FALSE);
}
// Pass the user pointer, it was already probed.
if ((Wnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_COMBOBOX]) && Wnd->fnid != FNID_COMBOBOX)
{
RETURN( (BOOL) co_IntSendMessage( Wnd->head.h, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi));
}
ppi = PsGetCurrentProcessWin32Process();
NotSameppi = ppi != Wnd->head.pti->ppi;
if (NotSameppi)
{
KeAttachProcess(&Wnd->head.pti->ppi->peProcess->Pcb);
}
_SEH2_TRY
{
LPHEADCOMBO lphc = ((PWND2CBOX)Wnd)->pCBox;
pcbi->rcItem = lphc->textRect;
pcbi->rcButton = lphc->buttonRect;
pcbi->stateButton = 0;
if (lphc->wState & CBF_BUTTONDOWN)
pcbi->stateButton |= STATE_SYSTEM_PRESSED;
if (RECTL_bIsEmptyRect(&lphc->buttonRect))
pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
pcbi->hwndCombo = lphc->self;
pcbi->hwndItem = lphc->hWndEdit;
pcbi->hwndList = lphc->hWndLBox;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Ret = FALSE;
SetLastNtError(_SEH2_GetExceptionCode());
}
_SEH2_END;
RETURN( Ret);
CLEANUP:
if (NotSameppi) KeDetachProcess();
TRACE("Leave NtUserGetComboBoxInfo, ret=%i\n",_ret_);
UserLeave();
END_CLEANUP;
}
////
//// ReactOS work around! Keep it the sames as in Listbox.c
////
/* Listbox structure */
typedef struct
{
HWND self; /* Our own window handle */
HWND owner; /* Owner window to send notifications to */
UINT style; /* Window style */
INT width; /* Window width */
INT height; /* Window height */
VOID *items; /* Array of items */
INT nb_items; /* Number of items */
INT top_item; /* Top visible item */
INT selected_item; /* Selected item */
INT focus_item; /* Item that has the focus */
INT anchor_item; /* Anchor item for extended selection */
INT item_height; /* Default item height */
INT page_size; /* Items per listbox page */
INT column_width; /* Column width for multi-column listboxes */
} LB_DESCR;
// Window Extra data container.
typedef struct _WND2LB
{
WND;
LB_DESCR * pLBiv;
} WND2LB, *PWND2LB;
////
////
////
DWORD
APIENTRY
NtUserGetListBoxInfo(
HWND hWnd)
{
PWND Wnd;
PPROCESSINFO ppi;
BOOL NotSameppi = FALSE;
DWORD Ret = 0;
DECLARE_RETURN(DWORD);
TRACE("Enter NtUserGetListBoxInfo\n");
UserEnterShared();
if (!(Wnd = UserGetWindowObject(hWnd)))
{
RETURN( 0 );
}
if ((Wnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_LISTBOX]) && Wnd->fnid != FNID_LISTBOX)
{
RETURN( (DWORD) co_IntSendMessage( Wnd->head.h, LB_GETLISTBOXINFO, 0, 0 ));
}
// wine lisbox:test_GetListBoxInfo lb_getlistboxinfo = 0, should not send a message!
ppi = PsGetCurrentProcessWin32Process();
NotSameppi = ppi != Wnd->head.pti->ppi;
if (NotSameppi)
{
KeAttachProcess(&Wnd->head.pti->ppi->peProcess->Pcb);
}
_SEH2_TRY
{
LB_DESCR *descr = ((PWND2LB)Wnd)->pLBiv;
// See Controls ListBox.c:LB_GETLISTBOXINFO must match...
Ret = descr->page_size;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Ret = 0;
SetLastNtError(_SEH2_GetExceptionCode());
}
_SEH2_END;
RETURN( Ret);
CLEANUP:
if (NotSameppi) KeDetachProcess();
TRACE("Leave NtUserGetListBoxInfo, ret=%lu\n", _ret_);
UserLeave();
END_CLEANUP;
}
/*
* NtUserSetParent
*
* The NtUserSetParent function changes the parent window of the specified
* child window.
*
* Remarks
* The new parent window and the child window must belong to the same
* application. If the window identified by the hWndChild parameter is
* visible, the system performs the appropriate redrawing and repainting.
* For compatibility reasons, NtUserSetParent does not modify the WS_CHILD
* or WS_POPUP window styles of the window whose parent is being changed.
*
* Status
* @implemented
*/
HWND APIENTRY
NtUserSetParent(HWND hWndChild, HWND hWndNewParent)
{
DECLARE_RETURN(HWND);
TRACE("Enter NtUserSetParent\n");
UserEnterExclusive();
/*
Check Parent first from user space, set it here.
*/
if (!hWndNewParent)
{
hWndNewParent = IntGetDesktopWindow();
}
else if (hWndNewParent == HWND_MESSAGE)
{
hWndNewParent = IntGetMessageWindow();
}
RETURN( co_UserSetParent(hWndChild, hWndNewParent));
CLEANUP:
TRACE("Leave NtUserSetParent, ret=%p\n", _ret_);
UserLeave();
END_CLEANUP;
}
/*
* UserGetShellWindow
*
* Returns a handle to shell window that was set by NtUserSetShellWindowEx.
*
* Status
* @implemented
*/
HWND FASTCALL UserGetShellWindow(VOID)
{
PWINSTATION_OBJECT WinStaObject;
HWND Ret;
NTSTATUS Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
UserMode,
0,
&WinStaObject,
0);
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
return( (HWND)0);
}
Ret = (HWND)WinStaObject->ShellWindow;
ObDereferenceObject(WinStaObject);
return( Ret);
}
/*
* NtUserSetShellWindowEx
*
* This is undocumented function to set global shell window. The global
* shell window has special handling of window position.
*
* Status
* @implemented
*/
BOOL APIENTRY
NtUserSetShellWindowEx(HWND hwndShell, HWND hwndListView)
{
PWINSTATION_OBJECT WinStaObject;
PWND WndShell, WndListView;
DECLARE_RETURN(BOOL);
USER_REFERENCE_ENTRY Ref;
NTSTATUS Status;
PTHREADINFO ti;
TRACE("Enter NtUserSetShellWindowEx\n");
UserEnterExclusive();
if (!(WndShell = UserGetWindowObject(hwndShell)))
{
RETURN(FALSE);
}
if (!(WndListView = UserGetWindowObject(hwndListView)))
{
RETURN(FALSE);
}
Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
UserMode,
0,
&WinStaObject,
0);
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
RETURN( FALSE);
}
/*
* Test if we are permitted to change the shell window.
*/
if (WinStaObject->ShellWindow)
{
ObDereferenceObject(WinStaObject);
RETURN( FALSE);
}
/*
* Move shell window into background.
*/
if (hwndListView && hwndListView != hwndShell)
{
/*
* Disabled for now to get Explorer working.
* -- Filip, 01/nov/2003
*/
#if 0
co_WinPosSetWindowPos(WndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
#endif
if (WndListView->ExStyle & WS_EX_TOPMOST)
{
ObDereferenceObject(WinStaObject);
RETURN( FALSE);
}
}
if (WndShell->ExStyle & WS_EX_TOPMOST)
{
ObDereferenceObject(WinStaObject);
RETURN( FALSE);
}
UserRefObjectCo(WndShell, &Ref);
WndShell->state2 |= WNDS2_BOTTOMMOST;
co_WinPosSetWindowPos(WndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
WinStaObject->ShellWindow = hwndShell;
WinStaObject->ShellListView = hwndListView;
ti = GetW32ThreadInfo();
if (ti->pDeskInfo)
{
ti->pDeskInfo->hShellWindow = hwndShell;
ti->pDeskInfo->spwndShell = WndShell;
ti->pDeskInfo->spwndBkGnd = WndListView;
ti->pDeskInfo->ppiShellProcess = ti->ppi;
}
UserRegisterHotKey(WndShell, SC_TASKLIST, MOD_CONTROL, VK_ESCAPE);
UserDerefObjectCo(WndShell);
ObDereferenceObject(WinStaObject);
RETURN( TRUE);
CLEANUP:
TRACE("Leave NtUserSetShellWindowEx, ret=%i\n",_ret_);
UserLeave();
END_CLEANUP;
}
// Fixes wine Win test_window_styles and todo tests...
static BOOL FASTCALL
IntCheckFrameEdge(ULONG Style, ULONG ExStyle)
{
if (ExStyle & WS_EX_DLGMODALFRAME)
return TRUE;
else if (!(ExStyle & WS_EX_STATICEDGE) && (Style & (WS_DLGFRAME | WS_THICKFRAME)))
return TRUE;
else
return FALSE;
}
static LONG_PTR
co_IntSetWindowLongPtr(HWND hWnd, DWORD Index, LONG_PTR NewValue, BOOL Ansi, ULONG Size, BOOL bAlter)
{
PWND Window, Parent;
PWINSTATION_OBJECT WindowStation;
LONG_PTR OldValue;
STYLESTRUCT Style;
if (!(Window = UserGetWindowObject(hWnd)))
{
return( 0);
}
if ((INT)Index >= 0)
{
if ((Index + Size) > Window->cbwndExtra)
{
EngSetLastError(ERROR_INVALID_INDEX);
return( 0);
}
#ifdef _WIN64
if (Size == sizeof(LONG))
{
OldValue = *((LONG *)((PCHAR)(Window + 1) + Index));
*((LONG*)((PCHAR)(Window + 1) + Index)) = (LONG)NewValue;
}
else
#endif
{
OldValue = *((LONG_PTR *)((PCHAR)(Window + 1) + Index));
/*
if ( Index == DWLP_DLGPROC && Wnd->state & WNDS_DIALOGWINDOW)
{
OldValue = (LONG_PTR)IntSetWindowProc( Wnd, (WNDPROC)NewValue, Ansi);
if (!OldValue) return 0;
}
*/
*((LONG_PTR*)((PCHAR)(Window + 1) + Index)) = NewValue;
}
}
else
{
#ifdef _WIN64
if (Size == sizeof(LONG))
{
if ((Index != GWL_STYLE) &&
(Index != GWL_EXSTYLE) &&
(Index != GWL_ID) &&
(Index != GWL_USERDATA))
{
ERR("NtUserSetWindowLong(): Index requires pointer size: %lu\n", Index);
EngSetLastError(ERROR_INVALID_INDEX);
return 0;
}
}
#endif
switch (Index)
{
case GWL_EXSTYLE: // LONG
OldValue = (LONG) Window->ExStyle;
Style.styleOld = OldValue;
Style.styleNew = NewValue;
co_IntSendMessage(hWnd, WM_STYLECHANGING, GWL_EXSTYLE, (LPARAM) &Style);
/*
* Remove extended window style bit WS_EX_TOPMOST for shell windows.
*/
WindowStation = Window->head.pti->rpdesk->rpwinstaParent;
if(WindowStation)
{
if (hWnd == WindowStation->ShellWindow || hWnd == WindowStation->ShellListView)
Style.styleNew &= ~WS_EX_TOPMOST;
}
/* WS_EX_WINDOWEDGE depends on some other styles */
if (IntCheckFrameEdge(Window->style, NewValue))
Style.styleNew |= WS_EX_WINDOWEDGE;
else
Style.styleNew &= ~WS_EX_WINDOWEDGE;
if (!(Window->ExStyle & WS_EX_LAYERED))
{
SetLayeredStatus(Window, 0);
}
Window->ExStyle = (DWORD)Style.styleNew;
co_IntSendMessage(hWnd, WM_STYLECHANGED, GWL_EXSTYLE, (LPARAM) &Style);
break;
case GWL_STYLE: // LONG
OldValue = (LONG) Window->style;
Style.styleOld = OldValue;
Style.styleNew = NewValue;
if (!bAlter)
co_IntSendMessage(hWnd, WM_STYLECHANGING, GWL_STYLE, (LPARAM) &Style);
/* WS_CLIPSIBLINGS can't be reset on top-level windows */
if (UserIsDesktopWindow(Window->spwndParent)) Style.styleNew |= WS_CLIPSIBLINGS;
/* WS_MINIMIZE can't be reset */
if (OldValue & WS_MINIMIZE) Style.styleNew |= WS_MINIMIZE;
/* Fixes wine FIXME: changing WS_DLGFRAME | WS_THICKFRAME is supposed to change WS_EX_WINDOWEDGE too */
if (IntCheckFrameEdge(NewValue, Window->ExStyle))
Window->ExStyle |= WS_EX_WINDOWEDGE;
else
Window->ExStyle &= ~WS_EX_WINDOWEDGE;
if ((OldValue & (WS_CHILD | WS_POPUP)) == WS_CHILD)
{
if ((NewValue & (WS_CHILD | WS_POPUP)) != WS_CHILD)
{
//// From child to non-child it should be null already.
ERR("IDMenu going null! %d\n",Window->IDMenu);
Window->IDMenu = 0; // Window->spmenu = 0;
}
}
else
{
if ((NewValue & (WS_CHILD | WS_POPUP)) == WS_CHILD)
{
PMENU pMenu = UserGetMenuObject(UlongToHandle(Window->IDMenu));
Window->state &= ~WNDS_HASMENU;
if (pMenu)
{
ERR("IDMenu released 0x%p\n",pMenu);
// ROS may not hold a lock after setting menu to window. But it should!
//IntReleaseMenuObject(pMenu);
}
}
}
if ((Style.styleOld ^ Style.styleNew) & WS_VISIBLE)
{
if (Style.styleOld & WS_VISIBLE) Window->head.pti->cVisWindows--;
if (Style.styleNew & WS_VISIBLE) Window->head.pti->cVisWindows++;
DceResetActiveDCEs( Window );
}
Window->style = (DWORD)Style.styleNew;
if (!bAlter)
co_IntSendMessage(hWnd, WM_STYLECHANGED, GWL_STYLE, (LPARAM) &Style);
break;
case GWLP_WNDPROC: // LONG_PTR
{
if ( Window->head.pti->ppi != PsGetCurrentProcessWin32Process() ||
Window->fnid & FNID_FREED)
{
EngSetLastError(ERROR_ACCESS_DENIED);
return( 0);
}
OldValue = (LONG_PTR)IntSetWindowProc(Window,
(WNDPROC)NewValue,
Ansi);
break;
}
case GWLP_HINSTANCE: // LONG_PTR
OldValue = (LONG_PTR) Window->hModule;
Window->hModule = (HINSTANCE) NewValue;
break;
case GWLP_HWNDPARENT: // LONG_PTR
Parent = Window->spwndParent;
if (Parent && (Parent->head.h == IntGetDesktopWindow()))
OldValue = (LONG_PTR) IntSetOwner(Window->head.h, (HWND) NewValue);
else
OldValue = (LONG_PTR) co_UserSetParent(Window->head.h, (HWND) NewValue);
break;
case GWLP_ID: // LONG
OldValue = (LONG) Window->IDMenu;
Window->IDMenu = (UINT) NewValue;
break;
case GWLP_USERDATA: // LONG or LONG_PTR
OldValue = Window->dwUserData;
Window->dwUserData = NewValue;
break;
default:
ERR("NtUserSetWindowLong(): Unsupported index %lu\n", Index);
EngSetLastError(ERROR_INVALID_INDEX);
OldValue = 0;
break;
}
}
return( OldValue);
}
LONG FASTCALL
co_UserSetWindowLong(HWND hWnd, DWORD Index, LONG NewValue, BOOL Ansi)
{
return (LONG)co_IntSetWindowLongPtr(hWnd, Index, NewValue, Ansi, sizeof(LONG), FALSE);
}
LONG_PTR FASTCALL
co_UserSetWindowLongPtr(HWND hWnd, DWORD Index, LONG_PTR NewValue, BOOL Ansi)
{
return co_IntSetWindowLongPtr(hWnd, Index, NewValue, Ansi, sizeof(LONG_PTR), FALSE);
}
/*
* NtUserSetWindowLong
*
* The NtUserSetWindowLong function changes an attribute of the specified
* window. The function also sets the 32-bit (long) value at the specified
* offset into the extra window memory.
*
* Status
* @implemented
*/
LONG APIENTRY
NtUserSetWindowLong(HWND hWnd, DWORD Index, LONG NewValue, BOOL Ansi)
{
LONG ret;
UserEnterExclusive();
if (hWnd == IntGetDesktopWindow())
{
EngSetLastError(STATUS_ACCESS_DENIED);
UserLeave();
return 0;
}
ret = (LONG)co_IntSetWindowLongPtr(hWnd, Index, NewValue, Ansi, sizeof(LONG), FALSE);
UserLeave();
return ret;
}
#ifdef _WIN64
LONG_PTR APIENTRY
NtUserSetWindowLongPtr(HWND hWnd, DWORD Index, LONG_PTR NewValue, BOOL Ansi)
{
LONG_PTR ret;
UserEnterExclusive();
if (hWnd == IntGetDesktopWindow())
{
EngSetLastError(STATUS_ACCESS_DENIED);
UserLeave();
return 0;
}
ret = co_IntSetWindowLongPtr(hWnd, Index, NewValue, Ansi, sizeof(LONG_PTR), FALSE);
UserLeave();
return ret;
}
#endif // _WIN64
DWORD APIENTRY
NtUserAlterWindowStyle(HWND hWnd, DWORD Index, LONG NewValue)
{
LONG ret;
UserEnterExclusive();
if (hWnd == IntGetDesktopWindow())
{
EngSetLastError(STATUS_ACCESS_DENIED);
UserLeave();
return 0;
}
ret = co_IntSetWindowLongPtr(hWnd, Index, NewValue, FALSE, sizeof(LONG), TRUE);
UserLeave();
return ret;
}
/*
* NtUserSetWindowWord
*
* Legacy function similar to NtUserSetWindowLong.
*
* Status
* @implemented
*/
WORD APIENTRY
NtUserSetWindowWord(HWND hWnd, INT Index, WORD NewValue)
{
PWND Window;
WORD OldValue;
DECLARE_RETURN(WORD);
TRACE("Enter NtUserSetWindowWord\n");
UserEnterExclusive();
if (hWnd == IntGetDesktopWindow())
{
EngSetLastError(STATUS_ACCESS_DENIED);
RETURN( 0);
}
if (!(Window = UserGetWindowObject(hWnd)))
{
RETURN( 0);
}
switch (Index)
{
case GWL_ID:
case GWL_HINSTANCE:
case GWL_HWNDPARENT:
RETURN( (WORD)co_UserSetWindowLong(UserHMGetHandle(Window), Index, (UINT)NewValue, TRUE));
default:
if (Index < 0)
{
EngSetLastError(ERROR_INVALID_INDEX);
RETURN( 0);
}
}
if ((ULONG)Index > (Window->cbwndExtra - sizeof(WORD)))
{
EngSetLastError(ERROR_INVALID_INDEX);
RETURN( 0);
}
OldValue = *((WORD *)((PCHAR)(Window + 1) + Index));
*((WORD *)((PCHAR)(Window + 1) + Index)) = NewValue;
RETURN( OldValue);
CLEANUP:
TRACE("Leave NtUserSetWindowWord, ret=%u\n", _ret_);
UserLeave();
END_CLEANUP;
}
/*
QueryWindow based on KJK::Hyperion and James Tabor.
0 = QWUniqueProcessId
1 = QWUniqueThreadId
2 = QWActiveWindow
3 = QWFocusWindow
4 = QWIsHung Implements IsHungAppWindow found
by KJK::Hyperion.
9 = QWKillWindow When I called this with hWnd ==
DesktopWindow, it shutdown the system
and rebooted.
*/
/*
* @implemented
*/
DWORD_PTR APIENTRY
NtUserQueryWindow(HWND hWnd, DWORD Index)
{
/* Console Leader Process CID Window offsets */
#define GWLP_CONSOLE_LEADER_PID 0
#define GWLP_CONSOLE_LEADER_TID 4
DWORD_PTR Result;
PWND pWnd, pwndActive;
PTHREADINFO pti, ptiActive;
DECLARE_RETURN(UINT);
TRACE("Enter NtUserQueryWindow\n");
UserEnterShared();
if (!(pWnd = UserGetWindowObject(hWnd)))
{
RETURN( 0);
}
switch(Index)
{
case QUERY_WINDOW_UNIQUE_PROCESS_ID:
{
if ( (pWnd->head.pti->TIF_flags & TIF_CSRSSTHREAD) &&
(pWnd->pcls->atomClassName == gaGuiConsoleWndClass) )
{
// IntGetWindowLong(offset == GWLP_CONSOLE_LEADER_PID)
Result = (DWORD_PTR)(*((LONG_PTR*)((PCHAR)(pWnd + 1) + GWLP_CONSOLE_LEADER_PID)));
}
else
{
Result = (DWORD_PTR)IntGetWndProcessId(pWnd);
}
break;
}
case QUERY_WINDOW_UNIQUE_THREAD_ID:
{
if ( (pWnd->head.pti->TIF_flags & TIF_CSRSSTHREAD) &&
(pWnd->pcls->atomClassName == gaGuiConsoleWndClass) )
{
// IntGetWindowLong(offset == GWLP_CONSOLE_LEADER_TID)
Result = (DWORD_PTR)(*((LONG_PTR*)((PCHAR)(pWnd + 1) + GWLP_CONSOLE_LEADER_TID)));
}
else
{
Result = (DWORD_PTR)IntGetWndThreadId(pWnd);
}
break;
}
case QUERY_WINDOW_ACTIVE:
Result = (DWORD_PTR)(pWnd->head.pti->MessageQueue->spwndActive ? UserHMGetHandle(pWnd->head.pti->MessageQueue->spwndActive) : 0);
break;
case QUERY_WINDOW_FOCUS:
Result = (DWORD_PTR)(pWnd->head.pti->MessageQueue->spwndFocus ? UserHMGetHandle(pWnd->head.pti->MessageQueue->spwndFocus) : 0);
break;
case QUERY_WINDOW_ISHUNG:
Result = (pWnd->fnid == FNID_GHOST) || MsqIsHung(pWnd->head.pti, MSQ_HUNG);
break;
case QUERY_WINDOW_REAL_ID:
Result = (DWORD_PTR)pWnd->head.pti->pEThread->Cid.UniqueProcess;
break;
case QUERY_WINDOW_FOREGROUND:
Result = (pWnd->head.pti->MessageQueue == gpqForeground);
break;
case QUERY_WINDOW_DEFAULT_IME: /* default IME window */
if (pWnd->head.pti->spwndDefaultIme)
Result = (DWORD_PTR)UserHMGetHandle(pWnd->head.pti->spwndDefaultIme);
else
Result = 0;
break;
case QUERY_WINDOW_DEFAULT_ICONTEXT: /* default input context handle */
if (pWnd->head.pti->spDefaultImc)
Result = (DWORD_PTR)UserHMGetHandle(pWnd->head.pti->spDefaultImc);
else
Result = 0;
break;
case QUERY_WINDOW_ACTIVE_IME:
Result = 0;
if (gpqForeground && gpqForeground->spwndActive)
{
pwndActive = gpqForeground->spwndActive;
pti = PsGetCurrentThreadWin32Thread();
if (pti->rpdesk == pwndActive->head.rpdesk)
{
ptiActive = pwndActive->head.pti;
if (ptiActive->spwndDefaultIme)
Result = (DWORD_PTR)UserHMGetHandle(ptiActive->spwndDefaultIme);
}
}
break;
default:
Result = 0;
break;
}
RETURN( Result);
CLEANUP:
TRACE("Leave NtUserQueryWindow, ret=%u\n", _ret_);
UserLeave();
END_CLEANUP;
}
/*
* @implemented
*/
UINT APIENTRY
NtUserRegisterWindowMessage(PUNICODE_STRING MessageNameUnsafe)
{
UNICODE_STRING SafeMessageName;
NTSTATUS Status;
UINT Ret;
DECLARE_RETURN(UINT);
TRACE("Enter NtUserRegisterWindowMessage\n");
UserEnterExclusive();
if(MessageNameUnsafe == NULL)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
RETURN( 0);
}
Status = IntSafeCopyUnicodeStringTerminateNULL(&SafeMessageName, MessageNameUnsafe);
if(!NT_SUCCESS(Status))
{
SetLastNtError(Status);
RETURN( 0);
}
Ret = (UINT)IntAddAtom(SafeMessageName.Buffer);
if (SafeMessageName.Buffer)
ExFreePoolWithTag(SafeMessageName.Buffer, TAG_STRING);
RETURN( Ret);
CLEANUP:
TRACE("Leave NtUserRegisterWindowMessage, ret=%u\n", _ret_);
UserLeave();
END_CLEANUP;
}
/*
* @implemented
*/
BOOL APIENTRY
NtUserSetWindowFNID(HWND hWnd,
WORD fnID)
{
PWND Wnd;
DECLARE_RETURN(BOOL);
TRACE("Enter NtUserSetWindowFNID\n");
UserEnterExclusive();
if (!(Wnd = UserGetWindowObject(hWnd)))
{
RETURN( FALSE);
}
if (Wnd->head.pti->ppi != PsGetCurrentProcessWin32Process())
{
EngSetLastError(ERROR_ACCESS_DENIED);
RETURN( FALSE);
}
// From user land we only set these.
if (fnID != FNID_DESTROY)
{
/* HACK: The minimum should be FNID_BUTTON, but menu code relies on this */
if (fnID < FNID_FIRST || fnID > FNID_GHOST ||
Wnd->fnid != 0)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
RETURN( FALSE);
}
}
Wnd->fnid |= fnID;
RETURN( TRUE);
CLEANUP:
TRACE("Leave NtUserSetWindowFNID\n");
UserLeave();
END_CLEANUP;
}
BOOL APIENTRY
DefSetText(PWND Wnd, PCWSTR WindowText)
{
UNICODE_STRING UnicodeString;
BOOL Ret = FALSE;
RtlInitUnicodeString(&UnicodeString, WindowText);
if (UnicodeString.Length != 0)
{
if (Wnd->strName.MaximumLength > 0 &&
UnicodeString.Length <= Wnd->strName.MaximumLength - sizeof(UNICODE_NULL))
{
ASSERT(Wnd->strName.Buffer != NULL);
Wnd->strName.Length = UnicodeString.Length;
Wnd->strName.Buffer[UnicodeString.Length / sizeof(WCHAR)] = L'\0';
RtlCopyMemory(Wnd->strName.Buffer,
UnicodeString.Buffer,
UnicodeString.Length);
}
else
{
PWCHAR buf;
Wnd->strName.MaximumLength = Wnd->strName.Length = 0;
buf = Wnd->strName.Buffer;
Wnd->strName.Buffer = NULL;
if (buf != NULL)
{
DesktopHeapFree(Wnd->head.rpdesk, buf);
}
Wnd->strName.Buffer = DesktopHeapAlloc(Wnd->head.rpdesk,
UnicodeString.Length + sizeof(UNICODE_NULL));
if (Wnd->strName.Buffer != NULL)
{
Wnd->strName.Buffer[UnicodeString.Length / sizeof(WCHAR)] = L'\0';
RtlCopyMemory(Wnd->strName.Buffer,
UnicodeString.Buffer,
UnicodeString.Length);
Wnd->strName.MaximumLength = UnicodeString.Length + sizeof(UNICODE_NULL);
Wnd->strName.Length = UnicodeString.Length;
}
else
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto Exit;
}
}
}
else
{
Wnd->strName.Length = 0;
if (Wnd->strName.Buffer != NULL)
Wnd->strName.Buffer[0] = L'\0';
}
// FIXME: HAX! Windows does not do this in here!
// In User32, these are called after: NotifyWinEvent EVENT_OBJECT_NAMECHANGE than
// RepaintButton, StaticRepaint, NtUserCallHwndLock HWNDLOCK_ROUTINE_REDRAWFRAMEANDHOOK, etc.
/* Send shell notifications */
if (!Wnd->spwndOwner && !IntGetParent(Wnd))
{
co_IntShellHookNotify(HSHELL_REDRAW, (WPARAM) UserHMGetHandle(Wnd), FALSE); // FIXME Flashing?
}
Ret = TRUE;
Exit:
if (UnicodeString.Buffer) RtlFreeUnicodeString(&UnicodeString);
return Ret;
}
/*
* NtUserDefSetText
*
* Undocumented function that is called from DefWindowProc to set
* window text.
*
* Status
* @implemented
*/
BOOL APIENTRY
NtUserDefSetText(HWND hWnd, PLARGE_STRING WindowText)
{
PWND Wnd;
LARGE_STRING SafeText;
UNICODE_STRING UnicodeString;
BOOL Ret = TRUE;
TRACE("Enter NtUserDefSetText\n");
if (WindowText != NULL)
{
_SEH2_TRY
{
SafeText = ProbeForReadLargeString(WindowText);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Ret = FALSE;
SetLastNtError(_SEH2_GetExceptionCode());
}
_SEH2_END;
if (!Ret)
return FALSE;
}
else
return TRUE;
UserEnterExclusive();
if(!(Wnd = UserGetWindowObject(hWnd)))
{
UserLeave();
return FALSE;
}
// ReactOS uses Unicode and not mixed. Up/Down converting will take time.
// Brought to you by: The Wine Project! Dysfunctional Thought Processes!
// Now we know what the bAnsi is for.
RtlInitUnicodeString(&UnicodeString, NULL);
if (SafeText.Buffer)
{
_SEH2_TRY
{
if (SafeText.bAnsi)
ProbeForRead(SafeText.Buffer, SafeText.Length, sizeof(CHAR));
else
ProbeForRead(SafeText.Buffer, SafeText.Length, sizeof(WCHAR));
Ret = RtlLargeStringToUnicodeString(&UnicodeString, &SafeText);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Ret = FALSE;
SetLastNtError(_SEH2_GetExceptionCode());
}
_SEH2_END;
if (!Ret) goto Exit;
}
if (UnicodeString.Length != 0)
{
if (Wnd->strName.MaximumLength > 0 &&
UnicodeString.Length <= Wnd->strName.MaximumLength - sizeof(UNICODE_NULL))
{
ASSERT(Wnd->strName.Buffer != NULL);
Wnd->strName.Length = UnicodeString.Length;
Wnd->strName.Buffer[UnicodeString.Length / sizeof(WCHAR)] = L'\0';
RtlCopyMemory(Wnd->strName.Buffer,
UnicodeString.Buffer,
UnicodeString.Length);
}
else
{
PWCHAR buf;
Wnd->strName.MaximumLength = Wnd->strName.Length = 0;
buf = Wnd->strName.Buffer;
Wnd->strName.Buffer = NULL;
if (buf != NULL)
{
DesktopHeapFree(Wnd->head.rpdesk, buf);
}
Wnd->strName.Buffer = DesktopHeapAlloc(Wnd->head.rpdesk,
UnicodeString.Length + sizeof(UNICODE_NULL));
if (Wnd->strName.Buffer != NULL)
{
Wnd->strName.Buffer[UnicodeString.Length / sizeof(WCHAR)] = L'\0';
RtlCopyMemory(Wnd->strName.Buffer,
UnicodeString.Buffer,
UnicodeString.Length);
Wnd->strName.MaximumLength = UnicodeString.Length + sizeof(UNICODE_NULL);
Wnd->strName.Length = UnicodeString.Length;
}
else
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
Ret = FALSE;
goto Exit;
}
}
}
else
{
Wnd->strName.Length = 0;
if (Wnd->strName.Buffer != NULL)
Wnd->strName.Buffer[0] = L'\0';
}
// FIXME: HAX! Windows does not do this in here!
// In User32, these are called after: NotifyWinEvent EVENT_OBJECT_NAMECHANGE than
// RepaintButton, StaticRepaint, NtUserCallHwndLock HWNDLOCK_ROUTINE_REDRAWFRAMEANDHOOK, etc.
/* Send shell notifications */
if (!Wnd->spwndOwner && !IntGetParent(Wnd))
{
co_IntShellHookNotify(HSHELL_REDRAW, (WPARAM) hWnd, FALSE); // FIXME Flashing?
}
Ret = TRUE;
Exit:
if (UnicodeString.Buffer) RtlFreeUnicodeString(&UnicodeString);
TRACE("Leave NtUserDefSetText, ret=%i\n", Ret);
UserLeave();
return Ret;
}
/*
* NtUserInternalGetWindowText
*
* Status
* @implemented
*/
INT APIENTRY
NtUserInternalGetWindowText(HWND hWnd, LPWSTR lpString, INT nMaxCount)
{
PWND Wnd;
NTSTATUS Status;
INT Result;
DECLARE_RETURN(INT);
TRACE("Enter NtUserInternalGetWindowText\n");
UserEnterShared();
if(lpString && (nMaxCount <= 1))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
RETURN( 0);
}
if(!(Wnd = UserGetWindowObject(hWnd)))
{
RETURN( 0);
}
Result = Wnd->strName.Length / sizeof(WCHAR);
if(lpString)
{
const WCHAR Terminator = L'\0';
INT Copy;
WCHAR *Buffer = (WCHAR*)lpString;
Copy = min(nMaxCount - 1, Result);
if(Copy > 0)
{
Status = MmCopyToCaller(Buffer, Wnd->strName.Buffer, Copy * sizeof(WCHAR));
if(!NT_SUCCESS(Status))
{
SetLastNtError(Status);
RETURN( 0);
}
Buffer += Copy;
}
Status = MmCopyToCaller(Buffer, &Terminator, sizeof(WCHAR));
if(!NT_SUCCESS(Status))
{
SetLastNtError(Status);
RETURN( 0);
}
Result = Copy;
}
RETURN( Result);
CLEANUP:
TRACE("Leave NtUserInternalGetWindowText, ret=%i\n",_ret_);
UserLeave();
END_CLEANUP;
}
/*
API Call
*/
BOOL
FASTCALL
IntShowOwnedPopups(PWND OwnerWnd, BOOL fShow )
{
int count = 0;
PWND pWnd;
HWND *win_array;
// ASSERT(OwnerWnd);
TRACE("Enter ShowOwnedPopups Show: %s\n", (fShow ? "TRUE" : "FALSE"));
/* NOTE: Popups are not children */
win_array = IntWinListOwnedPopups(OwnerWnd);
if (!win_array)
return TRUE;
while (win_array[count])
count++;
while (--count >= 0)
{
if (!(pWnd = ValidateHwndNoErr( win_array[count] )))
continue;
ASSERT(pWnd->spwndOwner == OwnerWnd);
if (fShow)
{
if (pWnd->state & WNDS_HIDDENPOPUP)
{
/* In Windows, ShowOwnedPopups(TRUE) generates
* WM_SHOWWINDOW messages with SW_PARENTOPENING,
* regardless of the state of the owner
*/
co_IntSendMessage(win_array[count], WM_SHOWWINDOW, SW_SHOWNORMAL, SW_PARENTOPENING);
pWnd->state &= ~WNDS_HIDDENPOPUP;
continue;
}
}
else
{
if (pWnd->style & WS_VISIBLE)
{
/* In Windows, ShowOwnedPopups(FALSE) generates
* WM_SHOWWINDOW messages with SW_PARENTCLOSING,
* regardless of the state of the owner
*/
co_IntSendMessage(win_array[count], WM_SHOWWINDOW, SW_HIDE, SW_PARENTCLOSING);
pWnd->state |= WNDS_HIDDENPOPUP;
continue;
}
}
}
ExFreePoolWithTag(win_array, USERTAG_WINDOWLIST);
TRACE("Leave ShowOwnedPopups\n");
return TRUE;
}
/* EOF */