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.
2650 lines
87 KiB
2650 lines
87 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: ClMsg.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Includes the mapping table for messages when calling the server.
|
|
*
|
|
* 04-11-91 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
#define fnINDESTROYCLIPBRD fnDWORD
|
|
#define fnOUTDWORDDWORD fnDWORD
|
|
#define fnPOWERBROADCAST fnDWORD
|
|
#define fnLOGONNOTIFY fnKERNELONLY
|
|
#define fnINLPKDRAWSWITCHWND fnKERNELONLY
|
|
|
|
#define MSGFN(func) fn ## func
|
|
#define FNSCSENDMESSAGE CFNSCSENDMESSAGE
|
|
|
|
#include "messages.h"
|
|
|
|
#if DBG
|
|
BOOL gfTurboDWP = TRUE;
|
|
#endif
|
|
|
|
#define BEGIN_CALLWINPROC(fInsideHook, lRet) \
|
|
PCLIENTTHREADINFO pcti = GetClientInfo()->pClientThreadInfo; \
|
|
BOOL fCallBack = ((pcti!=NULL) && \
|
|
TEST_BOOL_FLAG(pcti->CTIF_flags, CTIF_INCALLBACKMESSAGE)); \
|
|
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActivationFrame \
|
|
= { \
|
|
sizeof(ActivationFrame), \
|
|
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER \
|
|
}; \
|
|
fInsideHook = FALSE; \
|
|
\
|
|
if (!fCallBack) { \
|
|
RtlActivateActivationContextUnsafeFast( \
|
|
&ActivationFrame, \
|
|
pActCtx); \
|
|
\
|
|
fInsideHook = _BeginIfHookedUserApiHook(); \
|
|
} \
|
|
\
|
|
__try { \
|
|
|
|
#define END_CALLWINPROC(fInsideHook) \
|
|
} __finally { \
|
|
if (!fCallBack) { \
|
|
if (fInsideHook) { \
|
|
_EndUserApiHook(); \
|
|
} \
|
|
RtlDeactivateActivationContextUnsafeFast( \
|
|
&ActivationFrame); \
|
|
} \
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* UserCallWinProc
|
|
*
|
|
* Setups everything to finally call a Win32 WNDPROC
|
|
*
|
|
* History:
|
|
* 27-Apr-2000 jstall Rewrote to support "lightweight hooks"
|
|
\***************************************************************************/
|
|
|
|
LRESULT
|
|
UserCallWinProc(
|
|
PACTIVATION_CONTEXT pActCtx,
|
|
WNDPROC pfn,
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
BOOL fInsideHook;
|
|
LRESULT lRet = 0;
|
|
|
|
BEGIN_CALLWINPROC(fInsideHook, lRet)
|
|
BOOL fOverride = fInsideHook && IsMsgOverride(msg, &guah.uoiWnd.mm);
|
|
|
|
pfn = MapKernelClientFnToClientFn(pfn);
|
|
|
|
if (fOverride) {
|
|
/*
|
|
* NOTE: It is important that the same lRet is passed to all three
|
|
* calls, allowing the Before and After OWP's to examine the value.
|
|
*/
|
|
PVOID pvCookie = NULL;
|
|
if (!guah.uoiWnd.pfnBeforeOWP(hwnd, msg, wParam, lParam, &lRet, &pvCookie)) {
|
|
lRet = InternalCallWinProc((WNDPROC)KPVOID_TO_PVOID(pfn),
|
|
hwnd,
|
|
msg,
|
|
wParam,
|
|
lParam);
|
|
guah.uoiWnd.pfnAfterOWP(hwnd, msg, wParam, lParam, &lRet, &pvCookie);
|
|
}
|
|
} else {
|
|
lRet = InternalCallWinProc((WNDPROC)KPVOID_TO_PVOID(pfn),
|
|
hwnd,
|
|
msg,
|
|
wParam,
|
|
lParam);
|
|
}
|
|
END_CALLWINPROC(fInsideHook)
|
|
|
|
return lRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* UserCallWinProcCheckWow
|
|
*
|
|
* Sets up everything to finally call a Win32 or WOW WNDPROC.
|
|
*
|
|
* History:
|
|
* 27-Apr-2000 jstall Rewrote to support "lightweight hooks"
|
|
\***************************************************************************/
|
|
|
|
LRESULT
|
|
UserCallWinProcCheckWow(
|
|
PACTIVATION_CONTEXT pActCtx,
|
|
WNDPROC pfn,
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
PVOID pww,
|
|
BOOL fEnableLiteHooks)
|
|
{
|
|
BOOL fInsideHook;
|
|
LRESULT lRet = 0;
|
|
|
|
BEGIN_CALLWINPROC(fInsideHook, lRet)
|
|
|
|
BOOL fOverride = fInsideHook && fEnableLiteHooks && IsMsgOverride(msg, &guah.uoiWnd.mm);
|
|
|
|
pfn = MapKernelClientFnToClientFn(pfn);
|
|
|
|
if (fOverride) {
|
|
/*
|
|
* NOTE: It is important that the same lRet is passed to all three
|
|
* calls, allowing the Before and After OWP's to examine the value.
|
|
*/
|
|
void * pvCookie = NULL;
|
|
if (guah.uoiWnd.pfnBeforeOWP(hwnd, msg, wParam, lParam, &lRet, &pvCookie)) {
|
|
goto DoneCalls;
|
|
}
|
|
|
|
lRet = (IsWOWProc(pfn) ? (*pfnWowWndProcEx)(hwnd, msg, wParam, lParam, PtrToUlong(pfn), KPVOID_TO_PVOID(pww)) :
|
|
InternalCallWinProc((WNDPROC)KPVOID_TO_PVOID(pfn), hwnd, msg, wParam, lParam));
|
|
|
|
if (guah.uoiWnd.pfnAfterOWP(hwnd, msg, wParam, lParam, &lRet, &pvCookie)) {
|
|
// Fall through and exit normally
|
|
}
|
|
DoneCalls:
|
|
;
|
|
} else {
|
|
lRet = (IsWOWProc(pfn) ? (*pfnWowWndProcEx)(hwnd, msg, wParam, lParam, PtrToUlong(pfn), KPVOID_TO_PVOID(pww)) :
|
|
InternalCallWinProc((WNDPROC)KPVOID_TO_PVOID(pfn), hwnd, msg, wParam, lParam));
|
|
}
|
|
END_CALLWINPROC(fInsideHook)
|
|
|
|
return lRet;
|
|
#ifdef _WIN64
|
|
UNREFERENCED_PARAMETER(pww);
|
|
#endif // _WIN64
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* UserCallDlgProcCheckWow
|
|
*
|
|
* Setups everything to finally call a Win32 or WOW DLGPROC
|
|
*
|
|
* History:
|
|
* 27-Apr-2000 jstall Rewrote to support "lightweight hooks"
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
UserCallDlgProcCheckWow(
|
|
PACTIVATION_CONTEXT pActCtx,
|
|
DLGPROC pfn,
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
PVOID pww,
|
|
INT_PTR * pret)
|
|
{
|
|
BOOL fInsideHook;
|
|
INT_PTR fRet = 0;
|
|
BOOL fHandled = FALSE;
|
|
|
|
BEGIN_CALLWINPROC(fInsideHook, fRet)
|
|
|
|
BOOL fOverride = fInsideHook && IsMsgOverride(msg, &guah.uoiDlg.mm);
|
|
|
|
pfn = MapKernelClientFnToClientFn(pfn);
|
|
|
|
if (fOverride) {
|
|
/*
|
|
* NOTE: It is important that the same lRet is passed to all three
|
|
* calls, allowing the Before and After OWP's to examine the value.
|
|
*/
|
|
void * pvCookie = NULL;
|
|
if (guah.uoiDlg.pfnBeforeOWP(hwnd, msg, wParam, lParam, (LRESULT*) &fRet, &pvCookie)) {
|
|
fHandled = TRUE;
|
|
goto DoneCalls;
|
|
}
|
|
|
|
fRet = (IsWOWProc(pfn) ? (*pfnWowDlgProcEx)(hwnd, msg, wParam, lParam, PtrToUlong(pfn), KPVOID_TO_PVOID(pww)) :
|
|
InternalCallWinProc((WNDPROC)KPVOID_TO_PVOID(pfn), hwnd, msg, wParam, lParam));
|
|
|
|
if (guah.uoiDlg.pfnAfterOWP(hwnd, msg, wParam, lParam, (LRESULT*) &fRet, &pvCookie)) {
|
|
fHandled = TRUE;
|
|
// Fall through and exit normally
|
|
}
|
|
DoneCalls:
|
|
;
|
|
} else {
|
|
fRet = (IsWOWProc(pfn) ? (*pfnWowDlgProcEx)(hwnd, msg, wParam, lParam, PtrToUlong(pfn), KPVOID_TO_PVOID(pww)) :
|
|
InternalCallWinProc((WNDPROC)KPVOID_TO_PVOID(pfn), hwnd, msg, wParam, lParam));
|
|
}
|
|
|
|
END_CALLWINPROC(fInsideHook)
|
|
|
|
*pret = fRet;
|
|
|
|
return fHandled;
|
|
#ifdef _WIN64
|
|
UNREFERENCED_PARAMETER(pww);
|
|
#endif // _WIN64
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* GetMouseKeyState
|
|
*
|
|
* Returns the state of mouse and keyboard keys that are sent
|
|
* in a mouse message.
|
|
*
|
|
* History:
|
|
* 12-Nov-1998 adams Created.
|
|
\***************************************************************************/
|
|
|
|
WORD
|
|
GetMouseKeyState(void)
|
|
{
|
|
WORD keystate;
|
|
|
|
/*
|
|
* Note that it is more efficient to call GetKeyState for each
|
|
* key than to call GetKeyboardState, since the keys we are testing
|
|
* are cached and don't require a trip to the kernel to fetch.
|
|
*/
|
|
|
|
#define TESTANDSETKEYSTATE(x) \
|
|
if (GetKeyState(VK_##x) & 0x8000) { \
|
|
keystate |= MK_##x; \
|
|
}
|
|
|
|
keystate = 0;
|
|
TESTANDSETKEYSTATE(LBUTTON)
|
|
TESTANDSETKEYSTATE(RBUTTON)
|
|
TESTANDSETKEYSTATE(MBUTTON)
|
|
TESTANDSETKEYSTATE(XBUTTON1)
|
|
TESTANDSETKEYSTATE(XBUTTON2)
|
|
TESTANDSETKEYSTATE(SHIFT)
|
|
TESTANDSETKEYSTATE(CONTROL)
|
|
|
|
return keystate;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* These are client side thunks for server side window procs. This is being
|
|
* done so that when an app gets a wndproc via GetWindowLong, GetClassLong,
|
|
* or GetClassInfo, it gets a real callable address - some apps don't call
|
|
* CallWindowProc, but call the return ed address directly.
|
|
*
|
|
* 01-13-92 ScottLu Created.
|
|
* 03-Dec-1993 mikeke added client side handling of some messages
|
|
\***************************************************************************/
|
|
|
|
LRESULT WINAPI DesktopWndProcWorker(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
BOOL fAnsi)
|
|
{
|
|
PWND pwnd;
|
|
|
|
if (FWINDOWMSG(message, FNID_DESKTOP)) {
|
|
return CsSendMessage(hwnd, message, wParam, lParam,
|
|
0L, FNID_DESKTOP, fAnsi);
|
|
}
|
|
|
|
if ((pwnd = ValidateHwnd(hwnd)) == NULL)
|
|
return 0;
|
|
|
|
return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
|
|
|
|
}
|
|
|
|
LRESULT WINAPI DesktopWndProcA(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return DesktopWndProcWorker(hwnd, message, wParam, lParam, TRUE);
|
|
}
|
|
|
|
LRESULT WINAPI DesktopWndProcW(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return DesktopWndProcWorker(hwnd, message, wParam, lParam, FALSE);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* These are client side thunks for server side window procs. This is being
|
|
* done so that when an app gets a wndproc via GetWindowLong, GetClassLong,
|
|
* or GetClassInfo, it gets a real callable address - some apps don't call
|
|
* CallWindowProc, but call the return ed address directly.
|
|
*
|
|
* 01-13-92 ScottLu Created.
|
|
* 03-Dec-1993 mikeke added client side handling of some messages
|
|
\***************************************************************************/
|
|
|
|
LRESULT WINAPI MenuWndProcWorker(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
BOOL fAnsi)
|
|
{
|
|
PWND pwnd;
|
|
|
|
if (FWINDOWMSG(message, FNID_MENU)) {
|
|
return CsSendMessage(hwnd, message, wParam, lParam,
|
|
0L, FNID_MENU, fAnsi);
|
|
}
|
|
|
|
if ((pwnd = ValidateHwnd(hwnd)) == NULL)
|
|
return 0;
|
|
|
|
switch (message) {
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_NCLBUTTONDBLCLK:
|
|
case WM_RBUTTONDBLCLK:
|
|
case WM_NCRBUTTONDBLCLK:
|
|
|
|
/*
|
|
* Ignore double clicks on these windows.
|
|
*/
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT WINAPI MenuWndProcA(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return MenuWndProcWorker(hwnd, message, wParam, lParam, TRUE);
|
|
}
|
|
|
|
LRESULT WINAPI MenuWndProcW(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return MenuWndProcWorker(hwnd, message, wParam, lParam, FALSE);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
\***************************************************************************/
|
|
|
|
|
|
LRESULT WINAPI ScrollBarWndProcWorker(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
BOOL fAnsi)
|
|
{
|
|
PSBWND psbwnd;
|
|
LPSCROLLINFO lpsi;
|
|
PSBDATA pw;
|
|
|
|
if (FWINDOWMSG(message, FNID_SCROLLBAR)) {
|
|
return CsSendMessage(hwnd, message, wParam, lParam,
|
|
0L, FNID_SCROLLBAR, fAnsi);
|
|
}
|
|
|
|
if ((psbwnd = (PSBWND)ValidateHwnd(hwnd)) == NULL)
|
|
return 0;
|
|
|
|
switch (message) {
|
|
case WM_GETDLGCODE:
|
|
return DLGC_WANTARROWS;
|
|
|
|
case SBM_GETPOS:
|
|
return (LONG)psbwnd->SBCalc.pos;
|
|
|
|
case SBM_GETRANGE:
|
|
*((LPINT)wParam) = psbwnd->SBCalc.posMin;
|
|
*((LPINT)lParam) = psbwnd->SBCalc.posMax;
|
|
return 0;
|
|
|
|
case SBM_GETSCROLLINFO:
|
|
lpsi = (LPSCROLLINFO)lParam;
|
|
if ((lpsi->cbSize != sizeof(SCROLLINFO)) &&
|
|
(lpsi->cbSize != sizeof(SCROLLINFO) - 4)) {
|
|
RIPMSG0(RIP_ERROR, "SCROLLINFO: invalid cbSize");
|
|
return FALSE;
|
|
}
|
|
|
|
if (lpsi->fMask & ~SIF_MASK)
|
|
{
|
|
RIPMSG0(RIP_ERROR, "SCROLLINFO: Invalid fMask");
|
|
return FALSE;
|
|
}
|
|
|
|
pw = (PSBDATA)KPSBDATA_TO_PSBDATA(&(psbwnd->SBCalc));
|
|
return(NtUserSBGetParms(hwnd, SB_CTL, pw, lpsi));
|
|
|
|
case SBM_GETSCROLLBARINFO:
|
|
return NtUserGetScrollBarInfo(hwnd, OBJID_CLIENT, (PSCROLLBARINFO)lParam);
|
|
|
|
default:
|
|
return DefWindowProcWorker((PWND)psbwnd, message,
|
|
wParam, lParam, fAnsi);
|
|
}
|
|
}
|
|
|
|
|
|
LRESULT WINAPI ScrollBarWndProcA(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return ScrollBarWndProcWorker(hwnd, message, wParam, lParam, TRUE);
|
|
}
|
|
|
|
LRESULT WINAPI ScrollBarWndProcW(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return ScrollBarWndProcWorker(hwnd, message, wParam, lParam, FALSE);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* SendMessage
|
|
*
|
|
* Translates the message, calls SendMessage on server side.
|
|
*
|
|
* 04-11-91 ScottLu Created.
|
|
* 04-27-92 DarrinM Added code to support client-to-client SendMessages.
|
|
\***************************************************************************/
|
|
|
|
LRESULT SendMessageWorker(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
BOOL fAnsi)
|
|
{
|
|
HWND hwnd = HWq(pwnd);
|
|
PCLIENTINFO pci;
|
|
PCLS pcls;
|
|
BOOLEAN fAnsiRecv;
|
|
BOOLEAN fNeedTranslation = FALSE;
|
|
BOOLEAN bDoDbcsMessaging = FALSE;
|
|
LRESULT lRet;
|
|
|
|
UserAssert(pwnd);
|
|
|
|
/*
|
|
* Pass DDE messages to the server.
|
|
*/
|
|
if (message >= WM_DDE_FIRST && message <= WM_DDE_LAST)
|
|
goto lbServerSendMessage;
|
|
|
|
/*
|
|
* Server must handle inter-thread SendMessages and SendMessages
|
|
* to server-side procs.
|
|
*/
|
|
if ((PtiCurrent() != GETPTI(pwnd)) || TestWF(pwnd, WFSERVERSIDEPROC))
|
|
goto lbServerSendMessage;
|
|
|
|
/*
|
|
* Server must handle hooks (at least for now).
|
|
*/
|
|
pci = GetClientInfo();
|
|
if (IsHooked(pci, (WHF_CALLWNDPROC | WHF_CALLWNDPROCRET))) {
|
|
lbServerSendMessage:
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_SENDMESSAGE, fAnsi);
|
|
}
|
|
|
|
/*
|
|
* If the sender and the receiver are both ANSI or both UNICODE
|
|
* then no message translation is necessary.
|
|
*
|
|
* EditWndProc may need to go to the server for translation if we
|
|
* are calling vanilla EditWndProc from SendMessageA and the edit
|
|
* control is currently subclassed Ansi but the edit control is
|
|
* stored Unicode.
|
|
*/
|
|
fAnsiRecv = !!(TestWF(pwnd, WFANSIPROC));
|
|
if (!fAnsi != !fAnsiRecv) {
|
|
|
|
/*
|
|
* Translation might be necessary between sender and receiver,
|
|
* check to see if this is one of the messages we translate.
|
|
*/
|
|
switch (message) {
|
|
case WM_CHARTOITEM:
|
|
case EM_SETPASSWORDCHAR:
|
|
case WM_CHAR:
|
|
case WM_DEADCHAR:
|
|
case WM_SYSCHAR:
|
|
case WM_SYSDEADCHAR:
|
|
case WM_MENUCHAR:
|
|
case WM_IME_CHAR:
|
|
case WM_IME_COMPOSITION:
|
|
if (fAnsi) {
|
|
/*
|
|
* Setup DBCS Messaging for WM_CHAR...
|
|
*/
|
|
BUILD_DBCS_MESSAGE_TO_CLIENTW_FROM_CLIENTA(message,wParam,TRUE);
|
|
|
|
/*
|
|
* Convert wParam to Unicode...
|
|
*/
|
|
RtlMBMessageWParamCharToWCS(message, &wParam);
|
|
|
|
/*
|
|
* The message has been converted to Unicode.
|
|
*/
|
|
fAnsi = FALSE;
|
|
} else {
|
|
POINT ptZero = {0,0};
|
|
/*
|
|
* Convert wParam to ANSI...
|
|
*/
|
|
RtlWCSMessageWParamCharToMB(message, &wParam);
|
|
|
|
/*
|
|
* Let's DBCS messaging for WM_CHAR....
|
|
*/
|
|
BUILD_DBCS_MESSAGE_TO_CLIENTA_FROM_CLIENTW(
|
|
hwnd,message,wParam,lParam,0,ptZero,bDoDbcsMessaging);
|
|
|
|
/*
|
|
* The message has been converted to ANSI.
|
|
*/
|
|
fAnsi = TRUE;
|
|
}
|
|
break;
|
|
|
|
case EM_SETSEL:
|
|
case EM_GETSEL:
|
|
case CB_GETEDITSEL:
|
|
if (IS_DBCS_ENABLED()) {
|
|
RIPERR1(ERROR_INVALID_PARAMETER,
|
|
RIP_WARNING,
|
|
"Invalid DBCS message (%x) to SendMessageWorker",message);
|
|
}
|
|
//
|
|
// Fall down...
|
|
|
|
default:
|
|
if ((message < WM_USER) && MessageTable[message].bThunkMessage) {
|
|
fNeedTranslation = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef LATER
|
|
/*
|
|
* If the window has a client side worker proc and has
|
|
* not been subclassed, dispatch the message directly
|
|
* to the worker proc. Otherwise, dispatch it normally.
|
|
*/
|
|
pcls = REBASEALWAYS(pwnd, pcls);
|
|
|
|
if ((!IsInsideUserApiHook()) &&
|
|
(pcls->fnid >= FNID_CONTROLSTART && pcls->fnid <= FNID_CONTROLEND) &&
|
|
((KERNEL_ULONG_PTR)pwnd->lpfnWndProc == FNID_TO_CLIENT_PFNW_KERNEL(pcls->fnid) ||
|
|
(KERNEL_ULONG_PTR)pwnd->lpfnWndProc == FNID_TO_CLIENT_PFNA_KERNEL(pcls->fnid))) {
|
|
PWNDMSG pwm = &gSharedInfo.awmControl[pcls->fnid - FNID_START];
|
|
|
|
/*
|
|
* If this message is not processed by the control, call
|
|
* xxxDefWindowProc
|
|
*/
|
|
if (pwm->abMsgs && ((message > pwm->maxMsgs) ||
|
|
!((pwm->abMsgs)[message / 8] & (1 << (message & 7))))) {
|
|
|
|
/*
|
|
* Special case dialogs so that we can ignore unimportant
|
|
* messages during dialog creation.
|
|
*/
|
|
if (pcls->fnid == FNID_DIALOG &&
|
|
PDLG(pwnd) && PDLG(pwnd)->lpfnDlg != NULL) {
|
|
/*
|
|
* If A/W translation are needed for Dialog,
|
|
* it should go to kernel side to perform proper message.
|
|
* DefDlgProcWorker will call aplication's DlgProc directly
|
|
* without A/W conversion.
|
|
*/
|
|
if (fNeedTranslation) {
|
|
goto lbServerSendMessage;
|
|
}
|
|
/*
|
|
* Call woker procudure.
|
|
*/
|
|
SendMessageToWorker1Again:
|
|
lRet = ((PROC)(FNID_TO_CLIENT_PFNWORKER(pcls->fnid)))(pwnd, message, wParam, lParam, fAnsi);
|
|
/*
|
|
* if we have DBCS TrailingByte that should be sent, send it here..
|
|
*/
|
|
DISPATCH_DBCS_MESSAGE_IF_EXIST(message,wParam,bDoDbcsMessaging,SendMessageToWorker1);
|
|
|
|
return lRet;
|
|
} else {
|
|
/*
|
|
* Call worker procedure.
|
|
*/
|
|
SendMessageToDefWindowAgain:
|
|
lRet = DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
|
|
/*
|
|
* if we have DBCS TrailingByte that should be sent, send it here..
|
|
*/
|
|
DISPATCH_DBCS_MESSAGE_IF_EXIST(message,wParam,bDoDbcsMessaging,SendMessageToDefWindow);
|
|
|
|
return lRet;
|
|
}
|
|
} else {
|
|
/*
|
|
* Call woker procudure.
|
|
*/
|
|
SendMessageToWorker2Again:
|
|
lRet = ((PROC)(FNID_TO_CLIENT_PFNWORKER(pcls->fnid)))(pwnd, message, wParam, lParam, fAnsi);
|
|
|
|
/*
|
|
* if we have DBCS TrailingByte that should be sent, send it here..
|
|
*/
|
|
DISPATCH_DBCS_MESSAGE_IF_EXIST(message,wParam,bDoDbcsMessaging,SendMessageToWorker2);
|
|
|
|
return lRet;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If this message needs to be translated, go through the kernel.
|
|
*/
|
|
if (fNeedTranslation) {
|
|
goto lbServerSendMessage;
|
|
}
|
|
|
|
/*
|
|
* Call Client Windows procudure.
|
|
*/
|
|
SendMessageToWndProcAgain:
|
|
lRet = UserCallWinProcCheckWow(pwnd->pActCtx, (WNDPROC)pwnd->lpfnWndProc, hwnd, message, wParam, lParam, &(pwnd->state), TRUE);
|
|
|
|
/*
|
|
* if we have DBCS TrailingByte that should be sent, send it here..
|
|
*/
|
|
DISPATCH_DBCS_MESSAGE_IF_EXIST(message,wParam,bDoDbcsMessaging,SendMessageToWndProc);
|
|
|
|
return lRet;
|
|
}
|
|
|
|
// LATER!!! can this somehow be combined or subroutinized with SendMessageWork
|
|
// so we don't have to copies of 95% identical code.
|
|
|
|
/***************************************************************************\
|
|
* SendMessageTimeoutWorker
|
|
*
|
|
* Translates the message, calls SendMessageTimeout on server side.
|
|
*
|
|
* 07-21-92 ChrisBB Created/modified SendMessageWorkder
|
|
\***************************************************************************/
|
|
|
|
LRESULT SendMessageTimeoutWorker(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
UINT fuFlags,
|
|
UINT uTimeout,
|
|
PULONG_PTR lpdwResult,
|
|
BOOL fAnsi)
|
|
{
|
|
SNDMSGTIMEOUT smto;
|
|
|
|
/*
|
|
* Prevent apps from setting hi 16 bits so we can use them internally.
|
|
*/
|
|
if (message & RESERVED_MSG_BITS) {
|
|
RIPERR1(ERROR_INVALID_PARAMETER,
|
|
RIP_WARNING,
|
|
"Invalid parameter \"message\" (%ld) to SendMessageTimeoutWorker",
|
|
message);
|
|
|
|
return(0);
|
|
}
|
|
|
|
if (fuFlags & ~SMTO_VALID) {
|
|
RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "invalid dwFlags (%x) for SendMessageTimeout\n", fuFlags);
|
|
return(0);
|
|
}
|
|
|
|
if (lpdwResult != NULL)
|
|
*lpdwResult = 0L;
|
|
|
|
/*
|
|
* Always send broadcast requests straight to the server.
|
|
* Note: the xParam is used to id if it's from timeout or
|
|
* from an normal sendmessage.
|
|
*/
|
|
smto.fuFlags = fuFlags;
|
|
smto.uTimeout = uTimeout;
|
|
smto.lSMTOReturn = 0;
|
|
smto.lSMTOResult = 0;
|
|
|
|
/*
|
|
* Thunk through a special sendmessage for -1 hwnd's so that the general
|
|
* purpose thunks don't allow -1 hwnd's.
|
|
*/
|
|
if (hwnd == (HWND)-1 || hwnd == (HWND)0x0000FFFF) {
|
|
/*
|
|
* Get a real hwnd so the thunks will validation ok. Note that since
|
|
* -1 hwnd is really rare, calling GetDesktopWindow() here is not a
|
|
* big deal.
|
|
*/
|
|
hwnd = GetDesktopWindow();
|
|
|
|
CsSendMessage(hwnd, message, wParam, lParam,
|
|
(ULONG_PTR)&smto, FNID_SENDMESSAGEFF, fAnsi);
|
|
} else {
|
|
CsSendMessage(hwnd, message, wParam, lParam,
|
|
(ULONG_PTR)&smto, FNID_SENDMESSAGEEX, fAnsi);
|
|
}
|
|
|
|
if (lpdwResult != NULL)
|
|
*lpdwResult = smto.lSMTOResult;
|
|
|
|
return smto.lSMTOReturn;
|
|
}
|
|
|
|
#ifdef LAME_BUTTON
|
|
|
|
PLAMEBTNPROC gpfnCommentReport;
|
|
|
|
VOID LoadCommentReportIfNeeded(
|
|
VOID)
|
|
{
|
|
if (gpfnCommentReport == NULL) {
|
|
HMODULE hmod = LoadLibrary(L"LAMEBTN.DLL");
|
|
|
|
if (hmod != NULL) {
|
|
gpfnCommentReport = (PLAMEBTNPROC)GetProcAddress(hmod, "CommentReport");
|
|
if (gpfnCommentReport == NULL) {
|
|
FreeLibrary(hmod);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID CallLameButtonHandler(
|
|
PWND pwnd,
|
|
HWND hwnd)
|
|
{
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
|
|
UserAssert(TestWF(pwnd, WEFLAMEBUTTON));
|
|
|
|
LoadCommentReportIfNeeded();
|
|
|
|
if (gpfnCommentReport != NULL) {
|
|
(*gpfnCommentReport)(hwnd, GetProp(hwnd, MAKEINTATOM(gatomLameButton)));
|
|
}
|
|
}
|
|
#endif // LAME_BUTTON
|
|
|
|
VOID CopyMsgMask(
|
|
MSGMASK * pDest,
|
|
MSGMASK * pSrc,
|
|
BYTE * rgbLocal,
|
|
DWORD cbMax)
|
|
{
|
|
if ((pSrc->rgb != NULL) && (pSrc->cb > 0)) {
|
|
pDest->rgb = rgbLocal;
|
|
pDest->cb = min(cbMax, pSrc->cb);
|
|
CopyMemory(pDest->rgb, pSrc->rgb, pDest->cb);
|
|
} else {
|
|
pDest->rgb = NULL;
|
|
pDest->cb = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* InitUserApiHook
|
|
*
|
|
* This function gets called when the module that contains the UserApiHook's
|
|
* gets loaded. The UserApiHook is installed by calling
|
|
* RegisterUserApiHook and is loaded on demand by xxxCreateWindowEx and/or
|
|
* xxxDefWindowProc. The loading/unloading is controlled through the library
|
|
* management routines. The function calls the initialization function in the
|
|
* module and then sets up our global variables.
|
|
*
|
|
* We keep 2 reference counts. One counts calls to LoadLibrary/FreeLibrary. When
|
|
* this goes to zero, we can stop calling out to the substitute UserApiHook's.
|
|
* The other gets incremented when we do an actual callout to the substitute
|
|
* UserApiHook and decremented on return. We can't actually unload the module
|
|
* until this count goes to zero too (i.e. we aren't in a callout).
|
|
*
|
|
* History:
|
|
* 10-Mar-2000 JerrySh Created.
|
|
* 16-May-2000 JStall Changed to support uninitialize callback
|
|
* 12-Feb-2001 Mohamed Added the check for read-only on reset ptr.
|
|
\***************************************************************************/
|
|
BOOL InitUserApiHook(
|
|
HMODULE hmod,
|
|
ULONG_PTR offPfnInitUserApiHook)
|
|
{
|
|
INITUSERAPIHOOK pfnInitUserApi = NULL;
|
|
USERAPIHOOK uahTemp;
|
|
|
|
BOOL bUpdate= FALSE;
|
|
BOOL retval = FALSE;
|
|
|
|
/*
|
|
* If we're loading for the first time, call the initialization routine.
|
|
*/
|
|
ResetUserApiHook(&uahTemp);
|
|
pfnInitUserApi = (INITUSERAPIHOOK)((ULONG_PTR)hmod + offPfnInitUserApiHook);
|
|
bUpdate = pfnInitUserApi(UIAH_INITIALIZE, &uahTemp);
|
|
|
|
/*
|
|
* Check that the value of pfnForceResetUserApiHook hasn't been changed
|
|
* by client since this should be treated as read-only.
|
|
*/
|
|
if ((!bUpdate) || (uahTemp.cbSize <= 0) || (uahTemp.pfnForceResetUserApiHook != ForceResetUserApiHook)) {
|
|
return FALSE;
|
|
}
|
|
|
|
RtlEnterCriticalSection(&gcsUserApiHook);
|
|
|
|
/*
|
|
* Need to check this again inside critical section.
|
|
*/
|
|
if (ghmodUserApiHook == NULL) {
|
|
UserAssertMsg0(gpfnInitUserApi == NULL, "Ensure gpfnInitUserApi not set");
|
|
|
|
/*
|
|
* Save the global state if the init routine succeeded.
|
|
* Copy the hooked functions
|
|
*/
|
|
UserAssert(gcLoadUserApiHook == 0);
|
|
gcLoadUserApiHook = 1;
|
|
gfUserApiHook = TRUE; // Turn calling the hooks on
|
|
ghmodUserApiHook = hmod;
|
|
gpfnInitUserApi = pfnInitUserApi;
|
|
|
|
CopyMemory(&guah, &uahTemp, uahTemp.cbSize);
|
|
|
|
/*
|
|
* Copy the message-filter bit-mask
|
|
*/
|
|
CopyMsgMask(&guah.mmDWP, &uahTemp.mmDWP, grgbDwpLiteHookMsg, sizeof(grgbDwpLiteHookMsg));
|
|
CopyMsgMask(&guah.uoiWnd.mm, &uahTemp.uoiWnd.mm, grgbWndLiteHookMsg, sizeof(grgbWndLiteHookMsg));
|
|
CopyMsgMask(&guah.uoiDlg.mm, &uahTemp.uoiDlg.mm, grgbDlgLiteHookMsg, sizeof(grgbDlgLiteHookMsg));
|
|
|
|
retval = TRUE;
|
|
} else if (ghmodUserApiHook == hmod) {
|
|
/*
|
|
* This is the UserApiHook module, so bump up the reference count.
|
|
*/
|
|
UserAssert(gcLoadUserApiHook < MAXLONG);
|
|
UserAssertMsg0(gpfnInitUserApi == pfnInitUserApi, "Need to match from before");
|
|
++gcLoadUserApiHook;
|
|
retval = TRUE;
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&gcsUserApiHook);
|
|
|
|
if (!retval) {
|
|
/*
|
|
* Initialization failed, so ClientLoadLibrary() is going to
|
|
* FreeLibrary(). Notify before we do this.
|
|
*/
|
|
RIPMSG2(RIP_WARNING, "Uninit from Init Load %lx Call %lx", gcLoadUserApiHook, gcCallUserApiHook);
|
|
pfnInitUserApi(UIAH_UNINITIALIZE, NULL);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ClearUserApiHook
|
|
*
|
|
* This function gets called when the module that contains the UserApiHook
|
|
* is about to get unloaded. The unload happens when UnregisterUserApiHook
|
|
* is called or the process that registered it exits. If this is the last unload,
|
|
* we'll clear the globals containing the UserApiHook function addresses so we
|
|
* don't do any more callouts. If we're not currently doing a callout, we'll
|
|
* indicate that it's OK to unload the module. Otherwise, it'll get unloaded
|
|
* when the last callout completes.
|
|
*
|
|
* History:
|
|
* 10-Mar-2000 JerrySh Created.
|
|
* 16-May-2000 JStall Changed to support uninitialize callback
|
|
* 03-Apr-2001 Mohamed Added support for UIAH_UNHOOK logic.
|
|
\***************************************************************************/
|
|
BOOL ClearUserApiHook(
|
|
HMODULE hmod)
|
|
{
|
|
INITUSERAPIHOOK pfnInitUserApi = NULL;
|
|
INITUSERAPIHOOK pfnSignalInitUserApi = NULL;
|
|
|
|
/*
|
|
* If this is the last reference to the UserApiHook module, clear the
|
|
* global state.
|
|
*/
|
|
RtlEnterCriticalSection(&gcsUserApiHook);
|
|
if (ghmodUserApiHook == hmod) {
|
|
UserAssert(gcLoadUserApiHook > 0);
|
|
UserAssertMsg0(gpfnInitUserApi != NULL, "Ensure gpfnInitUserApi properly set");
|
|
UserAssertMsg0(ghmodUserApiHook != NULL, "Should still have valid ghmodUserApiHook");
|
|
pfnInitUserApi = gpfnInitUserApi;
|
|
|
|
if (--gcLoadUserApiHook == 0) {
|
|
/*
|
|
* Use the internal functions, so turn calling the hooks off. It is
|
|
* very important to set gfUserApiHook FALSE here so that new calls
|
|
* do not increment gcCallUserApiHook and keep the DLL from being
|
|
* unloaded.
|
|
*/
|
|
gfUserApiHook = FALSE;
|
|
ResetUserApiHook(&guah);
|
|
|
|
if (gcCallUserApiHook == 0) {
|
|
/*
|
|
* We're not calling into it, we can free the module.
|
|
*
|
|
* FreeLibrary() will be called on this DLL in
|
|
* ClientFreeLibrary() when this function returns.
|
|
*/
|
|
hmod = ghmodUserApiHook;
|
|
ghmodUserApiHook = NULL;
|
|
gpfnInitUserApi = NULL;
|
|
} else {
|
|
/*
|
|
* We're still calling into the module, so we can't free it yet.
|
|
* This means we have to delay the last callback with UIAH_UNINITIALIZE
|
|
* until we actually free the library. This will occur in
|
|
* _EndUserApiHook(). However, we set pfnSignalInitUserApi to initiate
|
|
* a callback with UIAH_UNHOOK to alert the module to this fact.
|
|
*/
|
|
hmod = NULL;
|
|
pfnInitUserApi = NULL;
|
|
pfnSignalInitUserApi = gpfnInitUserApi;
|
|
++gcLoadUserApiHook;
|
|
}
|
|
} else {
|
|
/*
|
|
* This part of code should never be executed since we guard against
|
|
* multiple loads of same DLL in xxxLoadUserApiHook. However, since
|
|
* this is a load count and could conceivably be greater than 1,
|
|
* this warning message is inserted to signal such an event.
|
|
*/
|
|
RIPMSG1(RIP_WARNING, " gcLoadUserApiHook: %lx > 1 in Clear Load", gcLoadUserApiHook);
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&gcsUserApiHook);
|
|
|
|
/*
|
|
* Signal that hooks have been uninitialized but DLL can't be unloaded due to outstanding calls.
|
|
*/
|
|
if (pfnSignalInitUserApi != NULL) {
|
|
RIPMSG2(RIP_WARNING, "Unhook from Clear Load %lx Call %lx", gcLoadUserApiHook, gcCallUserApiHook);
|
|
pfnSignalInitUserApi(UIAH_UNHOOK, NULL);
|
|
|
|
/*
|
|
* After having returned from the DLL, we revalidate the state of the hooking world again.
|
|
* The outstanding call that we were deferring in favor of, could have completed by now
|
|
* and finding the load count greater than zero, it ignored completing the deferred cleanup
|
|
* which we must now do at this point.
|
|
*/
|
|
RtlEnterCriticalSection(&gcsUserApiHook);
|
|
UserAssert(gcLoadUserApiHook > 0);
|
|
UserAssertMsg0(gpfnInitUserApi != NULL, "Ensure gpfnInitUserApi properly set");
|
|
UserAssertMsg0(ghmodUserApiHook != NULL, "Should still have valid ghmodUserApiHook");
|
|
pfnInitUserApi = gpfnInitUserApi;
|
|
|
|
if (--gcLoadUserApiHook == 0) {
|
|
if (gcCallUserApiHook == 0) {
|
|
/*
|
|
* The outstanding call has completed while we were
|
|
* calling back and we can now safely clean up.
|
|
* FreeLibrary() will be called on this DLL in
|
|
* ClientFreeLibrary() when this function returns.
|
|
*/
|
|
hmod = ghmodUserApiHook;
|
|
ghmodUserApiHook = NULL;
|
|
gpfnInitUserApi = NULL;
|
|
} else {
|
|
/*
|
|
* The outstanding call into the DLL hasn't returned. However, we are done
|
|
* from this point. The DLL has been notified of UNHOOK situation and the
|
|
* rest of the cleanup will be done in _EndUserApiHook when the last call
|
|
* into the DLL returns.
|
|
*/
|
|
hmod = NULL;
|
|
pfnInitUserApi = NULL;
|
|
}
|
|
} else {
|
|
/*
|
|
* This part of code should never be executed since we guard against
|
|
* multiple loads of same DLL in xxxLoadUserApiHook. However, since
|
|
* this is a load count and could conceivably be greater than 1,
|
|
* this warning message is inserted to signal such an event.
|
|
*/
|
|
RIPMSG1(RIP_WARNING, " gcLoadUserApiHook: %lx > 1 in Clear Load", gcLoadUserApiHook);
|
|
}
|
|
RtlLeaveCriticalSection(&gcsUserApiHook);
|
|
}
|
|
|
|
/*
|
|
* This is called in the case where the outstanding call into the DLL was completed in between
|
|
* the two critical sections and we have completed the full cleanup at this end.
|
|
*/
|
|
if (pfnInitUserApi != NULL) {
|
|
RIPMSG2(RIP_WARNING, "Uninit from Clear Load %lx Call %lx", gcLoadUserApiHook, gcCallUserApiHook);
|
|
pfnInitUserApi(UIAH_UNINITIALIZE, NULL);
|
|
}
|
|
|
|
return (hmod != NULL);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DefaultOWP
|
|
*
|
|
* This function provides an empty OWP implementation that can be safely
|
|
* called while the UserApiHook DLL is being unloaded and we are resetting
|
|
* the states.
|
|
*
|
|
* History:
|
|
* 27-Apr-2000 JStall Created.
|
|
\***************************************************************************/
|
|
BOOL CALLBACK DefaultOWP(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT * pr, void ** pvCookie)
|
|
{
|
|
UNREFERENCED_PARAMETER(hwnd);
|
|
UNREFERENCED_PARAMETER(message);
|
|
UNREFERENCED_PARAMETER(wParam);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
UNREFERENCED_PARAMETER(pr);
|
|
UNREFERENCED_PARAMETER(pvCookie);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* MDIRedrawFrame
|
|
*
|
|
*
|
|
* History:
|
|
* 20-Apr-2001 Mohamed Created.
|
|
\***************************************************************************/
|
|
void MDIRedrawFrame(
|
|
HWND hwndChild,
|
|
BOOL fAdd)
|
|
{
|
|
BEGIN_USERAPIHOOK()
|
|
guah.pfnMDIRedrawFrame(hwndChild, fAdd);
|
|
END_USERAPIHOOK()
|
|
}
|
|
|
|
void RealMDIRedrawFrame(
|
|
HWND hwndChild,
|
|
BOOL fAdd)
|
|
{
|
|
UNREFERENCED_PARAMETER(fAdd);
|
|
NtUserRedrawFrame(hwndChild);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ResetUserApiHook
|
|
*
|
|
* This function gets called to reset the UserApiHook function pointers to
|
|
* the internal (default) implementations of the functions. This is done
|
|
* when any installed UserApiHook is being removed by calling
|
|
* UnregisterUserApiHook().
|
|
*
|
|
* History:
|
|
* 28-Mar-2000 JStall Created.
|
|
* 28-Oct-2000 mohamed Added GetSystemMetrics and SystemParametersInfo hooks.
|
|
\***************************************************************************/
|
|
void ResetUserApiHook(USERAPIHOOK * puah)
|
|
{
|
|
puah->cbSize = sizeof(USERAPIHOOK);
|
|
puah->pfnDefWindowProcA = RealDefWindowProcA;
|
|
puah->pfnDefWindowProcW = RealDefWindowProcW;
|
|
puah->mmDWP.rgb = NULL;
|
|
puah->mmDWP.cb = 0;
|
|
puah->pfnGetScrollInfo = RealGetScrollInfo;
|
|
puah->pfnSetScrollInfo = RealSetScrollInfo;
|
|
puah->pfnEnableScrollBar = RealEnableScrollBar;
|
|
puah->pfnAdjustWindowRectEx = RealAdjustWindowRectEx;
|
|
puah->pfnSetWindowRgn = RealSetWindowRgn;
|
|
puah->uoiWnd.pfnBeforeOWP = DefaultOWP;
|
|
puah->uoiWnd.pfnAfterOWP = DefaultOWP;
|
|
puah->uoiWnd.mm.rgb = NULL;
|
|
puah->uoiWnd.mm.cb = 0;
|
|
puah->uoiDlg.pfnBeforeOWP = DefaultOWP;
|
|
puah->uoiDlg.pfnAfterOWP = DefaultOWP;
|
|
puah->uoiDlg.mm.rgb = NULL;
|
|
puah->uoiDlg.mm.cb = 0;
|
|
puah->pfnGetSystemMetrics = RealGetSystemMetrics;
|
|
puah->pfnSystemParametersInfoA = RealSystemParametersInfoA;
|
|
puah->pfnSystemParametersInfoW = RealSystemParametersInfoW;
|
|
puah->pfnForceResetUserApiHook = ForceResetUserApiHook;
|
|
puah->pfnDrawFrameControl = RealDrawFrameControl;
|
|
puah->pfnDrawCaption = RealDrawCaption;
|
|
puah->pfnMDIRedrawFrame = RealMDIRedrawFrame;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ForceResetUserApiHook
|
|
*
|
|
* NOTE : This is an API function that is called by external clients.
|
|
*
|
|
* This function gets called to force a reset of the UserApiHook function
|
|
* pointers. This is done usually by the hooked DLL in the case when it is
|
|
* about to be unloaded by anyone other than WindowManager and is done to
|
|
* prevent future calls into that DLL. It is to be noted however, that
|
|
* WindowManger still believes that it is hooked
|
|
*
|
|
* Note:
|
|
* 1- This function is meant to be called only by hooking dll inside
|
|
* DllMain when recieving a DLL_PROCESS_DETACH message. A check is
|
|
* made on the LoaderLock to verify.
|
|
* 2- No serialization is done based on the assumption that the call
|
|
* is made during DLL_PROCESS_DETACH and hence there is only the
|
|
* main thread.
|
|
*
|
|
* History:
|
|
* 02-Feb-2001 mohamed Created.
|
|
\***************************************************************************/
|
|
BOOL
|
|
ForceResetUserApiHook(
|
|
HMODULE hmod)
|
|
{
|
|
/*
|
|
* Verify that the calling module is indeed the same hooking module and that
|
|
* we are indeed being called from DllMain by verifying that we are inside
|
|
* the loader lock.
|
|
*/
|
|
|
|
if (ghmodUserApiHook != hmod || !RtlIsThreadWithinLoaderCallout()){
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Reset the function pointers back to Window Manager native functions and
|
|
* reset the global dll initialization function pointer to prevent calls
|
|
* into hooked dll with un\initialization messages.
|
|
*/
|
|
|
|
ResetUserApiHook(&guah);
|
|
gpfnInitUserApi = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* _EndUserApiHook
|
|
*
|
|
* This function gets called after each hooked API function call from
|
|
* END_USERAPIHOOK(). This provides a common place to clean up resources
|
|
* that were delayed because they were in use during the hooked function
|
|
* call.
|
|
*
|
|
* History:
|
|
* 28-Mar-2000 JStall Created.
|
|
* 16-May-2000 JStall Changed to support uninitialize callback
|
|
\***************************************************************************/
|
|
void _EndUserApiHook()
|
|
{
|
|
UserAssert(gcCallUserApiHook > 0);
|
|
if (InterlockedDecrement(&gcCallUserApiHook) == 0) {
|
|
/*
|
|
* If the load count went to zero, free the library.
|
|
*/
|
|
if (gcLoadUserApiHook == 0) {
|
|
HMODULE hmod = NULL;
|
|
INITUSERAPIHOOK pfnInitUserApi = NULL;
|
|
|
|
RtlEnterCriticalSection(&gcsUserApiHook);
|
|
if (gcLoadUserApiHook == 0) {
|
|
UserAssertMsg0(ghmodUserApiHook != NULL, "Should still have valid ghmodUserApiHook");
|
|
UserAssertMsg0(gpfnInitUserApi != NULL, "Should still have valid gpfnInitUserApi");
|
|
|
|
hmod = ghmodUserApiHook;
|
|
pfnInitUserApi = gpfnInitUserApi;
|
|
ghmodUserApiHook = NULL;
|
|
gpfnInitUserApi = NULL;
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&gcsUserApiHook);
|
|
|
|
/*
|
|
* Make the callback that we delayed from ClearUserApiHook()
|
|
* because there was still an outstanding API call.
|
|
*/
|
|
if (pfnInitUserApi != NULL) {
|
|
RIPMSG2(RIP_WARNING, "Uninit from End Load %lx Call %lx", gcLoadUserApiHook, gcCallUserApiHook);
|
|
pfnInitUserApi(UIAH_UNINITIALIZE, NULL);
|
|
}
|
|
|
|
if (hmod != NULL) {
|
|
FreeLibrary(hmod);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DefWindowProcWorker
|
|
*
|
|
* Handles any messages that can be dealt with wholly on the client and
|
|
* passes the rest to the server.
|
|
*
|
|
* 03-31-92 DarrinM Created.
|
|
\***************************************************************************/
|
|
|
|
LRESULT DefWindowProcWorker(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
DWORD fAnsi)
|
|
{
|
|
if (ghmodUserApiHook) {
|
|
if (fAnsi) {
|
|
return DefWindowProcA(HWq(pwnd), message, wParam, lParam);
|
|
} else {
|
|
return DefWindowProcW(HWq(pwnd), message, wParam, lParam);
|
|
}
|
|
}
|
|
return RealDefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
|
|
}
|
|
|
|
LRESULT RealDefWindowProcWorker(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
DWORD fAnsi)
|
|
{
|
|
HWND hwnd = HWq(pwnd);
|
|
int icolBack;
|
|
int icolFore;
|
|
PWND pwndParent;
|
|
HWND hwndDefIme;
|
|
PWND pwndDefIme;
|
|
PIMEUI pimeui;
|
|
|
|
#if DBG
|
|
if (!gfTurboDWP) {
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_DEFWINDOWPROC, fAnsi);
|
|
} else {
|
|
#endif
|
|
|
|
if (FDEFWINDOWMSG(message, DefWindowMsgs)) {
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_DEFWINDOWPROC, fAnsi);
|
|
} else if (!FDEFWINDOWMSG(message, DefWindowSpecMsgs)) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Important: If you add cases to the switch statement below,
|
|
* add the messages to server.c's gawDefWindowSpecMsgs.
|
|
* Similarly if you add cases to dwp.c's DefWindowProc
|
|
* which can come from the client, add the messages
|
|
* to gawDefWindowMsgs.
|
|
*/
|
|
|
|
switch (message) {
|
|
#ifdef LAME_BUTTON
|
|
case WM_NCLBUTTONDOWN:
|
|
if (wParam == HTLAMEBUTTON && TestWF(pwnd, WEFLAMEBUTTON)) {
|
|
CallLameButtonHandler(pwnd, hwnd);
|
|
}
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_DEFWINDOWPROC, fAnsi);
|
|
|
|
case WM_SYSCHAR:
|
|
if (wParam == LAMEBUTTONHOTKEY && (HIWORD(lParam) & SYS_ALTERNATE) &&
|
|
TestWF(pwnd, WEFLAMEBUTTON)) {
|
|
CallLameButtonHandler(pwnd, hwnd);
|
|
}
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_DEFWINDOWPROC, fAnsi);
|
|
|
|
case WM_SYSCOMMAND:
|
|
if (wParam == SC_LAMEBUTTON && TestWF(pwnd, WEFLAMEBUTTON)) {
|
|
CallLameButtonHandler(pwnd, hwnd);
|
|
}
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_DEFWINDOWPROC, fAnsi);
|
|
#endif // LAME_BUTTON
|
|
|
|
case WM_HELP:
|
|
{
|
|
PWND pwndDest;
|
|
|
|
/*
|
|
* If this window is a child window, Help message must be passed on
|
|
* to it's parent; Else, this must be passed on to the owner window.
|
|
*/
|
|
pwndDest = (TestwndChild(pwnd) ? pwnd->spwndParent : pwnd->spwndOwner);
|
|
if (pwndDest) {
|
|
pwndDest = REBASEPTR(pwnd, pwndDest);
|
|
if (pwndDest != _GetDesktopWindow())
|
|
return SendMessageW(HWq(pwndDest), WM_HELP, wParam, lParam);;
|
|
}
|
|
return(0L);
|
|
}
|
|
|
|
case WM_MOUSEWHEEL:
|
|
if (TestwndChild(pwnd)) {
|
|
pwndParent = REBASEPWND(pwnd, spwndParent);
|
|
SendMessageW(HW(pwndParent), WM_MOUSEWHEEL, wParam, lParam);
|
|
}
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
if (TestwndChild(pwnd)) {
|
|
pwndParent = REBASEPWND(pwnd, spwndParent);
|
|
SendMessageW(HW(pwndParent), WM_CONTEXTMENU,
|
|
(WPARAM)hwnd, lParam);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Default handling for WM_CONTEXTMENU support
|
|
*/
|
|
case WM_RBUTTONUP:
|
|
if (TestWF(pwnd, WEFLAYOUTRTL)) {
|
|
lParam = MAKELONG(pwnd->rcClient.right - GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) + pwnd->rcClient.top);
|
|
} else {
|
|
lParam = MAKELONG(GET_X_LPARAM(lParam) + pwnd->rcClient.left, GET_Y_LPARAM(lParam) + pwnd->rcClient.top);
|
|
}
|
|
SendMessageWorker(pwnd, WM_CONTEXTMENU, (WPARAM)hwnd, lParam, fAnsi);
|
|
break;
|
|
|
|
case WM_APPCOMMAND:
|
|
if (TestwndChild(pwnd)) {
|
|
/*
|
|
* Bubble the message to the parent
|
|
*/
|
|
pwndParent = REBASEPWND(pwnd, spwndParent);
|
|
return SendMessageW(HW(pwndParent), WM_APPCOMMAND, wParam, lParam);
|
|
} else {
|
|
/*
|
|
* Call the server side to send the shell hook HSHELL_APPCOMMAND
|
|
*/
|
|
return CsSendMessage(hwnd, WM_APPCOMMAND, wParam, lParam, 0L, FNID_DEFWINDOWPROC, fAnsi);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Default handling for WM_APPCOMMAND support
|
|
*/
|
|
case WM_NCXBUTTONUP:
|
|
case WM_XBUTTONUP:
|
|
{
|
|
WORD cmd;
|
|
WORD keystate;
|
|
LPARAM lParamAppCommand;
|
|
|
|
switch (GET_XBUTTON_WPARAM(wParam)) {
|
|
case XBUTTON1:
|
|
cmd = APPCOMMAND_BROWSER_BACKWARD;
|
|
break;
|
|
|
|
case XBUTTON2:
|
|
cmd = APPCOMMAND_BROWSER_FORWARD;
|
|
break;
|
|
|
|
default:
|
|
cmd = 0;
|
|
break;
|
|
}
|
|
|
|
if (cmd == 0) {
|
|
break;
|
|
}
|
|
|
|
cmd |= FAPPCOMMAND_MOUSE;
|
|
if (message == WM_XBUTTONUP) {
|
|
keystate = GET_KEYSTATE_WPARAM(wParam);
|
|
} else {
|
|
keystate = GetMouseKeyState();
|
|
}
|
|
|
|
lParamAppCommand = MAKELPARAM(keystate, cmd);
|
|
SendMessageWorker(pwnd, WM_APPCOMMAND, (WPARAM)hwnd, lParamAppCommand, fAnsi);
|
|
break;
|
|
}
|
|
|
|
case WM_WINDOWPOSCHANGED: {
|
|
PWINDOWPOS ppos = (PWINDOWPOS)lParam;
|
|
|
|
if (!(ppos->flags & SWP_NOCLIENTMOVE)) {
|
|
POINT pt = {pwnd->rcClient.left, pwnd->rcClient.top};
|
|
pwndParent = REBASEPWND(pwnd, spwndParent);
|
|
|
|
if (pwndParent != _GetDesktopWindow()) {
|
|
pt.x -= pwndParent->rcClient.left;
|
|
pt.y -= pwndParent->rcClient.top;
|
|
}
|
|
|
|
SendMessageWorker(pwnd, WM_MOVE, FALSE, MAKELPARAM(pt.x, pt.y), fAnsi);
|
|
}
|
|
|
|
if ((ppos->flags & SWP_STATECHANGE) || !(ppos->flags & SWP_NOCLIENTSIZE)) {
|
|
UINT cmd;
|
|
RECT rc;
|
|
|
|
if (TestWF(pwnd, WFMINIMIZED))
|
|
cmd = SIZEICONIC;
|
|
else if (TestWF(pwnd, WFMAXIMIZED))
|
|
cmd = SIZEFULLSCREEN;
|
|
else
|
|
cmd = SIZENORMAL;
|
|
|
|
/*
|
|
* HACK ALERT:
|
|
* If the window is minimized then the real client width and height are
|
|
* zero. But, in win3.1 they were non-zero. Under Chicago, PrintShop
|
|
* Deluxe ver 1.2 hits a divide by zero. To fix this we fake the width
|
|
* and height for old apps to be non-zero values.
|
|
* GetClientRect does that job for us.
|
|
*/
|
|
_GetClientRect(pwnd, &rc);
|
|
SendMessageWorker(pwnd, WM_SIZE, cmd,
|
|
MAKELONG(rc.right - rc.left,
|
|
rc.bottom - rc.top), fAnsi);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case WM_MOUSEACTIVATE: {
|
|
PWND pwndT;
|
|
LRESULT lt;
|
|
|
|
/*
|
|
* GetChildParent returns either a kernel pointer or NULL.
|
|
*/
|
|
pwndT = GetChildParent(pwnd);
|
|
if (pwndT != NULL) {
|
|
pwndT = REBASEPTR(pwnd, pwndT);
|
|
lt = SendMessageWorker(pwndT, WM_MOUSEACTIVATE, wParam, lParam, fAnsi);
|
|
if (lt != 0)
|
|
return lt;
|
|
}
|
|
|
|
/*
|
|
* Moving, sizing or minimizing? Activate AFTER we take action.
|
|
*/
|
|
return ((LOWORD(lParam) == HTCAPTION) && (HIWORD(lParam) == WM_LBUTTONDOWN )) ?
|
|
(LONG)MA_NOACTIVATE : (LONG)MA_ACTIVATE;
|
|
}
|
|
|
|
case WM_CTLCOLORSCROLLBAR:
|
|
if ((gpsi->BitCount < 8) ||
|
|
(SYSRGB(3DHILIGHT) != SYSRGB(SCROLLBAR)) ||
|
|
(SYSRGB(3DHILIGHT) == SYSRGB(WINDOW)))
|
|
{
|
|
/*
|
|
* Remove call to UnrealizeObject(). GDI Handles this for
|
|
* brushes on NT.
|
|
*
|
|
* UnrealizeObject(ghbrGray);
|
|
*/
|
|
|
|
SetBkColor((HDC)wParam, SYSRGB(3DHILIGHT));
|
|
SetTextColor((HDC)wParam, SYSRGB(3DFACE));
|
|
return((LRESULT)gpsi->hbrGray);
|
|
}
|
|
|
|
icolBack = COLOR_3DHILIGHT;
|
|
icolFore = COLOR_BTNTEXT;
|
|
goto SetColor;
|
|
|
|
case WM_CTLCOLORBTN:
|
|
if (pwnd == NULL)
|
|
goto ColorDefault;
|
|
|
|
if (TestWF(pwnd, WFWIN40COMPAT)) {
|
|
icolBack = COLOR_3DFACE;
|
|
icolFore = COLOR_BTNTEXT;
|
|
} else {
|
|
goto ColorDefault;
|
|
}
|
|
goto SetColor;
|
|
|
|
case WM_CTLCOLORSTATIC:
|
|
case WM_CTLCOLORDLG:
|
|
case WM_CTLCOLORMSGBOX:
|
|
// We want static controls in dialogs to have the 3D
|
|
// background color, but statics in windows to inherit
|
|
// their parents' background.
|
|
|
|
if (pwnd == NULL)
|
|
goto ColorDefault;
|
|
|
|
if (TestWF(pwnd, WFWIN40COMPAT)) {
|
|
icolBack = COLOR_3DFACE;
|
|
icolFore = COLOR_WINDOWTEXT;
|
|
goto SetColor;
|
|
}
|
|
// ELSE FALL THRU...
|
|
|
|
case WM_CTLCOLOR: // here for WOW only
|
|
case WM_CTLCOLORLISTBOX:
|
|
case WM_CTLCOLOREDIT:
|
|
ColorDefault:
|
|
icolBack = COLOR_WINDOW;
|
|
icolFore = COLOR_WINDOWTEXT;
|
|
|
|
SetColor:
|
|
{
|
|
SetBkColor((HDC)wParam, gpsi->argbSystem[icolBack]);
|
|
SetTextColor((HDC)wParam, gpsi->argbSystem[icolFore]);
|
|
return (LRESULT)(SYSHBRUSH(icolBack));
|
|
}
|
|
|
|
case WM_NCHITTEST:
|
|
return FindNCHit(pwnd, (LONG)lParam);
|
|
|
|
case WM_GETTEXT:
|
|
if (wParam != 0) {
|
|
|
|
LPWSTR lpszText;
|
|
UINT cchSrc;
|
|
|
|
if (pwnd->strName.Length) {
|
|
|
|
lpszText = REBASE(pwnd, strName.Buffer);
|
|
cchSrc = (UINT)pwnd->strName.Length / sizeof(WCHAR);
|
|
|
|
if (fAnsi) {
|
|
|
|
LPSTR lpName = (LPSTR)lParam;
|
|
|
|
/*
|
|
* Non-zero retval means some text to copy out. Do not
|
|
* copy out more than the requested byte count
|
|
* 'chMaxCount'.
|
|
*/
|
|
cchSrc = WCSToMB(lpszText,
|
|
cchSrc,
|
|
(LPSTR *)&lpName,
|
|
(UINT)(wParam - 1),
|
|
FALSE);
|
|
|
|
lpName[cchSrc] = '\0';
|
|
|
|
} else {
|
|
|
|
LPWSTR lpwName = (LPWSTR)lParam;
|
|
|
|
cchSrc = min(cchSrc, (UINT)(wParam - 1));
|
|
RtlCopyMemory(lpwName, lpszText, cchSrc * sizeof(WCHAR));
|
|
lpwName[cchSrc] = 0;
|
|
}
|
|
|
|
return cchSrc;
|
|
}
|
|
|
|
/*
|
|
* else Null terminate the text buffer since there is no text.
|
|
*/
|
|
if (fAnsi) {
|
|
((LPSTR)lParam)[0] = 0;
|
|
} else {
|
|
((LPWSTR)lParam)[0] = 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
case WM_GETTEXTLENGTH:
|
|
if (pwnd->strName.Length) {
|
|
UINT cch;
|
|
if (fAnsi) {
|
|
RtlUnicodeToMultiByteSize(&cch,
|
|
REBASE(pwnd, strName.Buffer),
|
|
pwnd->strName.Length);
|
|
} else {
|
|
cch = pwnd->strName.Length / sizeof(WCHAR);
|
|
}
|
|
return cch;
|
|
}
|
|
return 0L;
|
|
|
|
case WM_QUERYDRAGICON:
|
|
/*
|
|
* If the window is WIN40COMPAT or has a kernel side procedure
|
|
* do not attempt to look into the instance module
|
|
*/
|
|
if (TestWF(pwnd, WFWIN40COMPAT) || TestWF(pwnd, WFSERVERSIDEPROC)) {
|
|
return 0;
|
|
}
|
|
/*
|
|
* For old apps, like the VB3 ones, try to load the icon from resources
|
|
* This is how Win95 does.
|
|
*/
|
|
return (LRESULT)LoadIconW(KHANDLE_TO_HANDLE(pwnd->hModule), MAKEINTRESOURCE(1));
|
|
|
|
case WM_QUERYOPEN:
|
|
case WM_QUERYENDSESSION:
|
|
case WM_DEVICECHANGE:
|
|
case WM_POWERBROADCAST:
|
|
return TRUE;
|
|
|
|
case WM_KEYDOWN:
|
|
if (wParam == VK_F10) {
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_DEFWINDOWPROC, fAnsi);
|
|
}
|
|
break;
|
|
|
|
case WM_SYSKEYDOWN:
|
|
if ((HIWORD(lParam) & SYS_ALTERNATE) || (wParam == VK_F10) ||
|
|
(wParam == VK_ESCAPE))
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_DEFWINDOWPROC, fAnsi);
|
|
break;
|
|
|
|
case WM_UNICHAR:
|
|
if (wParam == UNICODE_NOCHAR) {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case WM_CHARTOITEM:
|
|
case WM_VKEYTOITEM:
|
|
/*
|
|
* Do default processing for keystrokes into owner draw listboxes.
|
|
*/
|
|
return -1;
|
|
|
|
case WM_ACTIVATE:
|
|
if (LOWORD(wParam))
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_DEFWINDOWPROC, fAnsi);
|
|
break;
|
|
|
|
case WM_SHOWWINDOW:
|
|
if (lParam != 0)
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_DEFWINDOWPROC, fAnsi);
|
|
break;
|
|
|
|
case WM_DROPOBJECT:
|
|
return DO_DROPFILE;
|
|
|
|
case WM_WINDOWPOSCHANGING:
|
|
/*
|
|
* If the window's size is changing, adjust the passed-in size
|
|
*/
|
|
#define ppos ((WINDOWPOS *)lParam)
|
|
if (!(ppos->flags & SWP_NOSIZE))
|
|
return CsSendMessage(hwnd, message, wParam, lParam, 0L,
|
|
FNID_DEFWINDOWPROC, fAnsi);
|
|
#undef ppos
|
|
break;
|
|
|
|
case WM_KLUDGEMINRECT:
|
|
{
|
|
SHELLHOOKINFO shi;
|
|
LPRECT lprc = (LPRECT)lParam;
|
|
|
|
shi.hwnd = (HWND)wParam;
|
|
shi.rc.left = MAKELONG(lprc->left, lprc->top);
|
|
shi.rc.top = MAKELONG(lprc->right, lprc->bottom);
|
|
|
|
if (gpsi->uiShellMsg == 0)
|
|
SetTaskmanWindow(NULL);
|
|
if (SendMessageWorker(pwnd, gpsi->uiShellMsg, HSHELL_GETMINRECT,
|
|
(LPARAM)&shi, fAnsi)) {
|
|
//
|
|
// Now convert the RECT back from two POINTS structures into two POINT
|
|
// structures.
|
|
//
|
|
lprc->left = (SHORT)LOWORD(shi.rc.left); // Sign extend
|
|
lprc->top = (SHORT)HIWORD(shi.rc.left); // Sign extend
|
|
lprc->right = (SHORT)LOWORD(shi.rc.top); // Sign extend
|
|
lprc->bottom = (SHORT)HIWORD(shi.rc.top); // Sign extend
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFYFORMAT:
|
|
if (lParam == NF_QUERY)
|
|
return(TestWF(pwnd, WFANSICREATOR) ? NFR_ANSI : NFR_UNICODE);
|
|
break;
|
|
|
|
case WM_IME_KEYDOWN:
|
|
if (fAnsi)
|
|
PostMessageA(hwnd, WM_KEYDOWN, wParam, lParam);
|
|
else
|
|
PostMessageW(hwnd, WM_KEYDOWN, wParam, lParam);
|
|
break;
|
|
|
|
case WM_IME_KEYUP:
|
|
if (fAnsi)
|
|
PostMessageA(hwnd, WM_KEYUP, wParam, lParam);
|
|
else
|
|
PostMessageW(hwnd, WM_KEYUP, wParam, lParam);
|
|
break;
|
|
|
|
case WM_IME_CHAR:
|
|
//if (TestCF(pwnd, CFIME))
|
|
// break;
|
|
|
|
if ( fAnsi ) {
|
|
if( IsDBCSLeadByteEx(THREAD_CODEPAGE(),(BYTE)(wParam >> 8)) ) {
|
|
PostMessageA(hwnd,
|
|
WM_CHAR,
|
|
(WPARAM)((BYTE)(wParam >> 8)), // leading byte
|
|
1L);
|
|
PostMessageA(hwnd,
|
|
WM_CHAR,
|
|
(WPARAM)((BYTE)wParam), // trailing byte
|
|
1L);
|
|
}
|
|
else
|
|
PostMessageA(hwnd,
|
|
WM_CHAR,
|
|
(WPARAM)(wParam),
|
|
1L);
|
|
} else {
|
|
PostMessageW(hwnd, WM_CHAR, wParam, 1L);
|
|
}
|
|
break;
|
|
|
|
case WM_IME_COMPOSITION:
|
|
//if (TestCF(pwnd, CFIME))
|
|
// break;
|
|
|
|
if (lParam & GCS_RESULTSTR) {
|
|
HIMC hImc;
|
|
DWORD cbLen;
|
|
|
|
if ((hImc = fpImmGetContext(hwnd)) == NULL_HIMC)
|
|
goto dwpime_ToIMEWnd_withchk;
|
|
|
|
if (fAnsi) {
|
|
LPSTR pszBuffer, psz;
|
|
|
|
/*
|
|
* ImmGetComposition returns the size of buffer needed in byte.
|
|
*/
|
|
if (!(cbLen = fpImmGetCompositionStringA(hImc, GCS_RESULTSTR, NULL, 0))) {
|
|
fpImmReleaseContext(hwnd, hImc);
|
|
goto dwpime_ToIMEWnd_withchk;
|
|
}
|
|
|
|
pszBuffer = psz = (LPSTR)UserLocalAlloc(HEAP_ZERO_MEMORY,
|
|
cbLen + sizeof(CHAR));
|
|
|
|
if (pszBuffer == NULL) {
|
|
fpImmReleaseContext(hwnd, hImc);
|
|
goto dwpime_ToIMEWnd_withchk;
|
|
}
|
|
|
|
fpImmGetCompositionStringA(hImc, GCS_RESULTSTR, psz, cbLen);
|
|
|
|
while (*psz) {
|
|
if (IsDBCSLeadByteEx(THREAD_CODEPAGE(),*psz)) {
|
|
if (*(psz+1)) {
|
|
SendMessageA( hwnd,
|
|
WM_IME_CHAR,
|
|
MAKEWPARAM(MAKEWORD(*(psz+1), *psz), 0),
|
|
1L );
|
|
psz++;
|
|
}
|
|
psz++;
|
|
}
|
|
else
|
|
SendMessageA( hwnd,
|
|
WM_IME_CHAR,
|
|
MAKEWPARAM(MAKEWORD(*(psz++), 0), 0),
|
|
1L );
|
|
}
|
|
|
|
UserLocalFree(pszBuffer);
|
|
|
|
fpImmReleaseContext(hwnd, hImc);
|
|
}
|
|
else {
|
|
LPWSTR pwszBuffer, pwsz;
|
|
|
|
/*
|
|
* ImmGetComposition returns the size of buffer needed in byte
|
|
*/
|
|
if (!(cbLen = fpImmGetCompositionStringW(hImc, GCS_RESULTSTR, NULL, 0))) {
|
|
fpImmReleaseContext(hwnd, hImc);
|
|
goto dwpime_ToIMEWnd_withchk;
|
|
}
|
|
|
|
pwszBuffer = pwsz = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY,
|
|
cbLen + sizeof(WCHAR));
|
|
|
|
if (pwszBuffer == NULL) {
|
|
fpImmReleaseContext(hwnd, hImc);
|
|
goto dwpime_ToIMEWnd_withchk;
|
|
}
|
|
|
|
fpImmGetCompositionStringW(hImc, GCS_RESULTSTR, pwsz, cbLen);
|
|
|
|
while (*pwsz)
|
|
SendMessageW(hwnd, WM_IME_CHAR, MAKEWPARAM(*pwsz++, 0), 1L);
|
|
|
|
UserLocalFree(pwszBuffer);
|
|
|
|
fpImmReleaseContext(hwnd, hImc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fall through to send to Default IME Window with checking
|
|
* activated hIMC.
|
|
*/
|
|
|
|
case WM_IME_STARTCOMPOSITION:
|
|
case WM_IME_ENDCOMPOSITION:
|
|
dwpime_ToIMEWnd_withchk:
|
|
//if (TestCF(pwnd, CFIME))
|
|
// break;
|
|
|
|
if (GetClientInfo()->dwTIFlags & TIF_DISABLEIME) {
|
|
break;
|
|
}
|
|
/*
|
|
* We assume this Wnd uses DefaultIMEWindow.
|
|
* If this window has its own IME window, it have to call
|
|
* ImmIsUIMessage()....
|
|
*/
|
|
hwndDefIme = fpImmGetDefaultIMEWnd(hwnd);
|
|
|
|
if (hwndDefIme == hwnd) {
|
|
/*
|
|
* VC++ 1.51 TLW0NCL.DLL subclass IME class window
|
|
* and pass IME messages to DefWindowProc().
|
|
*/
|
|
RIPMSG1(RIP_WARNING,
|
|
"IME Class window is hooked and IME message [%X] are sent to DefWindowProc",
|
|
message);
|
|
ImeWndProcWorker(pwnd, message, wParam, lParam, fAnsi);
|
|
break;
|
|
}
|
|
|
|
if ((pwndDefIme = ValidateHwndNoRip(hwndDefIme)) != NULL) {
|
|
/*
|
|
* If hImc of this window is not activated for IME window,
|
|
* we don't send WM_IME_NOTIFY.
|
|
*/
|
|
pimeui = ((PIMEWND)pwndDefIme)->pimeui;
|
|
if (pimeui->hIMC == fpImmGetContext(hwnd))
|
|
return SendMessageWorker(pwndDefIme, message, wParam, lParam, fAnsi);
|
|
else
|
|
RIPMSG1(RIP_WARNING,
|
|
"DefWindowProc can not send WM_IME_message [%X] now",
|
|
message);
|
|
}
|
|
break;
|
|
|
|
dwpime_ToTopLevel_withchk:
|
|
//if (TestCF(pwnd, CFIME))
|
|
// break;
|
|
|
|
/*
|
|
* We assume this Wnd uses DefaultIMEWindow.
|
|
* If this window has its own IME window, it have to call
|
|
* ImmIsUIMessage()....
|
|
*/
|
|
hwndDefIme = fpImmGetDefaultIMEWnd(hwnd);
|
|
|
|
if (hwndDefIme == hwnd) {
|
|
/*
|
|
* VC++ 1.51 TLW0NCL.DLL subclass IME class window
|
|
* and pass IME messages to DefWindowProc().
|
|
*/
|
|
RIPMSG1(RIP_WARNING,
|
|
"IME Class window is hooked and IME message [%X] are sent to DefWindowProc",
|
|
message);
|
|
ImeWndProcWorker(pwnd, message, wParam, lParam, fAnsi);
|
|
break;
|
|
}
|
|
|
|
pwndDefIme = ValidateHwndNoRip(hwndDefIme);
|
|
|
|
if ((pwndDefIme = ValidateHwndNoRip(hwndDefIme)) != NULL) {
|
|
PWND pwndT, pwndParent;
|
|
|
|
pwndT = pwnd;
|
|
|
|
while (TestwndChild(pwndT)) {
|
|
pwndParent = REBASEPWND(pwndT, spwndParent);
|
|
if (GETPTI(pwndParent) != GETPTI(pwnd))
|
|
break;
|
|
pwndT = pwndParent;
|
|
}
|
|
|
|
/*
|
|
* If hImc of this window is not activated for IME window,
|
|
* we don't send WM_IME_NOTIFY.
|
|
*/
|
|
if (pwndT != pwnd) {
|
|
pimeui = ((PIMEWND)pwndDefIme)->pimeui;
|
|
if (pimeui->hIMC == fpImmGetContext(hwnd))
|
|
return SendMessageWorker(pwndT, message, wParam, lParam, fAnsi);
|
|
else
|
|
RIPMSG1(RIP_WARNING,
|
|
"DefWindowProc can not send WM_IME_message [%X] now",
|
|
message);
|
|
}
|
|
else {
|
|
/*
|
|
* Review !!
|
|
* If this is the toplevel window, we pass messages to
|
|
* the default IME window...
|
|
*/
|
|
return SendMessageWorker(pwndDefIme, message, wParam, lParam, fAnsi);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_IME_NOTIFY:
|
|
switch (wParam) {
|
|
case IMN_OPENSTATUSWINDOW:
|
|
case IMN_CLOSESTATUSWINDOW:
|
|
#ifndef WKWOK_DEBUG
|
|
goto dwpime_ToIMEWnd_withchk;
|
|
#endif
|
|
goto dwpime_ToTopLevel_withchk;
|
|
|
|
default:
|
|
goto dwpime_ToIMEWnd_withchk;
|
|
}
|
|
break;
|
|
|
|
case WM_IME_REQUEST:
|
|
switch (wParam) {
|
|
case IMR_QUERYCHARPOSITION:
|
|
goto dwpime_ToIMEWnd_withchk;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_IME_SYSTEM:
|
|
if (wParam == IMS_SETACTIVECONTEXT) {
|
|
RIPMSG0(RIP_WARNING, "DefWindowProc received unexpected WM_IME_SYSTEM");
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* IMS_SETOPENSTATUS is depended on the activated input context.
|
|
* It needs to be sent to only the activated system window.
|
|
*/
|
|
if (wParam == IMS_SETOPENSTATUS)
|
|
goto dwpime_ToIMEWnd_withchk;
|
|
|
|
/*
|
|
* Fall through to send to Default IME Window.
|
|
*/
|
|
|
|
case WM_IME_SETCONTEXT:
|
|
//if (TestCF(pwnd, CFIME))
|
|
// break;
|
|
|
|
hwndDefIme = fpImmGetDefaultIMEWnd(hwnd);
|
|
|
|
if (hwndDefIme == hwnd) {
|
|
/*
|
|
* VC++ 1.51 TLW0NCL.DLL subclass IME class window
|
|
* and pass IME messages to DefWindowProc().
|
|
*/
|
|
RIPMSG1(RIP_WARNING,
|
|
"IME Class window is hooked and IME message [%X] are sent to DefWindowProc",
|
|
message);
|
|
ImeWndProcWorker(pwnd, message, wParam, lParam, fAnsi);
|
|
break;
|
|
}
|
|
|
|
if ((pwndDefIme = ValidateHwndNoRip(hwndDefIme)) != NULL)
|
|
return SendMessageWorker(pwndDefIme, message, wParam, lParam, fAnsi);
|
|
|
|
break;
|
|
|
|
case WM_IME_SELECT:
|
|
RIPMSG0(RIP_WARNING, "DefWindowProc should not receive WM_IME_SELECT");
|
|
break;
|
|
|
|
case WM_IME_COMPOSITIONFULL:
|
|
//if (TestCF(pwnd, CFIME))
|
|
// break;
|
|
|
|
if (GETAPPVER() < VER40) {
|
|
/*
|
|
* This is a temporary solution for win31app.
|
|
* FEREVIEW: For M5 this will call WINNLS message mapping logic
|
|
* -yutakan
|
|
*/
|
|
return SendMessageWorker(pwnd, WM_IME_REPORT,
|
|
IR_FULLCONVERT, (LPARAM)0L, fAnsi);
|
|
}
|
|
break;
|
|
|
|
case WM_CHANGEUISTATE:
|
|
{
|
|
WORD wAction = LOWORD(wParam);
|
|
WORD wFlags = HIWORD(wParam);
|
|
BOOL bRealChange = FALSE;
|
|
|
|
/*
|
|
* Validate parameters and determine the flags that should actually be changed.
|
|
*/
|
|
if ((wFlags & ~UISF_VALID) || (wAction > UIS_LASTVALID) || lParam) {
|
|
return 0;
|
|
}
|
|
|
|
if (wAction == UIS_INITIALIZE) {
|
|
wFlags = 0;
|
|
if (TEST_KbdCuesPUSIF) {
|
|
if (TEST_SRVIF(SRVIF_LASTRITWASKEYBOARD)) {
|
|
wAction = UIS_CLEAR;
|
|
} else {
|
|
wAction = UIS_SET;
|
|
}
|
|
wFlags = UISF_HIDEFOCUS | UISF_HIDEACCEL;
|
|
wParam = MAKEWPARAM(wAction, wFlags);
|
|
}
|
|
} else if (!TEST_KbdCuesPUSIF) {
|
|
wFlags &= ~(UISF_HIDEFOCUS | UISF_HIDEACCEL);
|
|
}
|
|
|
|
if (wFlags == 0) {
|
|
return 0;
|
|
}
|
|
|
|
UserAssert(wAction == UIS_SET || wAction == UIS_CLEAR);
|
|
/*
|
|
* If the state is not going to change, there's nothing to do here
|
|
*/
|
|
if (wFlags & UISF_HIDEFOCUS) {
|
|
bRealChange = (!!TestWF(pwnd, WEFPUIFOCUSHIDDEN)) ^ (wAction == UIS_SET);
|
|
}
|
|
if (wFlags & UISF_HIDEACCEL) {
|
|
bRealChange |= (!!TestWF(pwnd, WEFPUIACCELHIDDEN)) ^ (wAction == UIS_SET);
|
|
}
|
|
if (wFlags & UISF_ACTIVE) {
|
|
bRealChange |= (!!TestWF(pwnd, WEFPUIACTIVE)) ^ (wAction == UIS_SET);
|
|
}
|
|
|
|
if (!bRealChange) {
|
|
break;
|
|
}
|
|
/*
|
|
* Children pass this message up
|
|
* Top level windows update send down to themselves WM_UPDATEUISTATE.
|
|
* WM_UPDATEUISTATE will change the state bits and broadcast down the message
|
|
*/
|
|
if (TestwndChild(pwnd)) {
|
|
|
|
return SendMessageWorker(REBASEPWND(pwnd, spwndParent), WM_CHANGEUISTATE,
|
|
wParam, lParam, fAnsi);
|
|
} else {
|
|
return SendMessageWorker(pwnd, WM_UPDATEUISTATE, wParam, lParam, fAnsi);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case WM_QUERYUISTATE:
|
|
return (TestWF(pwnd, WEFPUIFOCUSHIDDEN) ? UISF_HIDEFOCUS : 0) |
|
|
(TestWF(pwnd, WEFPUIACCELHIDDEN) ? UISF_HIDEACCEL : 0) |
|
|
(TestWF(pwnd, WEFPUIACTIVE) ? UISF_ACTIVE : 0);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
|
|
#if DBG
|
|
} // gfTurboDWP
|
|
#endif
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* CallWindowProc
|
|
*
|
|
* Calls pfn with the passed message parameters. If pfn is a server-side
|
|
* window proc the server is called to deliver the message to the window.
|
|
* Currently we have the following restrictions:
|
|
*
|
|
* 04-17-91 DarrinM Created.
|
|
\***************************************************************************/
|
|
|
|
LRESULT WINAPI CallWindowProcAorW(
|
|
WNDPROC pfn,
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
BOOL bAnsi) // Denotes if input is Ansi or Unicode
|
|
{
|
|
PCALLPROCDATA pCPD;
|
|
PWND pwnd;
|
|
|
|
/*
|
|
* Raid# 78954: SPY++
|
|
*
|
|
* Under FE NT4.0 or NT5.0, the sytem sends WM_GETTEXTLENGTH
|
|
* corresponding to WM_xxxGETTEXT to optimize buffer allocation.
|
|
* This is really needed to avoid the buffer size inflation.
|
|
* For some reasons, Spy++ passes NULL as pfn to CallWindowProc
|
|
*
|
|
*/
|
|
if (pfn == NULL) {
|
|
RIPMSG0(RIP_WARNING, "CallWidowProcAorW(): pfn == NULL!");
|
|
return 0L;
|
|
}
|
|
|
|
// OPT!! check an ANSI\UNICODE table rather than fnDWORD
|
|
// OPT!! convert WM_CHAR family messages in line
|
|
|
|
/*
|
|
* Check if pfn is really a CallProcData Handle
|
|
* if it is and there is no ANSI data then convert the handle
|
|
* into an address; otherwise call the server for translation
|
|
*/
|
|
if (ISCPDTAG(pfn)) {
|
|
if (pCPD = HMValidateHandleNoRip((HANDLE)pfn, TYPE_CALLPROC)) {
|
|
if ((message >= WM_USER) || !MessageTable[message].bThunkMessage) {
|
|
pfn = (WNDPROC)pCPD->pfnClientPrevious;
|
|
} else {
|
|
return CsSendMessage(hwnd, message, wParam, lParam, (ULONG_PTR)pfn,
|
|
FNID_CALLWINDOWPROC, bAnsi);
|
|
}
|
|
} else {
|
|
RIPMSG1(RIP_WARNING, "CallWindowProc tried using a deleted CPD %#p\n", pfn);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
pwnd = ValidateHwnd(hwnd);
|
|
return UserCallWinProcCheckWow(PACTCTXT(pwnd), pfn, hwnd, message, wParam, lParam, NULL, FALSE);
|
|
}
|
|
|
|
|
|
FUNCLOG5(LOG_GENERAL, LRESULT, WINAPI, CallWindowProcA, WNDPROC, pfn, HWND, hwnd, UINT, message, WPARAM, wParam, LPARAM, lParam)
|
|
LRESULT WINAPI CallWindowProcA(
|
|
WNDPROC pfn,
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return CallWindowProcAorW(pfn, hwnd, message, wParam, lParam, TRUE);
|
|
}
|
|
|
|
FUNCLOG5(LOG_GENERAL, LRESULT, WINAPI, CallWindowProcW, WNDPROC, pfn, HWND, hwnd, UINT, message, WPARAM, wParam, LPARAM, lParam)
|
|
LRESULT WINAPI CallWindowProcW(
|
|
WNDPROC pfn,
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return CallWindowProcAorW(pfn, hwnd, message, wParam, lParam, FALSE);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MenuWindowProc
|
|
*
|
|
* Calls the sever-side function xxxMenuWindowProc
|
|
*
|
|
* 07-27-92 Mikehar Created.
|
|
\***************************************************************************/
|
|
|
|
|
|
FUNCLOG5(LOG_GENERAL, LRESULT, WINAPI, MenuWindowProcW, HWND, hwnd, HWND, hwndMDIClient, UINT, message, WPARAM, wParam, LPARAM, lParam)
|
|
LRESULT WINAPI MenuWindowProcW(
|
|
HWND hwnd,
|
|
HWND hwndMDIClient,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return CsSendMessage(hwnd, message, wParam, lParam,
|
|
(ULONG_PTR)hwndMDIClient, FNID_MENU, FALSE);
|
|
}
|
|
|
|
|
|
FUNCLOG5(LOG_GENERAL, LRESULT, WINAPI, MenuWindowProcA, HWND, hwnd, HWND, hwndMDIClient, UINT, message, WPARAM, wParam, LPARAM, lParam)
|
|
LRESULT WINAPI MenuWindowProcA(
|
|
HWND hwnd,
|
|
HWND hwndMDIClient,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return CsSendMessage(hwnd, message, wParam, lParam,
|
|
(ULONG_PTR)hwndMDIClient, FNID_MENU, TRUE);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _ClientGetListboxString
|
|
*
|
|
* This special function exists because LB_GETTEXT and CB_GETLBTEXT don't have
|
|
* buffer counts in them anywhere. Because there is no buffer count we have
|
|
* no idea how much room to reserved in the shared memory stack for this
|
|
* string to be copied into. The solution is to get the string length ahead
|
|
* of time, and send the message with this buffer length. Since this buffer
|
|
* length isn't a part of the original message, this routine is used for
|
|
* just this purpose.
|
|
*
|
|
* This routine gets called from the server.
|
|
*
|
|
* 04-13-91 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
DWORD WINAPI _ClientGetListboxString(
|
|
PWND pwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPSTR lParam, // May be a unicode or ANSI string
|
|
ULONG_PTR xParam,
|
|
PROC xpfn)
|
|
{
|
|
return ((DWORD)((GENERICPROC)xpfn)(pwnd, msg, wParam, (LPARAM)lParam, xParam));
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DispatchMessageWorker
|
|
*
|
|
* Handles any messages that can be dealt with wholly on the client and
|
|
* passes the rest to the server.
|
|
*
|
|
* 04-24-92 DarrinM Created.
|
|
\***************************************************************************/
|
|
LRESULT DispatchMessageWorker(
|
|
MSG *pmsg,
|
|
BOOL fAnsi)
|
|
{
|
|
PWND pwnd;
|
|
WPARAM wParamSaved;
|
|
LRESULT lRet;
|
|
BOOL bDoDbcsMessaging = FALSE;
|
|
|
|
/*
|
|
* Prevent apps from setting hi 16 bits so we can use them internally.
|
|
*/
|
|
if (pmsg->message & RESERVED_MSG_BITS) {
|
|
RIPERR1(ERROR_INVALID_PARAMETER,
|
|
RIP_WARNING,
|
|
"Invalid parameter \"pmsg->message\" (%ld) to DispatchMessageWorker",
|
|
pmsg->message);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (pmsg->hwnd != NULL) {
|
|
pwnd = ValidateHwnd(pmsg->hwnd);
|
|
if (pwnd == NULL) {
|
|
return 0;
|
|
}
|
|
pmsg->hwnd = HWq(pwnd); // get full 32-bit HWND in case this came from WoW
|
|
} else {
|
|
pwnd = NULL;
|
|
}
|
|
|
|
/*
|
|
* If this is a synchronous-only message (takes a pointer in wParam or
|
|
* lParam), then don't allow this message to go through since those
|
|
* parameters have not been thunked, and are pointing into outer-space
|
|
* (which would case exceptions to occur).
|
|
*
|
|
* (This api is only called in the context of a message loop, and you
|
|
* don't get synchronous-only messages in a message loop).
|
|
*/
|
|
if (TESTSYNCONLYMESSAGE(pmsg->message, pmsg->wParam)) {
|
|
/*
|
|
* Fail if 32 bit app is calling.
|
|
*/
|
|
if (!(GetClientInfo()->dwTIFlags & TIF_16BIT)) {
|
|
RIPERR0(ERROR_MESSAGE_SYNC_ONLY, RIP_WARNING, "DispatchMessageWorker: must be sync only");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* For wow apps, allow it to go through (for compatibility). Change
|
|
* the message id so our code doesn't understand the message - wow
|
|
* will get the message and strip out this bit before dispatching
|
|
* the message to the application.
|
|
*/
|
|
pmsg->message |= MSGFLAG_WOW_RESERVED;
|
|
}
|
|
|
|
/*
|
|
* Timer callbacks that don't go through window procs are sent with
|
|
* the callback address in lParam. Identify and dispatch those timers.
|
|
*/
|
|
if ((pmsg->message == WM_TIMER) || (pmsg->message == WM_SYSTIMER)) {
|
|
/*
|
|
* Console windows use WM_TIMER for the caret. However, they don't
|
|
* use a timer callback, so if this is CSRSS and there's a WM_TIMER
|
|
* for us, the only way lParam would be non-zero is if someone's trying
|
|
* to make us fault. No, this isn't a nice thing to do, but there
|
|
* are bad, bad people out there. Windows Bug #361246.
|
|
*/
|
|
if (pmsg->lParam != 0) {
|
|
/*
|
|
* System timers must be executed on the server's context.
|
|
*/
|
|
if (pmsg->message == WM_SYSTIMER) {
|
|
return NtUserDispatchMessage(pmsg);
|
|
} else if (!gfServerProcess) {
|
|
/*
|
|
* WM_TIMER with lParam could be an attack from
|
|
* malicious apps. To make sure the call is legitimate,
|
|
* let the kernel side validates it.
|
|
*/
|
|
if (!NtUserValidateTimerCallback(pmsg->lParam)) {
|
|
RIPMSGF3(RIP_WARNING, "invalid timer: hwnd=%p, wParam=%p, lParam=%p", pmsg->hwnd, pmsg->wParam, pmsg->lParam);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* We can't really trust what's in lParam, so make sure we
|
|
* handle any exceptions that occur during this call.
|
|
*/
|
|
try {
|
|
/*
|
|
* Windows NT Bug #234292.
|
|
* Since the called window/dialog proc may have a different
|
|
* calling convention, we must wrap the call and, check esp
|
|
* and replace with a good esp when the call returns. This
|
|
* is what UserCallWinProc* does.
|
|
*/
|
|
lRet = UserCallWinProc(PACTCTXT(pwnd),
|
|
(WNDPROC)pmsg->lParam,
|
|
pmsg->hwnd,
|
|
pmsg->message,
|
|
pmsg->wParam,
|
|
NtGetTickCount());
|
|
} except ((GetAppCompatFlags2(VER40) & GACF2_NO_TRYEXCEPT_CALLWNDPROC) ?
|
|
EXCEPTION_CONTINUE_SEARCH : W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
/*
|
|
* Windows NT Bug #359866.
|
|
* Some applications like Hagaki Studio 2000 need to handle
|
|
* the exception in WndProc in their handler, even though it
|
|
* skips the API calls. For those apps, we have to honor the
|
|
* behavior of NT4, with no protection.
|
|
*/
|
|
lRet = 0;
|
|
}
|
|
return lRet;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pwnd == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* To be safe (in case some bizarre app wants to look at the message
|
|
* again after dispatching it) save wParam so it can be restored after
|
|
* RtlMBMessageWParamCharToWCS() or RtlWCSMessageToMB() mangle it.
|
|
*/
|
|
wParamSaved = pmsg->wParam;
|
|
|
|
/*
|
|
* Pass messages intended for server-side windows over to the server.
|
|
* WM_PAINTs are passed over so the WFPAINTNOTPROCESSED code can be
|
|
* executed.
|
|
*/
|
|
if (TestWF(pwnd, WFSERVERSIDEPROC) || (pmsg->message == WM_PAINT)) {
|
|
if (fAnsi) {
|
|
/*
|
|
* Setup DBCS Messaging for WM_CHAR...
|
|
*/
|
|
BUILD_DBCS_MESSAGE_TO_SERVER_FROM_CLIENTA(pmsg->message,pmsg->wParam,TRUE);
|
|
|
|
/*
|
|
* Convert wParam to Unicode, if nessesary.
|
|
*/
|
|
RtlMBMessageWParamCharToWCS(pmsg->message, &pmsg->wParam);
|
|
}
|
|
lRet = NtUserDispatchMessage(pmsg);
|
|
pmsg->wParam = wParamSaved;
|
|
return lRet;
|
|
}
|
|
|
|
/*
|
|
* If the dispatcher and the receiver are both ANSI or both UNICODE
|
|
* then no message translation is necessary. NOTE: this test
|
|
* assumes that fAnsi is FALSE or TRUE, not just zero or non-zero.
|
|
*/
|
|
if (!fAnsi != !TestWF(pwnd, WFANSIPROC)) {
|
|
// before: if (fAnsi != ((TestWF(pwnd, WFANSIPROC)) ? TRUE : FALSE)) {
|
|
|
|
if (PtiCurrent() != GETPTI(pwnd)) {
|
|
RIPMSG0(RIP_WARNING, "message belongs to a different Q");
|
|
return 0;
|
|
}
|
|
|
|
if (fAnsi) {
|
|
/*
|
|
* Setup DBCS Messaging for WM_CHAR...
|
|
*/
|
|
BUILD_DBCS_MESSAGE_TO_CLIENTW_FROM_CLIENTA(pmsg->message,pmsg->wParam,TRUE);
|
|
|
|
/*
|
|
* Convert wParam to Unicode, if nessesary.
|
|
*/
|
|
RtlMBMessageWParamCharToWCS(pmsg->message, &pmsg->wParam);
|
|
} else {
|
|
/*
|
|
* Convert wParam to ANSI...
|
|
*/
|
|
RtlWCSMessageWParamCharToMB(pmsg->message, &pmsg->wParam);
|
|
|
|
/*
|
|
* Let's DBCS messaging for WM_CHAR....
|
|
*/
|
|
BUILD_DBCS_MESSAGE_TO_CLIENTA_FROM_CLIENTW(
|
|
pmsg->hwnd,pmsg->message,pmsg->wParam,pmsg->lParam,
|
|
pmsg->time,pmsg->pt,bDoDbcsMessaging);
|
|
}
|
|
}
|
|
|
|
DispatchMessageAgain:
|
|
lRet = UserCallWinProcCheckWow(pwnd->pActCtx, (WNDPROC)pwnd->lpfnWndProc, pmsg->hwnd, pmsg->message,
|
|
pmsg->wParam, pmsg->lParam, &(pwnd->state), TRUE);
|
|
|
|
/*
|
|
* if we have DBCS TrailingByte that should be sent, send it here..
|
|
*/
|
|
DISPATCH_DBCS_MESSAGE_IF_EXIST(pmsg->message,pmsg->wParam,bDoDbcsMessaging,DispatchMessage);
|
|
|
|
pmsg->wParam = wParamSaved;
|
|
return lRet;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetMessageTime (API)
|
|
*
|
|
* This API returns the time when the last message was read from
|
|
* the current message queue.
|
|
*
|
|
* History:
|
|
* 11-19-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
LONG GetMessageTime(VOID)
|
|
{
|
|
return (LONG)NtUserGetThreadState(UserThreadStateMessageTime);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetMessageExtraInfo (API)
|
|
*
|
|
* History:
|
|
* 28-May-1991 mikeke
|
|
\***************************************************************************/
|
|
|
|
LPARAM GetMessageExtraInfo(VOID)
|
|
{
|
|
return (LPARAM)NtUserGetThreadState(UserThreadStateExtraInfo);
|
|
}
|
|
|
|
|
|
FUNCLOG1(LOG_GENERAL, LPARAM, DUMMYCALLINGTYPE, SetMessageExtraInfo, LPARAM, lParam)
|
|
LPARAM SetMessageExtraInfo(LPARAM lParam)
|
|
{
|
|
return (LPARAM)NtUserCallOneParam(lParam, SFI__SETMESSAGEEXTRAINFO);
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************\
|
|
* InSendMessage (API)
|
|
*
|
|
* This function determines if the current thread is preocessing a message
|
|
* from another application.
|
|
*
|
|
* History:
|
|
* 01-13-91 DavidPe Ported.
|
|
\***********************************************************************/
|
|
|
|
BOOL InSendMessage(VOID)
|
|
{
|
|
PCLIENTTHREADINFO pcti = GetClientInfo()->pClientThreadInfo;
|
|
|
|
if (pcti) {
|
|
return TEST_BOOL_FLAG(pcti->CTIF_flags, CTIF_INSENDMESSAGE);
|
|
}
|
|
return NtUserGetThreadState(UserThreadStateInSendMessage) != ISMEX_NOSEND;
|
|
}
|
|
/***********************************************************************\
|
|
* InSendMessageEx (API)
|
|
*
|
|
* This function tells you what type of send message is being processed
|
|
* by the application, if any
|
|
*
|
|
* History:
|
|
* 01/22/97 GerardoB Created
|
|
\***********************************************************************/
|
|
|
|
|
|
FUNCLOG1(LOG_GENERAL, DWORD, DUMMYCALLINGTYPE, InSendMessageEx, LPVOID, lpReserved)
|
|
DWORD InSendMessageEx(LPVOID lpReserved)
|
|
{
|
|
PCLIENTTHREADINFO pcti = GetClientInfo()->pClientThreadInfo;
|
|
UNREFERENCED_PARAMETER(lpReserved);
|
|
|
|
if (pcti && !TEST_FLAG(pcti->CTIF_flags, CTIF_INSENDMESSAGE)) {
|
|
return ISMEX_NOSEND;
|
|
}
|
|
return (DWORD)NtUserGetThreadState(UserThreadStateInSendMessage);
|
|
}
|
|
|
|
/***********************************************************************\
|
|
* GetCPD
|
|
*
|
|
* This function calls the server to allocate a CPD structure.
|
|
*
|
|
* History:
|
|
* 11-15-94 JimA Created.
|
|
\***********************************************************************/
|
|
|
|
ULONG_PTR GetCPD(
|
|
KERNEL_PVOID pWndOrCls,
|
|
DWORD options,
|
|
ULONG_PTR dwData)
|
|
{
|
|
return NtUserGetCPD(HW(pWndOrCls), options, dwData);
|
|
}
|
|
|
|
#ifdef BUILD_WOW6432
|
|
/***********************************************************************\
|
|
* MapKernelClientFnToClientFn
|
|
*
|
|
* Maps a function pointer from what the kernel expects to what the
|
|
* client(user-mode) expects.
|
|
*
|
|
* History:
|
|
* 11-15-98 PeterHal Created.
|
|
\***********************************************************************/
|
|
WNDPROC_PWND
|
|
MapKernelClientFnToClientFn(
|
|
WNDPROC_PWND lpfnWndProc
|
|
)
|
|
{
|
|
KPKERNEL_ULONG_PTR pp;
|
|
|
|
for (pp = (KPKERNEL_ULONG_PTR)&gpsi->apfnClientA; pp < (KPKERNEL_ULONG_PTR) (&gpsi->apfnClientA+1); pp ++) {
|
|
if ((KERNEL_ULONG_PTR)lpfnWndProc == *pp) {
|
|
return (WNDPROC_PWND)((KERNEL_ULONG_PTR*) &pfnClientA) [ (pp - (KPKERNEL_ULONG_PTR)&gpsi->apfnClientA) ];
|
|
}
|
|
}
|
|
|
|
for (pp = (KPKERNEL_ULONG_PTR)&gpsi->apfnClientW; pp < (KPKERNEL_ULONG_PTR) (&gpsi->apfnClientW+1); pp ++) {
|
|
if ((KERNEL_ULONG_PTR)lpfnWndProc == *pp) {
|
|
return (WNDPROC_PWND)((KERNEL_ULONG_PTR*) &pfnClientW) [ (pp - (KPKERNEL_ULONG_PTR)&gpsi->apfnClientW) ];
|
|
}
|
|
}
|
|
|
|
return lpfnWndProc;
|
|
}
|
|
#endif
|
|
|
|
#ifdef GENERIC_INPUT
|
|
LRESULT
|
|
APIENTRY
|
|
DefRawInputProc(
|
|
PRAWINPUT* paRawInput,
|
|
INT nInput,
|
|
UINT cbSizeHeader)
|
|
{
|
|
UNREFERENCED_PARAMETER(paRawInput);
|
|
UNREFERENCED_PARAMETER(nInput);
|
|
|
|
if (cbSizeHeader != sizeof(RAWINPUTHEADER)) {
|
|
return (LRESULT)-1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|