You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1011 lines
32 KiB
1011 lines
32 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: getset.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains window manager information routines
|
|
*
|
|
* History:
|
|
* 22-Oct-1990 MikeHar Ported functions from Win 3.0 sources.
|
|
* 13-Feb-1991 MikeKe Added Revalidation code (None)
|
|
* 08-Feb-1991 IanJa Unicode/ANSI aware and neutral
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
/****************************************************************************\
|
|
* DefSetText
|
|
*
|
|
* Processes WM_SETTEXT messages by text-alloc'ing a string in the alternate
|
|
* ds and setting 'hwnd->hName' to it's handle.
|
|
*
|
|
* History:
|
|
* 23-Oct-1990 MikeHar Ported from Windows.
|
|
* 09-Nov-1990 DarrinM Cleanup.
|
|
\****************************************************************************/
|
|
BOOL DefSetText(
|
|
PWND pwnd,
|
|
PLARGE_STRING cczpstr)
|
|
{
|
|
/*
|
|
* Note -- string buffer may be on client side.
|
|
*/
|
|
PDESKTOP pdesk;
|
|
DWORD cbString;
|
|
BOOL fTranslateOk;
|
|
|
|
if (pwnd->head.rpdesk == NULL || cczpstr == NULL || cczpstr->Buffer == NULL) {
|
|
pwnd->strName.Length = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Capture the new window name
|
|
*/
|
|
if (cczpstr->bAnsi)
|
|
cbString = (cczpstr->Length + 1) * sizeof(WCHAR);
|
|
else
|
|
cbString = cczpstr->Length + sizeof(WCHAR);
|
|
|
|
/*
|
|
* If the current buffer is not large enough,
|
|
* reallocate it.
|
|
*/
|
|
pdesk = pwnd->head.rpdesk;
|
|
if (pwnd->strName.MaximumLength < cbString) {
|
|
if (pwnd->strName.Buffer != NULL)
|
|
DesktopFree(pdesk, pwnd->strName.Buffer);
|
|
pwnd->strName.Buffer = (LPWSTR)DesktopAlloc(pdesk, cbString, DTAG_TEXT);
|
|
pwnd->strName.Length = 0;
|
|
if (pwnd->strName.Buffer == NULL) {
|
|
pwnd->strName.MaximumLength = 0;
|
|
return FALSE;
|
|
}
|
|
pwnd->strName.MaximumLength = cbString;
|
|
}
|
|
|
|
fTranslateOk = TRUE;
|
|
if (cczpstr->Length != 0) {
|
|
try {
|
|
if (!cczpstr->bAnsi) {
|
|
RtlCopyMemory(pwnd->strName.Buffer, cczpstr->Buffer, cbString);
|
|
} else {
|
|
LPCSTR ccxpszAnsi = (LPCSTR)cczpstr->Buffer;
|
|
|
|
fTranslateOk = NT_SUCCESS(RtlMultiByteToUnicodeN(pwnd->strName.Buffer,
|
|
cbString, &cbString,
|
|
(LPSTR)ccxpszAnsi, cbString / sizeof(WCHAR)));
|
|
}
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
pwnd->strName.Length = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (fTranslateOk) {
|
|
pwnd->strName.Length = cbString - sizeof(WCHAR);
|
|
return TRUE;
|
|
} else {
|
|
pwnd->strName.Length = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FCallerOk
|
|
*
|
|
* Ensures that no client stomps on server windows.
|
|
*
|
|
* 04-Feb-1992 ScottLu Created.
|
|
\***************************************************************************/
|
|
BOOL FCallerOk(
|
|
PWND pwnd)
|
|
{
|
|
PTHREADINFO pti = PtiCurrent();
|
|
|
|
if ((GETPTI(pwnd)->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)) &&
|
|
!(pti->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD))) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (PsGetThreadProcessId(GETPTI(pwnd)->pEThread) == gpidLogon &&
|
|
PsGetThreadProcessId(pti->pEThread) != gpidLogon) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _SetWindowWord (supports SetWindowWordA/W API)
|
|
*
|
|
* Set a window word. Positive index values set application window words
|
|
* while negative index values set system window words. The negative
|
|
* indices are published in WINDOWS.H.
|
|
*
|
|
* History:
|
|
* 26-Nov-1990 DarrinM Wrote.
|
|
\***************************************************************************/
|
|
WORD _SetWindowWord(
|
|
PWND pwnd,
|
|
int index,
|
|
WORD value)
|
|
{
|
|
WORD wOld;
|
|
|
|
/*
|
|
* Don't allow setting of words belonging to a system thread if the
|
|
* caller is not a system thread. Same goes for winlogon.
|
|
*/
|
|
if (!FCallerOk(pwnd)) {
|
|
RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, "");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Applications can not set a WORD into a dialog Proc or any of the
|
|
* non-public reserved bytes in DLGWINDOWEXTRA (usersrv stores pointers
|
|
* there).
|
|
*/
|
|
if (TestWF(pwnd, WFDIALOGWINDOW)) {
|
|
if (((index >= DWLP_DLGPROC) && (index < DWLP_MSGRESULT)) ||
|
|
((index > DWLP_USER+sizeof(LONG_PTR)-sizeof(WORD)) && (index < DLGWINDOWEXTRA))) {
|
|
RIPERR3(ERROR_INVALID_INDEX, RIP_WARNING,
|
|
"SetWindowWord: Trying to set WORD of a windowproc pwnd=(%#p) index=(%ld) fnid (%lX)",
|
|
pwnd, index, (DWORD)pwnd->fnid);
|
|
return 0;
|
|
} else {
|
|
|
|
/*
|
|
* If this is really a dialog and not some other server class
|
|
* where usersrv has stored some data (Windows Compuserve -
|
|
* wincim - does this) then store the data now that we have
|
|
* verified the index limits.
|
|
*/
|
|
if (GETFNID(pwnd) == FNID_DIALOG) {
|
|
goto DoSetWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (index == GWLP_USERDATA) {
|
|
wOld = (WORD)pwnd->dwUserData;
|
|
pwnd->dwUserData = MAKELONG(value, HIWORD(pwnd->dwUserData));
|
|
return wOld;
|
|
}
|
|
|
|
// fix for RedShift, they call SetWindowWord
|
|
// tn play with the low word of the style dword
|
|
if (index == GWL_STYLE) {
|
|
wOld = (WORD)pwnd->style;
|
|
pwnd->style = MAKELONG(value, HIWORD(pwnd->style));
|
|
return wOld;
|
|
}
|
|
|
|
if (GETFNID(pwnd) != 0) {
|
|
if (index >= 0 &&
|
|
(index < (int)(CBFNID(pwnd->fnid)-sizeof(WND)))) {
|
|
switch (GETFNID(pwnd)) {
|
|
case FNID_MDICLIENT:
|
|
if (index == 0)
|
|
break;
|
|
goto DoDefault;
|
|
|
|
case FNID_BUTTON:
|
|
/*
|
|
* CorelDraw, Direct Access 1.0 and WordPerfect 6.0 do a
|
|
* get/set on the first button window word. Allow this
|
|
* for compatibility.
|
|
*/
|
|
if (index == 0) {
|
|
/*
|
|
* Since we now use a lookaside buffer for the control's
|
|
* private data, we need to indirect into this structure.
|
|
*/
|
|
PBUTN pbutn = ((PBUTNWND)pwnd)->pbutn;
|
|
if (!pbutn || (LONG_PTR)pbutn == (LONG_PTR)-1) {
|
|
return 0;
|
|
} else {
|
|
try {
|
|
wOld = (WORD)ProbeAndReadUlong(&pbutn->buttonState);
|
|
pbutn->buttonState = value;
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
wOld = 0;
|
|
}
|
|
return wOld;
|
|
}
|
|
}
|
|
goto DoDefault;
|
|
|
|
default:
|
|
DoDefault:
|
|
RIPERR3(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetWindowWord: Trying to set private server data pwnd=(%#p) index=(%ld) fnid (%lX)",
|
|
pwnd, index, (DWORD)pwnd->fnid);
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DoSetWord:
|
|
if ((index < 0) || ((UINT)index + sizeof(WORD) > (UINT)pwnd->cbwndExtra)) {
|
|
RIPERR0(ERROR_INVALID_INDEX, RIP_WARNING,"SetWindowWord Fails because of invalid index");
|
|
return 0;
|
|
} else {
|
|
WORD UNALIGNED *pw;
|
|
|
|
pw = (WORD UNALIGNED *)((BYTE *)(pwnd + 1) + index);
|
|
wOld = *pw;
|
|
*pw = value;
|
|
return (WORD)wOld;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxSetWindowLong (API)
|
|
*
|
|
* Set a window long. Positive index values set application window longs
|
|
* while negative index values set system window longs. The negative
|
|
* indices are published in WINDOWS.H.
|
|
*
|
|
* History:
|
|
* 26-Nov-1990 DarrinM Wrote.
|
|
\***************************************************************************/
|
|
ULONG_PTR xxxSetWindowLongPtr(
|
|
PWND pwnd,
|
|
int index,
|
|
ULONG_PTR dwData,
|
|
BOOL bAnsi)
|
|
{
|
|
ULONG_PTR dwOld;
|
|
|
|
/*
|
|
* The only case that leaves the critical section is where
|
|
* xxxSetWindowData is called, which ensures that the window is locked.
|
|
* This saves us some locks.
|
|
*
|
|
* Don't allow setting of words belonging to a system thread if the
|
|
* caller is not a system thread. Same goes for winlogon.
|
|
*/
|
|
if (!FCallerOk(pwnd)) {
|
|
RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, "");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If it's a dialog window, only a few indices are permitted.
|
|
*/
|
|
if (GETFNID(pwnd) != 0) {
|
|
if (TestWF(pwnd, WFDIALOGWINDOW)) {
|
|
switch (index) {
|
|
case DWLP_MSGRESULT:
|
|
dwOld = (ULONG_PTR)((PDIALOG)(pwnd))->resultWP;
|
|
((PDIALOG)(pwnd))->resultWP = (LONG_PTR)dwData;
|
|
return dwOld;
|
|
|
|
case DWLP_USER:
|
|
dwOld = (ULONG_PTR)((PDIALOG)(pwnd))->unused;
|
|
((PDIALOG)(pwnd))->unused = (LONG_PTR)dwData;
|
|
return dwOld;
|
|
|
|
default:
|
|
if (index >= 0 && index < DLGWINDOWEXTRA) {
|
|
RIPERR0(ERROR_PRIVATE_DIALOG_INDEX, RIP_VERBOSE, "");
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
if (index >= 0 && index < (int)(CBFNID(pwnd->fnid)-sizeof(WND))) {
|
|
switch (GETFNID(pwnd)) {
|
|
case FNID_BUTTON:
|
|
case FNID_COMBOBOX:
|
|
case FNID_COMBOLISTBOX:
|
|
case FNID_DIALOG:
|
|
case FNID_LISTBOX:
|
|
case FNID_STATIC:
|
|
case FNID_EDIT:
|
|
#ifdef FE_IME
|
|
case FNID_IME:
|
|
#endif
|
|
/*
|
|
* Allow the 0 index for controls to be set if it's
|
|
* still NULL or the window is being destroyed. This
|
|
* is where controls store their private data.
|
|
*/
|
|
if (index == 0) {
|
|
dwOld = *((PULONG_PTR)(pwnd + 1));
|
|
if (dwOld == 0 || TestWF(pwnd, WFDESTROYED))
|
|
goto SetData;
|
|
}
|
|
break;
|
|
|
|
case FNID_MDICLIENT:
|
|
/*
|
|
* Allow the 0 index (which is reserved) to be set/get.
|
|
* Quattro Pro 1.0 uses this index!
|
|
*
|
|
* Allow the 4 index to be set if it's still NULL or
|
|
* the window is being destroyed. This is where we
|
|
* store our private data.
|
|
*/
|
|
#ifndef _WIN64
|
|
if (index == 0) {
|
|
goto SetData;
|
|
}
|
|
#endif
|
|
if (index == GWLP_MDIDATA) {
|
|
dwOld = *((PULONG_PTR)(pwnd + 1));
|
|
if (dwOld == 0 || TestWF(pwnd, WFDESTROYED))
|
|
goto SetData;
|
|
}
|
|
break;
|
|
}
|
|
|
|
RIPERR3(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetWindowLongPtr: Trying to set private server data pwnd=(%#p) index=(%ld) FNID=(%lX)",
|
|
pwnd, index, (DWORD)pwnd->fnid);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (index < 0) {
|
|
return xxxSetWindowData(pwnd, index, dwData, bAnsi);
|
|
} else {
|
|
if ((UINT)index + sizeof(ULONG_PTR) > (UINT)pwnd->cbwndExtra) {
|
|
RIPERR3(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetWindowLongPtr: Index %d too big for cbWndExtra %d on pwnd %#p",
|
|
index, pwnd->cbwndExtra, pwnd);
|
|
return 0;
|
|
} else {
|
|
ULONG_PTR UNALIGNED *pudw;
|
|
|
|
SetData:
|
|
pudw = (ULONG_PTR UNALIGNED *)((BYTE *)(pwnd + 1) + index);
|
|
dwOld = *pudw;
|
|
*pudw = dwData;
|
|
return dwOld;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
DWORD xxxSetWindowLong(
|
|
PWND pwnd,
|
|
int index,
|
|
DWORD dwData,
|
|
BOOL bAnsi)
|
|
{
|
|
DWORD dwOld;
|
|
|
|
/*
|
|
* The only case that leaves the critical section is where we call
|
|
* xxxSetWindowData, which checks that the window is locked. This saves
|
|
* us some locks.
|
|
*
|
|
* Don't allow setting of words belonging to a system thread if the
|
|
* caller is not a system thread. Same goes for winlogon.
|
|
*/
|
|
if (!FCallerOk(pwnd)) {
|
|
RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, "");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If it's a dialog window, only a few indices are permitted.
|
|
*/
|
|
if (GETFNID(pwnd) != 0) {
|
|
if (TestWF(pwnd, WFDIALOGWINDOW)) {
|
|
switch (index) {
|
|
case DWLP_MSGRESULT:
|
|
dwOld = (DWORD)((PDIALOG)(pwnd))->resultWP;
|
|
((PDIALOG)(pwnd))->resultWP = (long)dwData;
|
|
return dwOld;
|
|
|
|
case DWLP_USER:
|
|
dwOld = (DWORD)((PDIALOG)(pwnd))->unused;
|
|
((PDIALOG)(pwnd))->unused = (long)dwData;
|
|
return dwOld;
|
|
|
|
default:
|
|
if (index >= 0 && index < DLGWINDOWEXTRA) {
|
|
RIPERR0(ERROR_PRIVATE_DIALOG_INDEX, RIP_VERBOSE, "");
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
if (index >= 0 &&
|
|
(index < (int)(CBFNID(pwnd->fnid)-sizeof(WND)))) {
|
|
switch (GETFNID(pwnd)) {
|
|
case FNID_MDICLIENT:
|
|
/*
|
|
* Allow the 0 index (which is reserved) to be set/get.
|
|
* Quattro Pro 1.0 uses this index!
|
|
*/
|
|
if (index == 0) {
|
|
goto SetData;
|
|
}
|
|
|
|
/*
|
|
* Allow the 4 index to be set if it's still NULL or
|
|
* the window is being destroyed. This is where we
|
|
* store our private data.
|
|
*/
|
|
if (index == GWLP_MDIDATA) {
|
|
dwOld = *((PDWORD)(pwnd + 1));
|
|
if (dwOld == 0 || TestWF(pwnd, WFDESTROYED))
|
|
goto SetData;
|
|
}
|
|
break;
|
|
}
|
|
|
|
RIPERR3(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetWindowLong: Trying to set private server data pwnd=(%#p) index=(%ld) FNID=(%lX)",
|
|
pwnd, index, (DWORD)pwnd->fnid);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (index < 0) {
|
|
if ((index != GWL_STYLE) && (index != GWL_EXSTYLE) && (index != GWL_ID) && (index != GWLP_USERDATA)) {
|
|
RIPERR1(ERROR_INVALID_INDEX, RIP_WARNING, "SetWindowLong: invalid index %d", index);
|
|
return 0;
|
|
}
|
|
return (DWORD)xxxSetWindowData(pwnd, index, dwData, bAnsi);
|
|
} else {
|
|
if ((UINT)index + sizeof(DWORD) > (UINT)pwnd->cbwndExtra) {
|
|
RIPERR3(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetWindowLong: Index %d too big for cbWndExtra %d on pwnd %#p",
|
|
index, pwnd->cbwndExtra, pwnd);
|
|
return 0;
|
|
} else {
|
|
DWORD UNALIGNED *pudw;
|
|
|
|
SetData:
|
|
pudw = (DWORD UNALIGNED *)((BYTE *)(pwnd + 1) + index);
|
|
dwOld = *pudw;
|
|
*pudw = dwData;
|
|
return dwOld;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************\
|
|
* xxxHandleOwnerSwitch
|
|
*
|
|
\***************************************************************************/
|
|
VOID xxxHandleOwnerSwitch(
|
|
PWND pwnd,
|
|
PWND pwndNewParent,
|
|
PWND pwndOldParent)
|
|
{
|
|
CheckLock(pwnd);
|
|
CheckLock(pwndNewParent);
|
|
CheckLock(pwndOldParent);
|
|
|
|
if (pwndOldParent != NULL && GETPTI(pwndOldParent) != GETPTI(pwnd)) {
|
|
/*
|
|
* See if it needs to be unattached.
|
|
*/
|
|
if (pwndNewParent == NULL ||
|
|
GETPTI(pwndNewParent) == GETPTI(pwnd) ||
|
|
GETPTI(pwndNewParent) != GETPTI(pwndOldParent)) {
|
|
zzzAttachThreadInput(GETPTI(pwnd), GETPTI(pwndOldParent), FALSE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* See if it needs to be attached.
|
|
*/
|
|
if (pwndNewParent != NULL &&
|
|
GETPTI(pwndNewParent) != GETPTI(pwnd) &&
|
|
(pwndOldParent == NULL ||
|
|
GETPTI(pwndNewParent) != GETPTI(pwndOldParent))) {
|
|
zzzAttachThreadInput(GETPTI(pwnd), GETPTI(pwndNewParent), TRUE);
|
|
}
|
|
|
|
/*
|
|
* Post hook messages for tray-windows.
|
|
*/
|
|
if (IsTrayWindow(pwnd)) {
|
|
HWND hwnd = PtoH(pwnd);
|
|
|
|
/*
|
|
* If we're setting the owner and it's changing from owned to
|
|
* unowned or vice-versa, notify the tray.
|
|
*/
|
|
if (pwndOldParent != NULL && pwndNewParent == NULL) {
|
|
xxxCallHook(HSHELL_WINDOWCREATED,
|
|
(WPARAM)hwnd,
|
|
(LONG)0,
|
|
WH_SHELL);
|
|
PostShellHookMessages(HSHELL_WINDOWCREATED, (LPARAM)hwnd);
|
|
} else if (pwndOldParent == NULL && pwndNewParent != NULL) {
|
|
xxxCallHook(HSHELL_WINDOWDESTROYED,
|
|
(WPARAM)hwnd,
|
|
(LONG)0,
|
|
WH_SHELL);
|
|
PostShellHookMessages(HSHELL_WINDOWDESTROYED, (LPARAM)hwnd);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxSetWindowData
|
|
*
|
|
* SetWindowWord and ServerSetWindowLong are now identical routines because they
|
|
* both can return DWORDs. This single routine performs the work for them both.
|
|
*
|
|
* History:
|
|
* 26-Nov-1990 DarrinM Wrote.
|
|
\***************************************************************************/
|
|
|
|
ULONG_PTR xxxSetWindowData(
|
|
PWND pwnd,
|
|
int index,
|
|
ULONG_PTR dwData,
|
|
BOOL bAnsi)
|
|
{
|
|
ULONG_PTR dwT;
|
|
ULONG_PTR dwOld;
|
|
PMENU pmenu;
|
|
PWND *ppwnd;
|
|
PWND pwndNewParent;
|
|
PWND pwndOldParent;
|
|
BOOL fTopOwner;
|
|
TL tlpwndOld;
|
|
TL tlpwndNew;
|
|
DWORD dwCPDType = 0;
|
|
|
|
CheckLock(pwnd);
|
|
UserAssert(IsWinEventNotifyDeferredOK());
|
|
|
|
switch (index) {
|
|
case GWLP_USERDATA:
|
|
dwOld = pwnd->dwUserData;
|
|
pwnd->dwUserData = dwData;
|
|
break;
|
|
|
|
case GWL_EXSTYLE:
|
|
case GWL_STYLE:
|
|
dwOld = xxxSetWindowStyle(pwnd, index, (DWORD)dwData);
|
|
break;
|
|
|
|
case GWLP_ID:
|
|
/*
|
|
* Win95 does a TestWF(pwnd, WFCHILD) here, but we'll do the same
|
|
* check we do everywhere else or it'll cause us trouble.
|
|
*/
|
|
if (TestwndChild(pwnd)) {
|
|
|
|
/*
|
|
* pwnd->spmenu is an id in this case.
|
|
*/
|
|
dwOld = (ULONG_PTR)pwnd->spmenu;
|
|
pwnd->spmenu = (struct tagMENU *)dwData;
|
|
} else {
|
|
dwOld = 0;
|
|
if (pwnd->spmenu != NULL)
|
|
dwOld = (ULONG_PTR)PtoH(pwnd->spmenu);
|
|
|
|
if (dwData == 0) {
|
|
UnlockWndMenu(pwnd, &pwnd->spmenu);
|
|
} else {
|
|
pmenu = ValidateHmenu((HANDLE)dwData);
|
|
if (pmenu != NULL) {
|
|
LockWndMenu(pwnd, &pwnd->spmenu, pmenu);
|
|
} else {
|
|
|
|
/*
|
|
* Menu is invalid, so don't set a new one!
|
|
*/
|
|
dwOld = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GWLP_HINSTANCE:
|
|
dwOld = (ULONG_PTR)pwnd->hModule;
|
|
pwnd->hModule = (HANDLE)dwData;
|
|
break;
|
|
|
|
case GWLP_WNDPROC: // See similar case DWLP_DLGPROC
|
|
|
|
/*
|
|
* Hide the window proc from other processes
|
|
*/
|
|
if (PpiCurrent() != GETPTI(pwnd)->ppi) {
|
|
RIPERR1(ERROR_ACCESS_DENIED, RIP_WARNING,
|
|
"SetWindowLong: Window owned by another process %#p", pwnd);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If the window has been zombized by a DestroyWindow but is still
|
|
* around because the window was locked don't let anyone change
|
|
* the window proc from DefWindowProc!
|
|
*
|
|
* !!! LATER long term move this test into the ValidateHWND; kind of
|
|
* !!! LATER close to shipping for that
|
|
*/
|
|
if (pwnd->fnid & FNID_DELETED_BIT) {
|
|
UserAssert(pwnd->lpfnWndProc == xxxDefWindowProc);
|
|
RIPERR1(ERROR_ACCESS_DENIED, RIP_WARNING,
|
|
"SetWindowLong: Window is a zombie %#p", pwnd);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If the application (client) subclasses a window that has a server -
|
|
* side window proc we must return an address that the client can call:
|
|
* this client-side wndproc expectes Unicode or ANSI depending on bAnsi
|
|
*/
|
|
|
|
if (TestWF(pwnd, WFSERVERSIDEPROC)) {
|
|
dwOld = MapServerToClientPfn((ULONG_PTR)pwnd->lpfnWndProc, bAnsi);
|
|
|
|
/*
|
|
* If we don't have a client side address (like for the DDEMLMon
|
|
* window) then blow off the subclassing.
|
|
*/
|
|
if (dwOld == 0) {
|
|
RIPMSG0(RIP_WARNING, "SetWindowLong: subclass server only window");
|
|
return(0);
|
|
}
|
|
|
|
ClrWF(pwnd, WFSERVERSIDEPROC);
|
|
} else {
|
|
/*
|
|
* Keep edit control behavior compatible with NT 3.51.
|
|
*/
|
|
if (GETFNID(pwnd) == FNID_EDIT) {
|
|
dwOld = (ULONG_PTR)MapKernelClientFnToClientFn(pwnd->lpfnWndProc);
|
|
goto CheckAnsiUnicodeMismatch;
|
|
} else {
|
|
dwOld = MapClientNeuterToClientPfn(pwnd->pcls, (ULONG_PTR)pwnd->lpfnWndProc, bAnsi);
|
|
}
|
|
|
|
/*
|
|
* If the client mapping didn't change the window proc then see if
|
|
* we need a callproc handle.
|
|
*/
|
|
if (dwOld == (ULONG_PTR)pwnd->lpfnWndProc) {
|
|
CheckAnsiUnicodeMismatch:
|
|
/*
|
|
* May need to return a CallProc handle if there is an Ansi/Unicode mismatch
|
|
*/
|
|
if (bAnsi != (TestWF(pwnd, WFANSIPROC) ? TRUE : FALSE)) {
|
|
dwCPDType |= bAnsi ? CPD_ANSI_TO_UNICODE : CPD_UNICODE_TO_ANSI;
|
|
}
|
|
}
|
|
|
|
UserAssert(!ISCPDTAG(dwOld));
|
|
|
|
if (dwCPDType) {
|
|
ULONG_PTR cpd;
|
|
|
|
cpd = GetCPD(pwnd, dwCPDType | CPD_WND, dwOld);
|
|
|
|
if (cpd) {
|
|
dwOld = cpd;
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "SetWindowLong unable to alloc CPD returning handle\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert a possible CallProc Handle into a real address. They may
|
|
* have kept the CallProc Handle from some previous mixed GetClassinfo
|
|
* or SetWindowLong.
|
|
*
|
|
* WARNING bAnsi is modified here to represent real type of
|
|
* proc rather than if SetWindowLongA or W was called
|
|
*
|
|
*/
|
|
if (ISCPDTAG(dwData)) {
|
|
PCALLPROCDATA pCPD;
|
|
if (pCPD = HMValidateHandleNoRip((HANDLE)dwData, TYPE_CALLPROC)) {
|
|
dwData = pCPD->pfnClientPrevious;
|
|
bAnsi = pCPD->wType & CPD_UNICODE_TO_ANSI;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If an app 'unsubclasses' a server-side window proc we need to
|
|
* restore everything so SendMessage and friends know that it's
|
|
* a server-side proc again. Need to check against client side
|
|
* stub addresses.
|
|
*/
|
|
if ((dwT = MapClientToServerPfn(dwData)) != 0) {
|
|
pwnd->lpfnWndProc = (WNDPROC_PWND)dwT;
|
|
SetWF(pwnd, WFSERVERSIDEPROC);
|
|
ClrWF(pwnd, WFANSIPROC);
|
|
} else {
|
|
pwnd->lpfnWndProc = (WNDPROC_PWND)MapClientNeuterToClientPfn(pwnd->pcls, dwData, bAnsi);
|
|
if (bAnsi) {
|
|
SetWF(pwnd, WFANSIPROC);
|
|
} else {
|
|
ClrWF(pwnd, WFANSIPROC);
|
|
}
|
|
|
|
pwnd->hMod16 = xxxClientWOWGetProcModule(pwnd->lpfnWndProc);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GWLP_HWNDPARENT:
|
|
/*
|
|
* Special case for pre-1.1 versions of Windows
|
|
* Set/GetWindowWord(GWW_HWNDPARENT) needs to be mapped
|
|
* to the hwndOwner for top level windows.
|
|
*/
|
|
fTopOwner = FALSE;
|
|
if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
|
|
ppwnd = &pwnd->spwndOwner;
|
|
fTopOwner = TRUE;
|
|
} else {
|
|
ppwnd = &pwnd->spwndParent;
|
|
}
|
|
|
|
|
|
/*
|
|
* If we're a topmost, then we're only changing the owner
|
|
* relationship. Otherwise, we are doing a relinking of the
|
|
* parent/child relationship.
|
|
*/
|
|
pwndOldParent = *ppwnd;
|
|
pwndNewParent = ValidateHwnd((HWND)dwData);
|
|
|
|
if (pwndNewParent == NULL && dwData) {
|
|
RIPERR1(ERROR_INVALID_PARAMETER,
|
|
RIP_WARNING,
|
|
"Set GWL_HWNDPARENT, invalid hwndParent 0x%p",
|
|
dwData);
|
|
return 0;
|
|
}
|
|
|
|
dwOld = (ULONG_PTR)HW(*ppwnd);
|
|
|
|
ThreadLock(pwndNewParent, &tlpwndNew);
|
|
|
|
if (fTopOwner) {
|
|
|
|
ThreadLock(pwndOldParent, &tlpwndOld);
|
|
|
|
xxxHandleOwnerSwitch(pwnd, pwndNewParent, pwndOldParent);
|
|
|
|
if (ValidateOwnerDepth(pwnd, pwndNewParent)) {
|
|
|
|
/*
|
|
* Set the owner.
|
|
*/
|
|
if (pwndNewParent) {
|
|
Lock(ppwnd, pwndNewParent);
|
|
} else {
|
|
Unlock(ppwnd);
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* Undo the switch and set last error.
|
|
*/
|
|
xxxHandleOwnerSwitch(pwnd, pwndOldParent, pwndNewParent);
|
|
RIPERR0(ERROR_INVALID_PARAMETER, RIP_ERROR, "Detected loop in owner chain");
|
|
dwOld = 0;
|
|
}
|
|
|
|
ThreadUnlock(&tlpwndOld);
|
|
} else {
|
|
if (!xxxSetParent(pwnd, pwndNewParent)) {
|
|
dwOld = 0;
|
|
}
|
|
}
|
|
|
|
ThreadUnlock(&tlpwndNew);
|
|
break;
|
|
|
|
default:
|
|
RIPERR1(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetWindowLong: Invalid index 0x%x",
|
|
index);
|
|
return 0;
|
|
}
|
|
|
|
return dwOld;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FindPCPD
|
|
*
|
|
* Searches the list of CallProcData's associated with window to see if
|
|
* one already exists representing this transition. CPD can be re-used
|
|
* and aren't deleted until a window or thread dies
|
|
*
|
|
*
|
|
* 04-Feb-1993 JohnC Created.
|
|
\***************************************************************************/
|
|
PCALLPROCDATA FindPCPD(
|
|
PCALLPROCDATA pCPD,
|
|
ULONG_PTR dwClientPrevious,
|
|
WORD wCPDType)
|
|
{
|
|
while (pCPD) {
|
|
if ((pCPD->pfnClientPrevious == dwClientPrevious) &&
|
|
(pCPD->wType == wCPDType))
|
|
return pCPD;
|
|
pCPD = pCPD->spcpdNext;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetCPD
|
|
*
|
|
* Searches the list of CallProcData's associated with a class or window
|
|
* (if the class is not provided). If one already exists representing this
|
|
* transition it is returned or else a new CPD is created
|
|
*
|
|
* 04-Feb-1993 JohnC Created.
|
|
\***************************************************************************/
|
|
|
|
ULONG_PTR GetCPD(
|
|
PVOID pWndOrCls,
|
|
DWORD CPDOption,
|
|
ULONG_PTR dwProc32)
|
|
{
|
|
PCALLPROCDATA pCPD;
|
|
PCLS pcls;
|
|
#if DBG
|
|
BOOL bAnsiProc;
|
|
#endif
|
|
|
|
PTHREADINFO ptiCurrent;
|
|
|
|
if (CPDOption & (CPD_WND | CPD_DIALOG)) {
|
|
UserAssert(!(CPDOption & (CPD_CLASS | CPD_WNDTOCLS)));
|
|
pcls = ((PWND)pWndOrCls)->pcls;
|
|
|
|
#if DBG
|
|
if (CPDOption & CPD_WND) {
|
|
bAnsiProc = !!(TestWF(pWndOrCls, WFANSIPROC));
|
|
} else {
|
|
/*
|
|
* We'll assume the client-side dialog box code knows what it's
|
|
* doing, since we can't check it from here.
|
|
*/
|
|
bAnsiProc = !!(CPDOption & CPD_UNICODE_TO_ANSI);
|
|
}
|
|
#endif
|
|
} else {
|
|
UserAssert(CPDOption & (CPD_CLASS | CPD_WNDTOCLS));
|
|
if (CPDOption & CPD_WNDTOCLS) {
|
|
pcls = ((PWND)pWndOrCls)->pcls;
|
|
} else {
|
|
pcls = pWndOrCls;
|
|
}
|
|
#if DBG
|
|
bAnsiProc = !!(pcls->CSF_flags & CSF_ANSIPROC);
|
|
#endif
|
|
}
|
|
|
|
#if DBG
|
|
/*
|
|
* We should never have a CallProc handle as the calling address.
|
|
*/
|
|
UserAssert(!ISCPDTAG(dwProc32));
|
|
|
|
if (CPDOption & CPD_UNICODE_TO_ANSI) {
|
|
UserAssert(bAnsiProc);
|
|
} else if (CPDOption & CPD_ANSI_TO_UNICODE) {
|
|
UserAssert(!bAnsiProc);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* See if we already have a CallProc Handle that represents this
|
|
* transition
|
|
*/
|
|
pCPD = FindPCPD(pcls->spcpdFirst, dwProc32, (WORD)CPDOption);
|
|
|
|
if (pCPD) {
|
|
return MAKE_CPDHANDLE(PtoH(pCPD));
|
|
}
|
|
|
|
CheckCritIn();
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
pCPD = HMAllocObject(ptiCurrent,
|
|
ptiCurrent->rpdesk,
|
|
TYPE_CALLPROC,
|
|
sizeof(CALLPROCDATA));
|
|
if (pCPD == NULL) {
|
|
RIPMSG0(RIP_WARNING, "GetCPD unable to alloc CALLPROCDATA\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Link in the new CallProcData to the class list.
|
|
* Note -- these pointers are locked because WOWCleanup can come in
|
|
* and delete objects, so we need to keep the pointers locked.
|
|
*/
|
|
Lock(&pCPD->spcpdNext, pcls->spcpdFirst);
|
|
Lock(&pcls->spcpdFirst, pCPD);
|
|
|
|
/*
|
|
* Initialize the CPD
|
|
*/
|
|
pCPD->pfnClientPrevious = dwProc32;
|
|
pCPD->wType = (WORD)CPDOption;
|
|
|
|
return MAKE_CPDHANDLE(PtoH(pCPD));
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MapClientToServerPfn
|
|
*
|
|
* Checks to see if a dword is a client wndproc stub to a server wndproc.
|
|
* If it is, this returns the associated server side wndproc. If it isn't
|
|
* this returns 0.
|
|
*
|
|
* 13-Jan-1992 ScottLu Created.
|
|
\***************************************************************************/
|
|
ULONG_PTR MapClientToServerPfn(
|
|
ULONG_PTR dw)
|
|
{
|
|
ULONG_PTR *pdw;
|
|
int i;
|
|
|
|
pdw = (ULONG_PTR *)&gpsi->apfnClientW;
|
|
for (i = FNID_WNDPROCSTART; i <= FNID_WNDPROCEND; i++, pdw++) {
|
|
if (*pdw == dw) {
|
|
return (ULONG_PTR)STOCID(i);
|
|
}
|
|
}
|
|
|
|
pdw = (ULONG_PTR *)&gpsi->apfnClientA;
|
|
for (i = FNID_WNDPROCSTART; i <= FNID_WNDPROCEND; i++, pdw++) {
|
|
if (*pdw == dw) {
|
|
return (ULONG_PTR)STOCID(i);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if DBG
|
|
ULONG DBGGetWindowLong(
|
|
PWND pwnd,
|
|
int index)
|
|
{
|
|
UserAssert(index >= 0);
|
|
UserAssert((UINT)index + sizeof(DWORD) <= (UINT)pwnd->cbwndExtra);
|
|
|
|
return __GetWindowLong(pwnd, index);
|
|
}
|
|
|
|
ULONG_PTR DBGGetWindowLongPtr(
|
|
PWND pwnd,
|
|
int index)
|
|
{
|
|
UserAssert(index >= 0);
|
|
UserAssert((UINT)index + sizeof(ULONG_PTR) <= (UINT)pwnd->cbwndExtra);
|
|
|
|
return __GetWindowLongPtr(pwnd, index);
|
|
}
|
|
#endif
|