1081 lines
26 KiB
C
1081 lines
26 KiB
C
|
/****************************** Module Header ******************************\
|
||
|
* Module Name: wow.c
|
||
|
|
||
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
||
|
|
||
|
* This module contains shared code between USER32 and USER16
|
||
|
* No New CODE should be added to this file, unless its shared
|
||
|
* with USER16.
|
||
|
|
||
|
* History:
|
||
|
* 29-DEC-93 NanduriR shared user32/user16 code.
|
||
|
*/
|
||
|
|
||
|
#include "wow.h"
|
||
|
|
||
|
|
||
|
#ifdef _USERK_
|
||
|
#define CHECK_RESTRICTED() \
|
||
|
if (((PTHREADINFO)W32GetCurrentThread())->TIF_flags & TIF_RESTRICTED) { \
|
||
|
if (!ValidateHandleSecure(h)) \
|
||
|
pobj = NULL; \
|
||
|
} \
|
||
|
|
||
|
#else
|
||
|
#define CHECK_RESTRICTED() \
|
||
|
if (pci && (pci->dwTIFlags & TIF_RESTRICTED) && pobj) { \
|
||
|
if (!NtUserValidateHandleSecure(h)) \
|
||
|
pobj = NULL; \
|
||
|
} \
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef _USERK_
|
||
|
#define GET_CURRENT_CLIENTINFO() \
|
||
|
{ \
|
||
|
PW32THREAD pW32Thread; \
|
||
|
\
|
||
|
pW32Thread = W32GetCurrentThread(); \
|
||
|
\
|
||
|
if (pW32Thread) { \
|
||
|
pci = ((PTHREADINFO)pW32Thread)->pClientInfo; \
|
||
|
} else { \
|
||
|
pci = NULL; \
|
||
|
} \
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
#define GET_CURRENT_CLIENTINFO() \
|
||
|
pci = GetClientInfo();
|
||
|
|
||
|
#endif // _USERK_
|
||
|
|
||
|
|
||
|
/*
|
||
|
* We have two types of desktop validation:
|
||
|
|
||
|
*/
|
||
|
|
||
|
#ifdef _USERK_
|
||
|
|
||
|
#define DESKTOPVALIDATE(pci, pobj) \
|
||
|
UNREFERENCED_PARAMETER(pci);
|
||
|
|
||
|
#define DESKTOPVALIDATECCX(pci, pobj) \
|
||
|
if ( ((PVOID)pobj >= pci->pDeskInfo->pvDesktopBase) && \
|
||
|
((PVOID)pobj < pci->pDeskInfo->pvDesktopLimit)) { \
|
||
|
pobj = (PBYTE)pobj - pci->ulClientDelta; \
|
||
|
} \
|
||
|
|
||
|
#define SHAREDVALIDATE(pobj)
|
||
|
|
||
|
#else
|
||
|
|
||
|
#define DESKTOPVALIDATE(pci, pobj) \
|
||
|
if ( pci->pDeskInfo && \
|
||
|
pobj >= pci->pDeskInfo->pvDesktopBase && \
|
||
|
pobj < pci->pDeskInfo->pvDesktopLimit) { \
|
||
|
pobj = (KERNEL_PVOID)((KERNEL_ULONG_PTR)pobj - pci->ulClientDelta); \
|
||
|
} else { \
|
||
|
pobj = (KERNEL_PVOID)NtUserCallOneParam((ULONG_PTR)h, \
|
||
|
SFI__MAPDESKTOPOBJECT); \
|
||
|
} \
|
||
|
|
||
|
#define SHAREDVALIDATE(pobj) \
|
||
|
pobj = REBASESHAREDPTRALWAYS(pobj);
|
||
|
|
||
|
#endif // _USERK_
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Keep the general path through validation straight without jumps - that
|
||
|
* means tunneling if()'s for this routine - this'll make validation fastest
|
||
|
* because of instruction caching.
|
||
|
|
||
|
* In order to have the validation code in one place only, we define
|
||
|
* the *ValidateHandleMacro macros which are to be included by the
|
||
|
* HMValidateHanlde* routines. We don't make these into functions
|
||
|
* because we're optimizing on time, not size.
|
||
|
*/
|
||
|
#define ValidateHandleMacro(pci, pobj, h, bType) \
|
||
|
StartValidateHandleMacro(h) \
|
||
|
BeginAliveValidateHandleMacro() \
|
||
|
BeginTypeValidateHandleMacro(pobj, bType) \
|
||
|
DESKTOPVALIDATE(pci, pobj) \
|
||
|
EndTypeValidateHandleMacro \
|
||
|
EndAliveValidateHandleMacro() \
|
||
|
EndValidateHandleMacro
|
||
|
|
||
|
#ifdef _USERK_
|
||
|
#define ValidateCatHandleMacro(pci, pobj, h, bType) \
|
||
|
StartValidateHandleMacro(h) \
|
||
|
BeginTypeValidateHandleMacro(pobj, bType) \
|
||
|
DESKTOPVALIDATE(pci, pobj) \
|
||
|
EndTypeValidateHandleMacro \
|
||
|
EndValidateHandleMacro
|
||
|
#define ValidateCatHandleMacroCcx(pci, pobj, h, bType) \
|
||
|
StartValidateHandleMacro(h) \
|
||
|
BeginTypeValidateHandleMacro(pobj, bType) \
|
||
|
DESKTOPVALIDATECCX(pci, pobj) \
|
||
|
EndTypeValidateHandleMacro \
|
||
|
EndValidateHandleMacro
|
||
|
#endif
|
||
|
|
||
|
#define ValidateSharedHandleMacro(pobj, h, bType) \
|
||
|
StartValidateHandleMacro(h) \
|
||
|
BeginAliveValidateHandleMacro() \
|
||
|
BeginTypeValidateHandleMacro(pobj, bType) \
|
||
|
SHAREDVALIDATE(pobj) \
|
||
|
EndTypeValidateHandleMacro \
|
||
|
EndAliveValidateHandleMacro() \
|
||
|
EndValidateHandleMacro
|
||
|
|
||
|
|
||
|
/*
|
||
|
* The handle validation routines should be optimized for time, not size,
|
||
|
* since they get called so often.
|
||
|
*/
|
||
|
#pragma optimize("t", on)
|
||
|
|
||
|
/*
|
||
|
* HMValidateHandle
|
||
|
|
||
|
* This routine validates a handle manager handle.
|
||
|
|
||
|
* 01-22-92 ScottLu Created.
|
||
|
*/
|
||
|
|
||
|
PVOID FASTCALL HMValidateHandle(
|
||
|
HANDLE h,
|
||
|
BYTE bType)
|
||
|
{
|
||
|
DWORD dwError;
|
||
|
KERNEL_PVOID pobj = NULL;
|
||
|
PCLIENTINFO pci;
|
||
|
|
||
|
GET_CURRENT_CLIENTINFO();
|
||
|
|
||
|
#if DBG != 0 && !defined(_USERK_)
|
||
|
/*
|
||
|
* We don't want 32 bit apps passing 16 bit handles
|
||
|
* we should consider failing this before we get
|
||
|
* stuck supporting it (Some VB apps do this).
|
||
|
*/
|
||
|
if (pci && (h != NULL)
|
||
|
&& (HMUniqFromHandle(h) == 0)
|
||
|
&& !(pci->dwTIFlags & TIF_16BIT)) {
|
||
|
RIPMSG3(RIP_WARNING, "HMValidateHandle: 32bit process [%d] using 16 bit handle [%#p] bType:%#lx",
|
||
|
HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess), h, (DWORD)bType);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Object can't be located in shared memory.
|
||
|
*/
|
||
|
UserAssert(bType != TYPE_MONITOR);
|
||
|
|
||
|
/*
|
||
|
* Validation macro. Falls through if the handle is invalid.
|
||
|
*/
|
||
|
ValidateHandleMacro(pci, pobj, h, bType);
|
||
|
|
||
|
/*
|
||
|
* check for secure process
|
||
|
*/
|
||
|
CHECK_RESTRICTED();
|
||
|
|
||
|
if (pobj != NULL) {
|
||
|
return pobj;
|
||
|
}
|
||
|
|
||
|
switch (bType) {
|
||
|
|
||
|
case TYPE_WINDOW:
|
||
|
dwError = ERROR_INVALID_WINDOW_HANDLE;
|
||
|
break;
|
||
|
|
||
|
case TYPE_MENU:
|
||
|
dwError = ERROR_INVALID_MENU_HANDLE;
|
||
|
break;
|
||
|
|
||
|
case TYPE_CURSOR:
|
||
|
dwError = ERROR_INVALID_CURSOR_HANDLE;
|
||
|
break;
|
||
|
|
||
|
case TYPE_ACCELTABLE:
|
||
|
dwError = ERROR_INVALID_ACCEL_HANDLE;
|
||
|
break;
|
||
|
|
||
|
case TYPE_HOOK:
|
||
|
dwError = ERROR_INVALID_HOOK_HANDLE;
|
||
|
break;
|
||
|
|
||
|
case TYPE_SETWINDOWPOS:
|
||
|
dwError = ERROR_INVALID_DWP_HANDLE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
dwError = ERROR_INVALID_HANDLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
RIPERR2(dwError,
|
||
|
RIP_WARNING,
|
||
|
"HMValidateHandle: Invalid:%#p Type:%#lx",
|
||
|
h, (DWORD)bType);
|
||
|
|
||
|
/*
|
||
|
* If we get here, it's an error.
|
||
|
*/
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* HMValidateHandleNoSecure
|
||
|
|
||
|
* This routine validates a handle manager handle.
|
||
|
|
||
|
* 01-22-92 ScottLu Created.
|
||
|
*/
|
||
|
PVOID FASTCALL HMValidateHandleNoSecure(
|
||
|
HANDLE h,
|
||
|
BYTE bType)
|
||
|
{
|
||
|
KERNEL_PVOID pobj = NULL;
|
||
|
PCLIENTINFO pci;
|
||
|
|
||
|
GET_CURRENT_CLIENTINFO();
|
||
|
|
||
|
#if !defined(_USERK_)
|
||
|
/*
|
||
|
* We don't want 32 bit apps passing 16 bit handles
|
||
|
* we should consider failing this before we get
|
||
|
* stuck supporting it (Some VB apps do this).
|
||
|
*/
|
||
|
if (pci && (h != NULL)
|
||
|
&& (HMUniqFromHandle(h) == 0)
|
||
|
&& !(pci->dwTIFlags & TIF_16BIT)) {
|
||
|
RIPMSG3(RIP_WARNING, "HMValidateHandle: 32bit process [%d] using 16 bit handle [%#p] bType:%#lx",
|
||
|
HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess), h, (DWORD)bType);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Object can't be located in shared memory.
|
||
|
*/
|
||
|
UserAssert(bType != TYPE_MONITOR);
|
||
|
|
||
|
/*
|
||
|
* Validation macro.
|
||
|
*/
|
||
|
ValidateHandleMacro(pci, pobj, h, bType);
|
||
|
|
||
|
return pobj;
|
||
|
}
|
||
|
|
||
|
#if defined(_USERK_)
|
||
|
PVOID FASTCALL HMValidateCatHandleNoSecure(
|
||
|
HANDLE h,
|
||
|
BYTE bType)
|
||
|
{
|
||
|
PVOID pobj = NULL;
|
||
|
PCLIENTINFO pci;
|
||
|
|
||
|
GET_CURRENT_CLIENTINFO();
|
||
|
|
||
|
/*
|
||
|
* Object can't be located in shared memory.
|
||
|
*/
|
||
|
UserAssert(bType != TYPE_MONITOR);
|
||
|
|
||
|
/*
|
||
|
* Validation macro.
|
||
|
*/
|
||
|
ValidateCatHandleMacro(pci, pobj, h, bType);
|
||
|
|
||
|
return pobj;
|
||
|
}
|
||
|
PVOID FASTCALL HMValidateCatHandleNoSecureCCX(
|
||
|
HANDLE h,
|
||
|
BYTE bType,
|
||
|
PCLIENTINFO ccxPci)
|
||
|
{
|
||
|
PVOID pobj = NULL;
|
||
|
|
||
|
/*
|
||
|
* Object can't be located in shared memory.
|
||
|
*/
|
||
|
UserAssert(bType != TYPE_MONITOR);
|
||
|
|
||
|
/*
|
||
|
* Validation macro.
|
||
|
*/
|
||
|
ValidateCatHandleMacroCcx(ccxPci, pobj, h, bType);
|
||
|
|
||
|
return pobj;
|
||
|
}
|
||
|
|
||
|
PVOID FASTCALL HMValidateCatHandleNoRip(
|
||
|
HANDLE h,
|
||
|
BYTE bType)
|
||
|
{
|
||
|
PVOID pobj = NULL;
|
||
|
PCLIENTINFO pci;
|
||
|
|
||
|
/*
|
||
|
* This is the fastest way way to do validation, because
|
||
|
* unlike HMValidateHandle, this function doesn't set the
|
||
|
* last error.
|
||
|
|
||
|
* Validation macro. Falls through if the handle is invalid.
|
||
|
*/
|
||
|
|
||
|
GET_CURRENT_CLIENTINFO();
|
||
|
|
||
|
/*
|
||
|
* Object can't be located in shared memory.
|
||
|
*/
|
||
|
UserAssert(bType != TYPE_MONITOR);
|
||
|
|
||
|
ValidateCatHandleMacro(pci, pobj, h, bType);
|
||
|
|
||
|
/*
|
||
|
* check for secure process
|
||
|
*/
|
||
|
CHECK_RESTRICTED();
|
||
|
|
||
|
return pobj;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
PVOID FASTCALL HMValidateHandleNoRip(
|
||
|
HANDLE h,
|
||
|
BYTE bType)
|
||
|
{
|
||
|
KERNEL_PVOID pobj = NULL;
|
||
|
PCLIENTINFO pci;
|
||
|
|
||
|
/*
|
||
|
* This is the fastest way way to do validation, because
|
||
|
* unlike HMValidateHandle, this function doesn't set the
|
||
|
* last error.
|
||
|
|
||
|
* Validation macro. Falls through if the handle is invalid.
|
||
|
*/
|
||
|
|
||
|
GET_CURRENT_CLIENTINFO();
|
||
|
|
||
|
/*
|
||
|
* Object can't be located in shared memory.
|
||
|
*/
|
||
|
UserAssert(bType != TYPE_MONITOR);
|
||
|
|
||
|
ValidateHandleMacro(pci, pobj, h, bType);
|
||
|
|
||
|
/*
|
||
|
* check for secure process
|
||
|
*/
|
||
|
CHECK_RESTRICTED();
|
||
|
|
||
|
return pobj;
|
||
|
}
|
||
|
|
||
|
#if DBG != 0 && !defined(_USERK_)
|
||
|
/*
|
||
|
* HMValidateHandleNoDesktop is a debug-client-side only function
|
||
|
* used to verify a given handle without calling DESKTOPVALIDATE.
|
||
|
* If the handle is valid, it returns the object's kernel pointer
|
||
|
* which can be used as a BOOL value only.
|
||
|
* Use this function to verify handles for which corresponding phe->phead
|
||
|
* is a pool allocation (as opposed to desktop-heap allocations).
|
||
|
*/
|
||
|
KERNEL_PVOID FASTCALL HMValidateHandleNoDesktop(
|
||
|
HANDLE h,
|
||
|
BYTE bType)
|
||
|
{
|
||
|
KERNEL_PVOID pobj = NULL;
|
||
|
|
||
|
StartValidateHandleMacro(h)
|
||
|
BeginTypeValidateHandleMacro(pobj, bType)
|
||
|
EndTypeValidateHandleMacro
|
||
|
EndValidateHandleMacro
|
||
|
return pobj;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*
|
||
|
* HMValidateSharedHandle
|
||
|
|
||
|
* This routine validates a handle manager handle allocated in
|
||
|
* shared memory.
|
||
|
|
||
|
* History:
|
||
|
* 02-Apr-1997 adams Created.
|
||
|
*/
|
||
|
|
||
|
PVOID FASTCALL HMValidateSharedHandle(
|
||
|
HANDLE h,
|
||
|
BYTE bType)
|
||
|
{
|
||
|
DWORD dwError;
|
||
|
KERNEL_PVOID pobj = NULL;
|
||
|
|
||
|
#if DBG != 0 && !defined(_USERK_)
|
||
|
|
||
|
/*
|
||
|
* We don't want 32 bit apps passing 16 bit handles
|
||
|
* we should consider failing this before we get
|
||
|
* stuck supporting it (Some VB apps do this).
|
||
|
*/
|
||
|
if ((h != NULL)
|
||
|
&& (HMUniqFromHandle(h) == 0)
|
||
|
&& !(GetClientInfo()->dwTIFlags & TIF_16BIT)) {
|
||
|
RIPMSG3(RIP_WARNING, "HMValidateHandle: 32bit process [%d] using 16 bit handle [%#p] bType:%#lx",
|
||
|
HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess), h, (DWORD)bType);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Validation macro. Falls through if the handle is invalid.
|
||
|
*/
|
||
|
ValidateSharedHandleMacro(pobj, h, bType);
|
||
|
|
||
|
if (pobj != NULL)
|
||
|
return pobj;
|
||
|
|
||
|
switch (bType) {
|
||
|
case TYPE_MONITOR:
|
||
|
dwError = ERROR_INVALID_MONITOR_HANDLE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
UserAssertMsg0(0, "Logic error in HMValidateSharedHandle");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
RIPERR2(dwError,
|
||
|
RIP_WARNING,
|
||
|
"HMValidateSharedHandle: Invalid:%#p Type:%#lx",
|
||
|
h, (DWORD)bType);
|
||
|
|
||
|
/*
|
||
|
* If we get here, it's an error.
|
||
|
*/
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Switch back to default optimization.
|
||
|
*/
|
||
|
#pragma optimize("", on)
|
||
|
|
||
|
/*
|
||
|
* MNLookUpItem
|
||
|
|
||
|
* Return a pointer to the menu item specified by wCmd and wFlags
|
||
|
|
||
|
* History:
|
||
|
* 10-11-90 JimA Translated from ASM
|
||
|
* 01-07-93 FritzS Ported from Chicago
|
||
|
*/
|
||
|
|
||
|
PITEM MNLookUpItem(
|
||
|
PMENU pMenu,
|
||
|
UINT wCmd,
|
||
|
BOOL fByPosition,
|
||
|
PMENU *ppMenuItemIsOn)
|
||
|
{
|
||
|
PITEM pItem;
|
||
|
PITEM pItemRet = NULL;
|
||
|
PITEM pItemMaybe;
|
||
|
PMENU pMenuMaybe = NULL;
|
||
|
int i;
|
||
|
|
||
|
if (ppMenuItemIsOn != NULL)
|
||
|
*ppMenuItemIsOn = NULL;
|
||
|
|
||
|
if (pMenu == NULL || !pMenu->cItems || wCmd == MFMWFP_NOITEM) {
|
||
|
// RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "MNLookUpItem: invalid item");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* dwFlags determines how we do the search
|
||
|
*/
|
||
|
if (fByPosition) {
|
||
|
if (wCmd < (UINT)pMenu->cItems) {
|
||
|
pItemRet = &((PITEM)REBASEALWAYS(pMenu, rgItems))[wCmd];
|
||
|
if (ppMenuItemIsOn != NULL)
|
||
|
*ppMenuItemIsOn = pMenu;
|
||
|
return (pItemRet);
|
||
|
} else
|
||
|
return NULL;
|
||
|
}
|
||
|
/*
|
||
|
* Walk down the menu and try to find an item with an ID of wCmd.
|
||
|
* The search procedes from the end of the menu (as was done in
|
||
|
* assembler).
|
||
|
*/
|
||
|
|
||
|
/* this is the Chicago code, which walks from the front of the menu -- Fritz */
|
||
|
|
||
|
|
||
|
// for (pItem = &pMenu->rgItems[i - 1]; pItemRet == NULL && i--; --pItem) {
|
||
|
for (i = 0, pItem = REBASEALWAYS(pMenu, rgItems); i < (int)pMenu->cItems;
|
||
|
i++, pItem++) {
|
||
|
|
||
|
/*
|
||
|
* If the item is a popup, recurse down the tree
|
||
|
*/
|
||
|
if (pItem->spSubMenu != NULL) {
|
||
|
|
||
|
// COMPAT:
|
||
|
// Allow apps to pass in menu handle as ID in menu APIs. We
|
||
|
// remember that this popup had a menu handle with the same ID
|
||
|
// value. This is a 2nd choice though. We still want to see
|
||
|
// if there's some actual command that has this ID value first.
|
||
|
|
||
|
if (pItem->wID == wCmd) {
|
||
|
pMenuMaybe = pMenu;
|
||
|
pItemMaybe = pItem;
|
||
|
}
|
||
|
|
||
|
pItemRet = MNLookUpItem((PMENU)REBASEPTR(pMenu, pItem->spSubMenu),
|
||
|
wCmd, FALSE, ppMenuItemIsOn);
|
||
|
if (pItemRet != NULL)
|
||
|
return pItemRet;
|
||
|
} else if (pItem->wID == wCmd) {
|
||
|
|
||
|
/*
|
||
|
* Found the item, now save things for later
|
||
|
*/
|
||
|
if (ppMenuItemIsOn != NULL)
|
||
|
*ppMenuItemIsOn = pMenu;
|
||
|
return pItem;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pMenuMaybe) {
|
||
|
// no non popup menu match found -- use the 2nd choice popup menu
|
||
|
// match
|
||
|
if (ppMenuItemIsOn != NULL)
|
||
|
*ppMenuItemIsOn = pMenuMaybe;
|
||
|
return(pItemMaybe);
|
||
|
}
|
||
|
|
||
|
return(NULL);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* GetMenuState
|
||
|
|
||
|
* Either returns the state of a menu item or the state and item count
|
||
|
* of a popup.
|
||
|
|
||
|
* History:
|
||
|
* 10-11-90 JimA Translated from ASM
|
||
|
*/
|
||
|
|
||
|
UINT _GetMenuState(
|
||
|
PMENU pMenu,
|
||
|
UINT wId,
|
||
|
UINT dwFlags)
|
||
|
{
|
||
|
PITEM pItem;
|
||
|
DWORD fFlags;
|
||
|
|
||
|
/*
|
||
|
* If the item does not exist, leave
|
||
|
*/
|
||
|
if ((pItem = MNLookUpItem(pMenu, wId, (BOOL) (dwFlags & MF_BYPOSITION), NULL)) == NULL)
|
||
|
return (UINT)-1;
|
||
|
|
||
|
fFlags = pItem->fState | pItem->fType;
|
||
|
|
||
|
#ifndef _USERK_
|
||
|
/*
|
||
|
* Add old MFT_BITMAP flag to keep old apps happy
|
||
|
*/
|
||
|
if ((pItem->hbmp != NULL) && (pItem->lpstr == NULL)) {
|
||
|
fFlags |= MFT_BITMAP;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (pItem->spSubMenu != NULL) {
|
||
|
/*
|
||
|
* If the item is a popup, return item count in high byte and
|
||
|
* popup flags in low byte
|
||
|
*/
|
||
|
|
||
|
fFlags = ((fFlags | MF_POPUP) & 0x00FF) +
|
||
|
(((PMENU)REBASEPTR(pMenu, pItem->spSubMenu))->cItems << 8);
|
||
|
}
|
||
|
|
||
|
return fFlags;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* GetPrevPwnd
|
||
|
|
||
|
|
||
|
|
||
|
* History:
|
||
|
* 11-05-90 darrinm Ported from Win 3.0 sources.
|
||
|
*/
|
||
|
|
||
|
PWND GetPrevPwnd(
|
||
|
PWND pwndList,
|
||
|
PWND pwndFind)
|
||
|
{
|
||
|
PWND pwndFound, pwndNext;
|
||
|
|
||
|
if (pwndList == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
if (pwndList->spwndParent == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
pwndNext = REBASEPWND(pwndList, spwndParent);
|
||
|
pwndNext = REBASEPWND(pwndNext, spwndChild);
|
||
|
pwndFound = NULL;
|
||
|
|
||
|
while (pwndNext != NULL) {
|
||
|
if (pwndNext == pwndFind)
|
||
|
break;
|
||
|
pwndFound = pwndNext;
|
||
|
pwndNext = REBASEPWND(pwndNext, spwndNext);
|
||
|
}
|
||
|
|
||
|
return (pwndNext == pwndFind) ? pwndFound : NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* _GetWindow (API)
|
||
|
|
||
|
|
||
|
* History:
|
||
|
* 11-05-90 darrinm Ported from Win 3.0 sources.
|
||
|
* 02-19-91 JimA Added enum access check
|
||
|
* 05-04-02 DarrinM Removed enum access check and moved to USERRTL.DLL
|
||
|
*/
|
||
|
|
||
|
PWND _GetWindow(
|
||
|
PWND pwnd,
|
||
|
UINT cmd)
|
||
|
{
|
||
|
PWND pwndT;
|
||
|
BOOL fRebase = FALSE;
|
||
|
|
||
|
/*
|
||
|
* If this is a desktop window, return NULL for sibling or
|
||
|
* parent information.
|
||
|
*/
|
||
|
if (GETFNID(pwnd) == FNID_DESKTOP) {
|
||
|
switch (cmd) {
|
||
|
case GW_CHILD:
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return NULL;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Rebase the returned window at the end of the routine
|
||
|
* to avoid multiple test for pwndT == NULL.
|
||
|
*/
|
||
|
pwndT = NULL;
|
||
|
switch (cmd) {
|
||
|
case GW_HWNDNEXT:
|
||
|
pwndT = pwnd->spwndNext;
|
||
|
fRebase = TRUE;
|
||
|
break;
|
||
|
|
||
|
case GW_HWNDFIRST:
|
||
|
if (pwnd->spwndParent) {
|
||
|
pwndT = REBASEPWND(pwnd, spwndParent);
|
||
|
pwndT = REBASEPWND(pwndT, spwndChild);
|
||
|
if (GetAppCompatFlags(NULL) & GACF_IGNORETOPMOST) {
|
||
|
while (pwndT != NULL) {
|
||
|
if (!TestWF(pwndT, WEFTOPMOST))
|
||
|
break;
|
||
|
pwndT = REBASEPWND(pwndT, spwndNext);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case GW_HWNDLAST:
|
||
|
pwndT = GetPrevPwnd(pwnd, NULL);
|
||
|
break;
|
||
|
|
||
|
case GW_HWNDPREV:
|
||
|
pwndT = GetPrevPwnd(pwnd, pwnd);
|
||
|
break;
|
||
|
|
||
|
case GW_OWNER:
|
||
|
pwndT = pwnd->spwndOwner;
|
||
|
fRebase = TRUE;
|
||
|
break;
|
||
|
|
||
|
case GW_CHILD:
|
||
|
pwndT = pwnd->spwndChild;
|
||
|
fRebase = TRUE;
|
||
|
break;
|
||
|
|
||
|
#if !defined(_USERK_)
|
||
|
case GW_ENABLEDPOPUP:
|
||
|
pwndT = (PWND)NtUserCallHwnd(PtoHq(pwnd), SFI_DWP_GETENABLEDPOPUP);
|
||
|
fRebase = TRUE;
|
||
|
break;
|
||
|
#endif
|
||
|
|
||
|
default:
|
||
|
RIPERR0(ERROR_INVALID_GW_COMMAND, RIP_VERBOSE, "");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (pwndT != NULL && fRebase)
|
||
|
pwndT = REBASEPTR(pwnd, pwndT);
|
||
|
|
||
|
return pwndT;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* _GetParent (API)
|
||
|
|
||
|
|
||
|
|
||
|
* History:
|
||
|
* 11-12-90 darrinm Ported.
|
||
|
* 02-19-91 JimA Added enum access check
|
||
|
* 05-04-92 DarrinM Removed enum access check and moved to USERRTL.DLL
|
||
|
*/
|
||
|
|
||
|
PWND _GetParent(
|
||
|
PWND pwnd)
|
||
|
{
|
||
|
/*
|
||
|
* For 1.03 compatibility reasons, we should return NULL
|
||
|
* for top level "tiled" windows and owner for other popups.
|
||
|
* pwndOwner is set to NULL in xxxCreateWindow for top level
|
||
|
* "tiled" windows.
|
||
|
*/
|
||
|
if (!(TestwndTiled(pwnd))) {
|
||
|
if (TestwndChild(pwnd))
|
||
|
pwnd = REBASEPWND(pwnd, spwndParent);
|
||
|
else
|
||
|
pwnd = REBASEPWND(pwnd, spwndOwner);
|
||
|
return pwnd;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The window was not a child window; they may have been just testing
|
||
|
* if it was
|
||
|
*/
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* GetSubMenu
|
||
|
|
||
|
* Return the handle of a popup menu.
|
||
|
|
||
|
* History:
|
||
|
* 10-11-90 JimA Translated from ASM
|
||
|
*/
|
||
|
|
||
|
PMENU _GetSubMenu(
|
||
|
PMENU pMenu,
|
||
|
int nPos)
|
||
|
{
|
||
|
PITEM pItem;
|
||
|
PMENU pPopup = NULL;
|
||
|
|
||
|
/*
|
||
|
* Make sure nPos refers to a valid popup
|
||
|
*/
|
||
|
if ((UINT)nPos < (UINT)((PMENU)pMenu)->cItems) {
|
||
|
pItem = &((PITEM)REBASEALWAYS(pMenu, rgItems))[nPos];
|
||
|
if (pItem->spSubMenu != NULL)
|
||
|
pPopup = (PMENU)REBASEPTR(pMenu, pItem->spSubMenu);
|
||
|
|
||
|
}
|
||
|
|
||
|
return (PVOID)pPopup;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* _IsChild (API)
|
||
|
|
||
|
|
||
|
|
||
|
* History:
|
||
|
* 11-07-90 darrinm Translated from Win 3.0 ASM code.
|
||
|
*/
|
||
|
|
||
|
BOOL _IsChild(
|
||
|
PWND pwndParent,
|
||
|
PWND pwnd)
|
||
|
{
|
||
|
/*
|
||
|
* Don't need a test to get out of the loop because the
|
||
|
* desktop is not a child.
|
||
|
*/
|
||
|
while (pwnd != NULL) {
|
||
|
if (!TestwndChild(pwnd))
|
||
|
return FALSE;
|
||
|
|
||
|
pwnd = REBASEPWND(pwnd, spwndParent);
|
||
|
if (pwndParent == pwnd)
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* _IsWindowVisible (API)
|
||
|
|
||
|
* IsWindowVisible returns the TRUEVIS state of a window, rather than just
|
||
|
* the state of its WFVISIBLE flag. According to this routine, a window is
|
||
|
* considered visible when it and all the windows on its parent chain are
|
||
|
* visible (WFVISIBLE flag set). A special case hack was put in that causes
|
||
|
* any icon window being dragged to be considered as visible.
|
||
|
|
||
|
* History:
|
||
|
* 11-12-90 darrinm Ported.
|
||
|
*/
|
||
|
|
||
|
BOOL _IsWindowVisible(
|
||
|
PWND pwnd)
|
||
|
{
|
||
|
/*
|
||
|
* Check if this is the iconic window being moved around with a mouse
|
||
|
* If so, return a TRUE, though, strictly speaking, it is hidden.
|
||
|
* This helps the Tracer guys from going crazy!
|
||
|
* Fix for Bug #57 -- SANKAR -- 08-08-89 --
|
||
|
*/
|
||
|
if (pwnd == NULL)
|
||
|
return TRUE;
|
||
|
|
||
|
for (;;) {
|
||
|
if (!TestWF(pwnd, WFVISIBLE))
|
||
|
return FALSE;
|
||
|
if (GETFNID(pwnd) == FNID_DESKTOP)
|
||
|
break;
|
||
|
pwnd = REBASEPWND(pwnd, spwndParent);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* _ClientToScreen (API)
|
||
|
|
||
|
* Map a point from client to screen-relative coordinates.
|
||
|
|
||
|
* History:
|
||
|
* 11-12-90 darrinm Translated from Win 3.0 ASM code.
|
||
|
*/
|
||
|
|
||
|
VOID _ClientToScreen(
|
||
|
PWND pwnd,
|
||
|
PPOINT ppt)
|
||
|
{
|
||
|
/*
|
||
|
* Client and screen coordinates are the same for the
|
||
|
* desktop window.
|
||
|
*/
|
||
|
if (GETFNID(pwnd) != FNID_DESKTOP) {
|
||
|
#ifdef USE_MIRRORING
|
||
|
if (TestWF(pwnd, WEFLAYOUTRTL)) {
|
||
|
ppt->x = pwnd->rcClient.right - ppt->x;
|
||
|
} else
|
||
|
#endif
|
||
|
{
|
||
|
ppt->x += pwnd->rcClient.left;
|
||
|
}
|
||
|
ppt->y += pwnd->rcClient.top;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* _GetClientRect (API)
|
||
|
|
||
|
|
||
|
|
||
|
* History:
|
||
|
* 26-Oct-1990 DarrinM Implemented.
|
||
|
*/
|
||
|
|
||
|
VOID _GetClientRect(
|
||
|
PWND pwnd,
|
||
|
LPRECT prc)
|
||
|
{
|
||
|
/*
|
||
|
* If this is a 3.1 app, and it's minimized, then we need to return
|
||
|
* a rectangle other than the real-client-rect. This is necessary since
|
||
|
* there is no client-rect-size in Win4.0. Apps such as PackRat 1.0
|
||
|
* will GPF if returned a empty-rect.
|
||
|
*/
|
||
|
if (TestWF(pwnd, WFMINIMIZED) && !TestWF(pwnd, WFWIN40COMPAT)) {
|
||
|
prc->left = 0;
|
||
|
prc->top = 0;
|
||
|
prc->right = SYSMETRTL(CXMINIMIZED);
|
||
|
prc->bottom = SYSMETRTL(CYMINIMIZED);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (GETFNID(pwnd) != FNID_DESKTOP) {
|
||
|
*prc = pwnd->rcClient;
|
||
|
OffsetRect(prc, -pwnd->rcClient.left, -pwnd->rcClient.top);
|
||
|
} else {
|
||
|
/*
|
||
|
* For compatibility, return the rect of the primary
|
||
|
* monitor for the desktop window.
|
||
|
*/
|
||
|
prc->left = prc->top = 0;
|
||
|
prc->right = SYSMETRTL(CXSCREEN);
|
||
|
prc->bottom = SYSMETRTL(CYSCREEN);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* _GetWindowRect (API)
|
||
|
|
||
|
|
||
|
|
||
|
* History:
|
||
|
* 26-Oct-1990 DarrinM Implemented.
|
||
|
*/
|
||
|
|
||
|
VOID _GetWindowRect(
|
||
|
PWND pwnd,
|
||
|
LPRECT prc)
|
||
|
{
|
||
|
|
||
|
if (GETFNID(pwnd) != FNID_DESKTOP) {
|
||
|
*prc = pwnd->rcWindow;
|
||
|
} else {
|
||
|
/*
|
||
|
* For compatibility, return the rect of the primary
|
||
|
* monitor for the desktop window.
|
||
|
*/
|
||
|
prc->left = 0;
|
||
|
prc->top = 0;
|
||
|
prc->right = SYSMETRTL(CXSCREEN);
|
||
|
prc->bottom = SYSMETRTL(CYSCREEN);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* _ScreenToClient (API)
|
||
|
|
||
|
* Map a point from screen to client-relative coordinates.
|
||
|
|
||
|
* History:
|
||
|
* 11-12-90 darrinm Translated from Win 3.0 ASM code.
|
||
|
*/
|
||
|
|
||
|
VOID _ScreenToClient(
|
||
|
PWND pwnd,
|
||
|
PPOINT ppt)
|
||
|
{
|
||
|
/*
|
||
|
* Client and screen coordinates are the same for the
|
||
|
* desktop window.
|
||
|
*/
|
||
|
if (GETFNID(pwnd) != FNID_DESKTOP) {
|
||
|
#ifdef USE_MIRRORING
|
||
|
if (TestWF(pwnd, WEFLAYOUTRTL)) {
|
||
|
ppt->x = pwnd->rcClient.right - ppt->x;
|
||
|
} else
|
||
|
#endif
|
||
|
{
|
||
|
ppt->x -= pwnd->rcClient.left;
|
||
|
}
|
||
|
ppt->y -= pwnd->rcClient.top;
|
||
|
}
|
||
|
}
|
||
|
/*
|
||
|
* PhkNextValid
|
||
|
|
||
|
* This helper routine walk the phkNext chain looking for the next valid
|
||
|
* hook (i.e., not marked as destroyed). If the end of the local (or
|
||
|
* thread specific) hook chain is reached, then it jumps to the global
|
||
|
* (or desktop) chain.
|
||
|
|
||
|
* Once a hook is destroyed, we don't want anymore activity on it; however,
|
||
|
* if the hook is locked at destroy time (= someone is calling it), then
|
||
|
* we keep it in the list so CallNextHook will work properly
|
||
|
|
||
|
* History:
|
||
|
* 03/24/96 GerardoB Moved to rtl and added *Valid stuff.
|
||
|
* 01-30-91 DavidPe Created.
|
||
|
*/
|
||
|
PHOOK PhkNextValid(PHOOK phk)
|
||
|
{
|
||
|
#if DBG
|
||
|
int iHook = phk->iHook;
|
||
|
#ifdef _USERK_
|
||
|
CheckCritInShared();
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
do {
|
||
|
/*
|
||
|
* If this hook is marked as destroyed, it must be either
|
||
|
* locked or we should be in the process of destroying it
|
||
|
*/
|
||
|
UserAssert(!(phk->flags & HF_DESTROYED)
|
||
|
|| (((PHEAD)phk)->cLockObj != 0)
|
||
|
|| (phk->flags & HF_INCHECKWHF));
|
||
|
/*
|
||
|
* Get the next hook
|
||
|
*/
|
||
|
if (phk->phkNext != NULL) {
|
||
|
phk = REBASEALWAYS(phk, phkNext);
|
||
|
} else if (!(phk->flags & HF_GLOBAL)) {
|
||
|
#ifdef _USERK_
|
||
|
phk = PtiCurrent()->pDeskInfo->aphkStart[phk->iHook + 1];
|
||
|
#else
|
||
|
PCLIENTINFO pci = GetClientInfo();
|
||
|
phk = pci->pDeskInfo->aphkStart[phk->iHook + 1];
|
||
|
/*
|
||
|
* If it found a pointer, rebase it.
|
||
|
*/
|
||
|
if (phk != NULL) {
|
||
|
(KPBYTE)phk -= pci->ulClientDelta;
|
||
|
}
|
||
|
#endif
|
||
|
UserAssert((phk == NULL) || (phk->flags & HF_GLOBAL));
|
||
|
} else {
|
||
|
return NULL;
|
||
|
}
|
||
|
/*
|
||
|
* If destroyed, keep looking.
|
||
|
*/
|
||
|
} while ((phk != NULL) && (phk->flags & HF_DESTROYED));
|
||
|
|
||
|
#ifdef _USERK_
|
||
|
DbgValidateHooks(phk, iHook);
|
||
|
#endif
|
||
|
|
||
|
return phk;
|
||
|
}
|