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.
1695 lines
55 KiB
1695 lines
55 KiB
/*++
|
|
*
|
|
* WOW v1.0
|
|
*
|
|
* Copyright (c) 1991, Microsoft Corporation
|
|
*
|
|
* WALIAS.C
|
|
* WOW32 16-bit handle alias support
|
|
*
|
|
* History:
|
|
* Created 27-Jan-1991 by Jeff Parsons (jeffpar)
|
|
* Modified 12-May-1992 by Mike Tricker (miketri) to add MultiMedia support
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
MODNAME(walias.c);
|
|
|
|
extern HANDLE hmodWOW32;
|
|
|
|
extern CRITICAL_SECTION gcsWOW;
|
|
extern PTD gptdTaskHead;
|
|
|
|
//BUGBUG - this must be removed once MM_MCISYSTEM_STRING is defined in MMSYSTEM.H.
|
|
#ifndef MM_MCISYSTEM_STRING
|
|
#define MM_MCISYSTEM_STRING 0x3CA
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
extern BOOL fSkipLog; // TRUE to temporarily skip certain logging
|
|
#endif
|
|
|
|
typedef struct _stdclass {
|
|
LPSTR lpszClassName;
|
|
ATOM aClassAtom;
|
|
WNDPROC lpfnWndProc;
|
|
INT iOrdinal;
|
|
DWORD vpfnWndProc;
|
|
} STDCLASS;
|
|
|
|
// Some cool defines stolen from USERSRV.H
|
|
#define MENUCLASS MAKEINTATOM(0x8000)
|
|
#define DESKTOPCLASS MAKEINTATOM(0x8001)
|
|
#define DIALOGCLASS MAKEINTATOM(0x8002)
|
|
#define SWITCHWNDCLASS MAKEINTATOM(0x8003)
|
|
#define ICONTITLECLASS MAKEINTATOM(0x8004)
|
|
|
|
// See WARNING below!
|
|
STDCLASS stdClasses[] = {
|
|
NULL, 0, NULL, 0, 0, // WOWCLASS_UNKNOWN
|
|
NULL, 0, NULL, 0, 0, // WOWCLASS_WIN16
|
|
"BUTTON", 0, NULL, FUN_BUTTONWNDPROC, 0, // WOWCLASS_BUTTON,
|
|
"COMBOBOX", 0, NULL, FUN_COMBOBOXCTLWNDPROC, 0, // WOWCLASS_COMBOBOX,
|
|
"EDIT", 0, NULL, FUN_EDITWNDPROC, 0, // WOWCLASS_EDIT,
|
|
"LISTBOX", 0, NULL, FUN_LBOXCTLWNDPROC, 0, // WOWCLASS_LISTBOX,
|
|
"MDICLIENT", 0, NULL, FUN_MDICLIENTWNDPROC, 0, // WOWCLASS_MDICLIENT,
|
|
"SCROLLBAR", 0, NULL, FUN_SBWNDPROC, 0, // WOWCLASS_SCROLLBAR,
|
|
"STATIC", 0, NULL, FUN_STATICWNDPROC, 0, // WOWCLASS_STATIC,
|
|
"#32769", (WORD)DESKTOPCLASS, NULL, FUN_DESKTOPWNDPROC, 0, // WOWCLASS_DESKTOP,
|
|
"#32770", (WORD)DIALOGCLASS, NULL, FUN_DEFDLGPROCTHUNK, 0, // WOWCLASS_DIALOG,
|
|
"#32772", (WORD)ICONTITLECLASS, NULL, FUN_TITLEWNDPROC, 0, // WOWCLASS_ICONTITLE,
|
|
"#32768", (WORD)MENUCLASS, NULL, FUN_MENUWNDPROC, 0, // WOWCLASS_MENU,
|
|
"#32771", (WORD)SWITCHWNDCLASS, NULL, 0, 0, // WOWCLASS_SWITCHWND,
|
|
"COMBOLBOX", 0, NULL, FUN_LBOXCTLWNDPROC, 0, // WOWCLASS_COMBOLBOX
|
|
};
|
|
//
|
|
// WARNING! The above sequence and values must be maintained otherwise the
|
|
// table in WMSG16.C for message thunking must be changed. Same goes for
|
|
// the #define's in WALIAS.H
|
|
//
|
|
// The above COMBOLBOX case is special because it is class that is
|
|
// almost identical to a listbox. Therefore we lie about it.
|
|
|
|
INT GetStdClassNumber(
|
|
PSZ pszClass
|
|
) {
|
|
INT i;
|
|
|
|
if ( HIWORD(pszClass) ) {
|
|
|
|
// They passed us a string
|
|
|
|
for ( i = WOWCLASS_BUTTON; i < NUMEL(stdClasses); i++ ) {
|
|
if ( WOW32_stricmp(pszClass, stdClasses[i].lpszClassName) == 0 ) {
|
|
return( i );
|
|
}
|
|
}
|
|
} else {
|
|
|
|
// They passed us an atom
|
|
|
|
for ( i = WOWCLASS_BUTTON; i < NUMEL(stdClasses); i++ ) {
|
|
if ( stdClasses[i].aClassAtom == 0 ) {
|
|
// RegisterWindowMessage is an undocumented way of determining
|
|
// an atom value in the context of the server-side heap.
|
|
stdClasses[i].aClassAtom = (ATOM)RegisterWindowMessage(stdClasses[i].lpszClassName);
|
|
}
|
|
if ( (ATOM)LOWORD(pszClass) == stdClasses[i].aClassAtom ) {
|
|
return( i );
|
|
}
|
|
}
|
|
}
|
|
return( WOWCLASS_WIN16 ); // private 16-bit class created by the app
|
|
}
|
|
|
|
// Returns a 32 window proc given a class index
|
|
|
|
WNDPROC GetStdClassWndProc(
|
|
DWORD iClass
|
|
) {
|
|
WNDPROC lpfn32;
|
|
|
|
if ( iClass < WOWCLASS_WIN16 || iClass > WOWCLASS_MAX ) {
|
|
WOW32ASSERT(FALSE);
|
|
return( NULL );
|
|
}
|
|
|
|
lpfn32 = stdClasses[iClass].lpfnWndProc;
|
|
|
|
if ( lpfn32 == NULL ) {
|
|
WNDCLASS wc;
|
|
BOOL f;
|
|
|
|
f = GetClassInfo( NULL, stdClasses[iClass].lpszClassName, &wc );
|
|
|
|
if ( f ) {
|
|
VPVOID vp;
|
|
DWORD UNALIGNED * lpdw;
|
|
|
|
lpfn32 = wc.lpfnWndProc;
|
|
stdClasses[iClass].lpfnWndProc = lpfn32;
|
|
|
|
vp = GetStdClassThunkProc(iClass);
|
|
vp = (VPVOID)((DWORD)vp - sizeof(DWORD)*3);
|
|
|
|
GETVDMPTR( vp, sizeof(DWORD)*3, lpdw );
|
|
|
|
WOW32ASSERT(*lpdw == SUBCLASS_MAGIC); // Are we editing the right stuff?
|
|
|
|
if (!lpdw)
|
|
*(lpdw+2) = (DWORD)lpfn32;
|
|
|
|
FLUSHVDMCODEPTR( vp, sizeof(DWORD)*3, lpdw );
|
|
FREEVDMPTR( lpdw );
|
|
|
|
}
|
|
}
|
|
return( lpfn32 );
|
|
}
|
|
|
|
// Returns a 16 window proc thunk given a class index
|
|
|
|
DWORD GetStdClassThunkProc(
|
|
INT iClass
|
|
) {
|
|
DWORD dwResult;
|
|
SHORT iOrdinal;
|
|
PARM16 Parm16;
|
|
|
|
if ( iClass < WOWCLASS_WIN16 || iClass > WOWCLASS_MAX ) {
|
|
WOW32ASSERT(FALSE);
|
|
return( 0 );
|
|
}
|
|
|
|
iOrdinal = (SHORT)stdClasses[iClass].iOrdinal;
|
|
|
|
if ( iOrdinal == 0 ) {
|
|
return( (DWORD)NULL );
|
|
}
|
|
|
|
// If we've already gotten this proc, then don't bother calling into 16-bit
|
|
dwResult = stdClasses[iClass].vpfnWndProc;
|
|
|
|
if ( dwResult == (DWORD)NULL ) {
|
|
|
|
// Callback into the 16-bit world asking for the 16:16 address
|
|
|
|
Parm16.SubClassProc.iOrdinal = iOrdinal;
|
|
|
|
if (!CallBack16(RET_SUBCLASSPROC, &Parm16, (VPPROC)NULL,
|
|
(PVPVOID)&dwResult)) {
|
|
WOW32ASSERT(FALSE);
|
|
return( 0 );
|
|
}
|
|
// Save it since it is a constant.
|
|
stdClasses[iClass].vpfnWndProc = dwResult;
|
|
}
|
|
return( dwResult );
|
|
}
|
|
|
|
/*
|
|
* PWC GetClassWOWWords(hInst, pszClass)
|
|
* is a ***private*** API for WOW only. It returns a pointer to the
|
|
* WOW Class structure in the server's window class structure.
|
|
* This is similar to GetClassLong(hwnd32, GCL_WOWWORDS) (see FindPWC),
|
|
* but in this case we don't have a hwnd32, we have the class name
|
|
* and instance handle.
|
|
*/
|
|
|
|
PWC FindClass16(LPCSTR pszClass, HAND16 hInst)
|
|
{
|
|
register PWC pwc;
|
|
|
|
pwc = (PWC)(pfnOut.pfnGetClassWOWWords)(HMODINST32(hInst), pszClass);
|
|
WOW32WARNMSGF(
|
|
pwc,
|
|
("WOW32 warning: GetClassWOWWords('%s', %04x) returned NULL\n", pszClass, hInst)
|
|
);
|
|
|
|
return (pwc);
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
INT nAliases;
|
|
INT iLargestListSlot;
|
|
|
|
PSZ apszHandleClasses[] = {
|
|
"Unknown", // WOWCLASS_UNKNOWN
|
|
"Window", // WOWCLASS_WIN16
|
|
"Button", // WOWCLASS_BUTTON
|
|
"ComboBox", // WOWCLASS_COMBOBOX
|
|
"Edit", // WOWCLASS_EDIT
|
|
"ListBox", // WOWCLASS_LISTBOX
|
|
"MDIClient", // WOWCLASS_MDICLIENT
|
|
"Scrollbar", // WOWCLASS_SCROLLBAR
|
|
"Static", // WOWCLASS_STATIC
|
|
"Desktop", // WOWCLASS_DESKTOP
|
|
"Dialog", // WOWCLASS_DIALOG
|
|
"Menu", // WOWCLASS_MENU
|
|
"IconTitle", // WOWCLASS_ICONTITLE
|
|
"Accel", // WOWCLASS_ACCEL
|
|
"Cursor", // WOWCLASS_CURSOR
|
|
"Icon", // WOWCLASS_ICON
|
|
"DC", // WOWCLASS_DC
|
|
"Font", // WOWCLASS_FONT
|
|
"MetaFile", // WOWCLASS_METAFILE
|
|
"Region", // WOWCLASS_RGN
|
|
"Bitmap", // WOWCLASS_BITMAP
|
|
"Brush", // WOWCLASS_BRUSH
|
|
"Palette", // WOWCLASS_PALETTE
|
|
"Pen", // WOWCLASS_PEN
|
|
"Object" // WOWCLASS_OBJECT
|
|
};
|
|
|
|
|
|
BOOL MessageNeedsThunking(UINT uMsg)
|
|
{
|
|
switch (uMsg) {
|
|
case WM_CREATE:
|
|
case WM_ACTIVATE:
|
|
case WM_SETFOCUS:
|
|
case WM_KILLFOCUS:
|
|
case WM_SETTEXT:
|
|
case WM_GETTEXT:
|
|
case WM_ERASEBKGND:
|
|
case WM_WININICHANGE:
|
|
case WM_DEVMODECHANGE:
|
|
case WM_ACTIVATEAPP:
|
|
case WM_SETCURSOR:
|
|
case WM_MOUSEACTIVATE:
|
|
case WM_GETMINMAXINFO:
|
|
case WM_ICONERASEBKGND:
|
|
case WM_NEXTDLGCTL:
|
|
case WM_DRAWITEM:
|
|
case WM_MEASUREITEM:
|
|
case WM_DELETEITEM:
|
|
case WM_VKEYTOITEM:
|
|
case WM_CHARTOITEM:
|
|
case WM_SETFONT:
|
|
case WM_GETFONT:
|
|
case WM_QUERYDRAGICON:
|
|
case WM_COMPAREITEM:
|
|
case WM_OTHERWINDOWCREATED:
|
|
case WM_OTHERWINDOWDESTROYED:
|
|
case WM_COMMNOTIFY:
|
|
case WM_WINDOWPOSCHANGING:
|
|
case WM_WINDOWPOSCHANGED:
|
|
case WM_NCCREATE:
|
|
case WM_NCCALCSIZE:
|
|
case WM_COMMAND:
|
|
case WM_HSCROLL:
|
|
case WM_VSCROLL:
|
|
case WM_INITMENU:
|
|
case WM_INITMENUPOPUP:
|
|
case WM_MENUSELECT:
|
|
case WM_MENUCHAR:
|
|
case WM_ENTERIDLE:
|
|
case WM_CTLCOLORMSGBOX:
|
|
case WM_CTLCOLOREDIT:
|
|
case WM_CTLCOLORLISTBOX:
|
|
case WM_CTLCOLORBTN:
|
|
case WM_CTLCOLORDLG:
|
|
case WM_CTLCOLORSCROLLBAR:
|
|
case WM_CTLCOLORSTATIC:
|
|
case WM_PARENTNOTIFY:
|
|
case WM_MDICREATE:
|
|
case WM_MDIDESTROY:
|
|
case WM_MDIACTIVATE:
|
|
case WM_MDIGETACTIVE:
|
|
case WM_MDISETMENU:
|
|
case WM_RENDERFORMAT:
|
|
case WM_PAINTCLIPBOARD:
|
|
case WM_VSCROLLCLIPBOARD:
|
|
case WM_SIZECLIPBOARD:
|
|
case WM_ASKCBFORMATNAME:
|
|
case WM_CHANGECBCHAIN:
|
|
case WM_HSCROLLCLIPBOARD:
|
|
case WM_PALETTEISCHANGING:
|
|
case WM_PALETTECHANGED:
|
|
case MM_JOY1MOVE:
|
|
case MM_JOY2MOVE:
|
|
case MM_JOY1ZMOVE:
|
|
case MM_JOY2ZMOVE:
|
|
case MM_JOY1BUTTONDOWN:
|
|
case MM_JOY2BUTTONDOWN:
|
|
case MM_JOY1BUTTONUP:
|
|
case MM_JOY2BUTTONUP:
|
|
case MM_MCINOTIFY:
|
|
case MM_MCISYSTEM_STRING:
|
|
case MM_WOM_OPEN:
|
|
case MM_WOM_CLOSE:
|
|
case MM_WOM_DONE:
|
|
case MM_WIM_OPEN:
|
|
case MM_WIM_CLOSE:
|
|
case MM_WIM_DATA:
|
|
case MM_MIM_OPEN:
|
|
case MM_MIM_CLOSE:
|
|
case MM_MIM_DATA:
|
|
case MM_MIM_LONGDATA:
|
|
case MM_MIM_ERROR:
|
|
case MM_MIM_LONGERROR:
|
|
case MM_MOM_OPEN:
|
|
case MM_MOM_CLOSE:
|
|
case MM_MOM_DONE:
|
|
LOGDEBUG(LOG_IMPORTANT,
|
|
("MessageNeedsThunking: WM_msg %04x is not thunked\n", uMsg));
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
PTD ThreadProcID32toPTD(DWORD dwThreadID, DWORD dwProcessID)
|
|
{
|
|
PTD ptd, ptdThis;
|
|
PWOAINST pWOA;
|
|
|
|
//
|
|
// If we have active child instances of WinOldAp,
|
|
// try to map the process ID of a child Win32 app
|
|
// to the corresponding WinOldAp PTD.
|
|
//
|
|
|
|
ptdThis = CURRENTPTD();
|
|
|
|
EnterCriticalSection(&ptdThis->csTD);
|
|
|
|
pWOA = ptdThis->pWOAList;
|
|
|
|
while (pWOA && pWOA->dwChildProcessID != dwProcessID) {
|
|
pWOA = pWOA->pNext;
|
|
}
|
|
|
|
if (pWOA) {
|
|
|
|
ptd = pWOA->ptdWOA;
|
|
|
|
LeaveCriticalSection(&ptdThis->csTD);
|
|
|
|
} else {
|
|
|
|
LeaveCriticalSection(&ptdThis->csTD);
|
|
|
|
//
|
|
// We didn't find a WinOldAp PTD to return, see
|
|
// if the thread ID matches one of our app threads.
|
|
//
|
|
|
|
EnterCriticalSection(&gcsWOW);
|
|
|
|
ptd = gptdTaskHead;
|
|
|
|
while (ptd && ptd->dwThreadID != dwThreadID) {
|
|
ptd = ptd->ptdNext;
|
|
}
|
|
|
|
LeaveCriticalSection(&gcsWOW);
|
|
}
|
|
|
|
return ptd;
|
|
|
|
}
|
|
|
|
PTD Htask16toPTD(
|
|
HTASK16 htask16
|
|
) {
|
|
PTD ptd;
|
|
|
|
EnterCriticalSection(&gcsWOW);
|
|
|
|
ptd = gptdTaskHead;
|
|
|
|
while(ptd) {
|
|
|
|
if ( ptd->htask16 == htask16 ) {
|
|
break;
|
|
}
|
|
ptd = ptd->ptdNext;
|
|
}
|
|
|
|
LeaveCriticalSection(&gcsWOW);
|
|
|
|
return ptd;
|
|
}
|
|
|
|
|
|
HTASK16 ThreadID32toHtask16(
|
|
DWORD ThreadID32
|
|
) {
|
|
PTD ptd;
|
|
HTASK16 htask16;
|
|
|
|
|
|
if ( ThreadID32 == 0 ) {
|
|
WOW32ASSERTMSG(ThreadID32, "WOW::ThreadID32tohTask16: Thread ID is 0\n");
|
|
htask16 = 0;
|
|
} else {
|
|
|
|
ptd = ThreadProcID32toPTD( ThreadID32, (DWORD)-1 );
|
|
if ( ptd ) {
|
|
// Good, its one of our wow threads.
|
|
htask16 = ptd->htask16;
|
|
} else {
|
|
// Nope, its is some other 32-bit thread
|
|
htask16 = FindHtaskAlias( ThreadID32 );
|
|
if ( htask16 == 0 ) {
|
|
//
|
|
// See the comment in WOLE2.C for a nice description
|
|
//
|
|
htask16 = AddHtaskAlias( ThreadID32 );
|
|
}
|
|
}
|
|
}
|
|
|
|
return htask16;
|
|
}
|
|
|
|
DWORD Htask16toThreadID32(
|
|
HTASK16 htask16
|
|
) {
|
|
if ( htask16 == 0 ) {
|
|
return( 0 );
|
|
}
|
|
|
|
if ( ISTASKALIAS(htask16) ) {
|
|
return( GetHtaskAlias(htask16,NULL) );
|
|
} else {
|
|
return( THREADID32(htask16) );
|
|
}
|
|
}
|
|
|
|
//***************************************************************************
|
|
// GetGCL_HMODULE - returns the valid hmodule if the window corresponds to
|
|
// a 16bit class else returns the hmodule of 16bit user.exe
|
|
// if the window is of a standard class.
|
|
//
|
|
// These cases are required for compatibility sake.
|
|
// apps like VirtualMonitor, hDC etc depend on such behaviour.
|
|
// - Nanduri
|
|
//***************************************************************************
|
|
WORD gUser16hInstance = 0;
|
|
|
|
ULONG GetGCL_HMODULE(HWND hwnd)
|
|
{
|
|
ULONG ul;
|
|
PTD ptd;
|
|
PWOAINST pWOA;
|
|
DWORD dwProcessID;
|
|
|
|
ul = (ULONG)GetClassLong(hwnd, GCL_HMODULE);
|
|
|
|
//
|
|
// hMod32 = 0xZZZZ0000
|
|
//
|
|
|
|
if (ul != 0 && LOWORD(ul) == 0) {
|
|
|
|
//
|
|
// If we have active WinOldAp children, see if this window
|
|
// belongs to a Win32 process spawned by one of the
|
|
// active winoldap's. If it is, return the hmodule
|
|
// of the corresponding winoldap. Otherwise we
|
|
// return user.exe's hinstance (why not hmodule?)
|
|
//
|
|
|
|
dwProcessID = (DWORD)-1;
|
|
GetWindowThreadProcessId(hwnd, &dwProcessID);
|
|
|
|
ptd = CURRENTPTD();
|
|
|
|
EnterCriticalSection(&ptd->csTD);
|
|
|
|
pWOA = ptd->pWOAList;
|
|
while (pWOA && pWOA->dwChildProcessID != dwProcessID) {
|
|
pWOA = pWOA->pNext;
|
|
}
|
|
|
|
if (pWOA) {
|
|
ul = pWOA->ptdWOA->hMod16;
|
|
LOGDEBUG(LOG_ALWAYS, ("WOW32 GetClassLong(0x%x, GWW_HMODULE) returning 0x%04x\n",
|
|
hwnd, ul));
|
|
} else {
|
|
ul = (ULONG) gUser16hInstance;
|
|
WOW32ASSERT(ul);
|
|
}
|
|
|
|
LeaveCriticalSection(&ptd->csTD);
|
|
}
|
|
else {
|
|
ul = (ULONG)GETHMOD16(ul); // 32-bit hmod is HMODINST32
|
|
}
|
|
|
|
return ul;
|
|
}
|
|
|
|
//
|
|
// EXPORTED handle mapping functions. WOW32 code should use the
|
|
// macros defined in walias.h -- these functions are for use by
|
|
// third-party 32-bit code running in WOW, for example called
|
|
// using generic thunks from WOW-specific 16-bit code.
|
|
//
|
|
|
|
HANDLE WOWHandle32 (WORD h16, WOW_HANDLE_TYPE htype)
|
|
{
|
|
switch (htype) {
|
|
case WOW_TYPE_HWND:
|
|
return HWND32(h16);
|
|
case WOW_TYPE_HMENU:
|
|
return HMENU32(h16);
|
|
case WOW_TYPE_HDWP:
|
|
return HDWP32(h16);
|
|
case WOW_TYPE_HDROP:
|
|
return HDROP32(h16);
|
|
case WOW_TYPE_HDC:
|
|
return HDC32(h16);
|
|
case WOW_TYPE_HFONT:
|
|
return HFONT32(h16);
|
|
case WOW_TYPE_HMETAFILE:
|
|
return HMETA32(h16);
|
|
case WOW_TYPE_HRGN:
|
|
return HRGN32(h16);
|
|
case WOW_TYPE_HBITMAP:
|
|
return HBITMAP32(h16);
|
|
case WOW_TYPE_HBRUSH:
|
|
return HBRUSH32(h16);
|
|
case WOW_TYPE_HPALETTE:
|
|
return HPALETTE32(h16);
|
|
case WOW_TYPE_HPEN:
|
|
return HPEN32(h16);
|
|
case WOW_TYPE_HACCEL:
|
|
return HACCEL32(h16);
|
|
case WOW_TYPE_HTASK:
|
|
return (HANDLE)HTASK32(h16);
|
|
case WOW_TYPE_FULLHWND:
|
|
return (HANDLE)FULLHWND32(h16);
|
|
default:
|
|
return(INVALID_HANDLE_VALUE);
|
|
}
|
|
}
|
|
|
|
WORD WOWHandle16 (HANDLE h32, WOW_HANDLE_TYPE htype)
|
|
{
|
|
switch (htype) {
|
|
case WOW_TYPE_HWND:
|
|
return GETHWND16(h32);
|
|
case WOW_TYPE_HMENU:
|
|
return GETHMENU16(h32);
|
|
case WOW_TYPE_HDWP:
|
|
return GETHDWP16(h32);
|
|
case WOW_TYPE_HDROP:
|
|
return GETHDROP16(h32);
|
|
case WOW_TYPE_HDC:
|
|
return GETHDC16(h32);
|
|
case WOW_TYPE_HFONT:
|
|
return GETHFONT16(h32);
|
|
case WOW_TYPE_HMETAFILE:
|
|
return GETHMETA16(h32);
|
|
case WOW_TYPE_HRGN:
|
|
return GETHRGN16(h32);
|
|
case WOW_TYPE_HBITMAP:
|
|
return GETHBITMAP16(h32);
|
|
case WOW_TYPE_HBRUSH:
|
|
return GETHBRUSH16(h32);
|
|
case WOW_TYPE_HPALETTE:
|
|
return GETHPALETTE16(h32);
|
|
case WOW_TYPE_HPEN:
|
|
return GETHPEN16(h32);
|
|
case WOW_TYPE_HACCEL:
|
|
return GETHACCEL16(h32);
|
|
case WOW_TYPE_HTASK:
|
|
return GETHTASK16(h32);
|
|
default:
|
|
return(0xffff);
|
|
}
|
|
}
|
|
|
|
extern PVOID GdiQueryTable();
|
|
|
|
PVOID gpGdiHandleInfo = (PVOID)-1;
|
|
|
|
//WARNING: This structure must match ENTRY in ntgdi\inc\hmgshare.h
|
|
// and in ..\vdmexts\wow.c
|
|
|
|
typedef struct _ENTRYWOW
|
|
{
|
|
LONG l1;
|
|
LONG l2;
|
|
USHORT FullUnique;
|
|
USHORT us1;
|
|
LONG l3;
|
|
} ENTRYWOW, *PENTRYWOW;
|
|
|
|
|
|
/*++
|
|
Notes on GDI handle mapping:
|
|
|
|
Since NT 3.1, GDI has been limited to handles that had values < 16K. The reason
|
|
for this limitation is not known. But we do know that on Windows 3.1 the same
|
|
limitation existed since GDI handles were really just hLocalMem handles which
|
|
in reality were just offsets into GDI's local heap. The local heap manager
|
|
always returned handles with the two lowest bits set to 0. The 2nd bit, the 2's
|
|
bit, was used by the Win 3.1 heap manager to mark memory as fixed. The 1's bit
|
|
we assume was not set because memory offsets probably weren't odd. Therefore
|
|
they only had 14-bits available for handle values. There are notes in WOW that
|
|
16-bit applications were aware of this and used the two lowest bits for their
|
|
own evil purposes. In fact, we use the lowest bit at times in WOW for evil
|
|
purposes of our own! (see notes in GetDC() thunk in wuser.c)
|
|
|
|
GDI32 handles are made up of two parts, the loword of the handle is the handle
|
|
value which, until Windows XP, was < 16K as mentioned above. The hiword of the
|
|
handle consists of a bunch of "uniqueness" (bad name for these really) bits.
|
|
Prior to Windows XP, WOW thunked GDI32 handles by stripping off the hiword
|
|
uniqueness bits and then left-shifted the loword handle value by 2 -- exposing
|
|
the two low order bits for apps to use to their heart's content. Since the
|
|
handle value was < 16K, we didn't lose any relevant handle information by left-
|
|
shiftng by two. To un-thunk the handle back to 32-bits, we right-shifted it by
|
|
2 and OR'd the uniqueness bits back onto the hiword (we get the uniqueness bits
|
|
from a table that GDI32 exposes to us). This scheme allowed a very nice one-to-
|
|
one mapping of the handles back-and-forth so there was never any need to create
|
|
a mapping table.
|
|
|
|
Enter Windows XP. The GDI group, with good reason, needed to increase the
|
|
number of handles system-wide, so they changed their handles to use all 16-bits
|
|
in the loword handle values. Unfortunately, when we do our left-shift thunk
|
|
thing, any handles values > 16K get trashed and things go south pretty quickly.
|
|
We found this whole issue out very late in the XP client ship cycle (3 weeks to
|
|
RTM) and couldn't do too much about it at that point. So we just tested the
|
|
handles values to see if they were > 16K and told the user that the 16-bit
|
|
subsystem was out of resources & that they needed to reboot their system --
|
|
then we killed the VDM. Gack! Not something somebody running an enterprise
|
|
server wants to see. So...
|
|
|
|
For the .NET Server & SP1 releases of XP we decided that we needed to come up
|
|
with a mapping algorithm that allows WOW to use handles with values above 16K.
|
|
Here it is:
|
|
|
|
1. We allocate a mapping table with 64K entries to accomodate all possible
|
|
32-bit handles. The loword of an h32 is used to index directly into this
|
|
table. An entry in this table is the index of the corresponding h16 in the
|
|
h16 mapping table.
|
|
|
|
2. We reserve a mapping table with 16K entries in virtual memory. Each entry
|
|
in the table contains the corresponding h32, an index to the next free entry
|
|
in the table, and a State. The State can be one of the following: IN_USE,
|
|
SLOT_FREE, H16_DELETE, and GDI_STOCK_OBJECT. Initially we only commit one
|
|
"table page" of memory in the table -- enough to map 1K handles.
|
|
|
|
3. When an app calls an API such as CreateBrush(), we map the returned handle
|
|
into the first available slot in the free list. The index of the selected
|
|
slot is left-shifted by 2 (to open up the lower two bits as before) and that
|
|
value is used as the h16 that we return to the app.
|
|
|
|
4. When the app calls an API using an h16 such as SelectObject(), we right
|
|
shift the handle back into an index into our table and retreive the mapped
|
|
h32 that we stored at the index location.
|
|
|
|
5. When an app calls DeleteObject(), we originally released all the mapping
|
|
information and returned the associated slot index to the end of the free
|
|
list. This didn't work so good. We found that many apps will try to use old
|
|
handles that they already free'd. They also depend on getting back the same
|
|
handle value that they had just free'd -- not good. So, we implemented a
|
|
"lazy-deletion" algorithm to leave all the mapping info in the table as long
|
|
as possible before finally returning it to the free list. Things got much
|
|
better. We also try to re-map recycled 32-bit handles back to the same index
|
|
mapping that they previously had.
|
|
|
|
6. If our free list becomes empty (most slots are marked IN_USE or H16_DELETE
|
|
for lazy-deletion) we will be forced into a reclaim function to try to
|
|
reclaim leaked handles (handles that an app created but never deleted) and
|
|
also finally free *some* of the lazy-delete handles. Reclaimed handles are
|
|
added to the end of the free list. We try not to reclaim all the lazy-delete
|
|
handles during reclaim because that would kind of put a hiccup in our lazy
|
|
deletion scheme. If we are unable to reclaim enough handles, we then commit
|
|
a new table page from our virtual memory reserve and add the new slots to
|
|
the front of the free list.
|
|
|
|
7. We do have to be careful of handle leaks in our table. One potential leak is
|
|
for messages of the WM_PAINTCLIPBOARD nature. Assume that a 16-bit app put
|
|
something on the clipboard in a format only understood by that app. Now, a
|
|
32-bit app wants to paste what is on the clipboard onto his client window.o After a query, the 32-bit app finds that our 16-bit can do the painting for
|
|
him, so the 32-bit app sends a WM_PAINTCLIPBOARD message to the 16-bit app
|
|
complete with an hDC to 32-bit client window. Problem is, GDI handles are
|
|
only good in the process they were created in. User32, intercepts the
|
|
message and just before dispatching it to the 16-bit app, it essentially
|
|
does a CreateCompatibleDC() in the context of the 16-bit app's process and
|
|
passes on the new handle. Works great. We just need to make sure that we
|
|
know when to un-map the new handle from our table or we'll get a leak. See
|
|
code for WM_PAINTCLIPBOARD in wmdisp32.c to see how we do this. There are
|
|
other issues of this nature that aren't so easy because there are no
|
|
reliable clues as to when we can delete these handles that are created by
|
|
external elements on our behalf. To try to keep our table fromm leaking too
|
|
badly we check all handles in our table at reclaim time to see if they are
|
|
still valid. And finally, if the only running task in the VDM is wowexec,
|
|
we just throw away the tables altogether & re-build them from scratch.
|
|
|
|
--*/
|
|
|
|
/*+++
|
|
|
|
Here are some restrictions to 16-bit GDI handles:
|
|
- An h16 can't = 0 since that means failure from API's that return handles.
|
|
If an app specifies an hDC = 0, it usually means the DISPLAY DC.
|
|
- We can't give out handles with values <= COLOR_ENDCOLORS since the
|
|
hbrBackground member of WNDCLASS structs can specify ordinals in that
|
|
range.
|
|
- We can't give out handle values > 0x3FFF (16K).
|
|
- GDI16 caches the stock objects at WOW32 boot time. We need to make sure
|
|
that the stock object handles are always mapped the same -- across all
|
|
WOW processes.
|
|
|
|
So we reduce the size of our table to deal with handles in the range of
|
|
COLOR_ENDCOLORS+1 -> 0x3FFF. Since we left shift table index values by 2 (ie.
|
|
multiply by 4) to get the h16 we give to the app, we can calculate the first
|
|
allowable index value by COLOR_ENDCOLORS/4 + 1.
|
|
|
|
--*/
|
|
#define FIRST_ALLOWABLE_INDEX ((COLOR_ENDCOLORS/4) + 1)
|
|
#define LAST_ALLOWABLE_INDEX 0x3FFF // Allows for
|
|
// FIRST_ALLOWABLE_INDEX -> 0x3FFF entries
|
|
|
|
// These two constants give us the absolute maximum number of GDI16 handles
|
|
// we can support and the maximum size of the GDI16 handle table.
|
|
// If the table is static we should define MAX_GDI16_HANDLES as follows:
|
|
//#define MAX_GDI16_HANDLES ((LAST_ALLOWABLE_INDEX - FIRST_ALLOWABLE_INDEX) + 1)
|
|
// Since the 16-bit mapping table is to grow dynamically, we'll make all pages
|
|
// the same size to simplify things and waste the first few entries in the first
|
|
// page.
|
|
#define MAX_GDI16_HANDLES 0x4000 // 16K handles
|
|
#define MAX_GDI16_HANDLE_TABLE_SIZE (MAX_GDI16_HANDLES * sizeof(GDIH16MAP))
|
|
|
|
#define GDI16_HANDLES_PER_PAGE 512
|
|
#define GDI16_HANDLE_PAGE_SIZE (GDI16_HANDLES_PER_PAGE * sizeof(GDIH16MAP))
|
|
|
|
|
|
// This table *has to* have 64K entries so we can index the 32-bit GDI handles
|
|
// directly by the low word of the h32.
|
|
#define GDI32_HANDLE_TABLE_ENTRIES 0x10000
|
|
#define GDI32_HANDLE_TABLE_SIZE (GDI32_HANDLE_TABLE_ENTRIES * sizeof(GDIH32MAP))
|
|
|
|
// Max number of handles to reclaim when we're short
|
|
#define GDI16_RECLAIM_SIZE 64
|
|
|
|
WORD MapGdi32Handle(HANDLE h32, WORD State);
|
|
void RegisterStockObjects(void);
|
|
BOOL ReclaimTableEntries(void);
|
|
BOOL DeleteMappedGdi32Handle(HANDLE h32, WORD index, BOOL bReclaim);
|
|
BOOL OkToDeleteThis(HANDLE h32, WORD index, BOOL bReclaim);
|
|
BOOL CommitNewGdi16TablePage(PGDIH16MAP pTable16);
|
|
PGDIH16MAP AllocGDI16Table(void);
|
|
|
|
// This is a global so it can be tuned via app comaptflag if necessary.
|
|
int gGdi16ReclaimSize = GDI16_RECLAIM_SIZE;
|
|
|
|
WORD gH16_deleted_count = 0;
|
|
WORD ghGdi16NextFree = 0;
|
|
WORD ghGdi16LastFree = 0;
|
|
HANDLE hGdi32TableHeap = NULL;
|
|
UINT gMaxGdiHandlesPerProcess = 0;
|
|
WORD gLastAllowableIndex = 0;
|
|
WORD gFirstNonStockObject = 0;
|
|
WORD gwNextReclaimStart = 0;
|
|
DWORD gdwPageCommitSize = 0;
|
|
PGDIH16MAP pGdiH16MappingTable = NULL;
|
|
PGDIH32MAP pGdiH32MappingTable = NULL;
|
|
#ifdef DEBUG
|
|
WORD gprevNextFree = 0xFFFF;
|
|
UINT gAllocatedHandleCount = 0;
|
|
#endif
|
|
|
|
|
|
// This routine converts a 16bit GDI handle to a GDI32 handle.
|
|
HANDLE hConvert16to32(int h16)
|
|
{
|
|
WORD index;
|
|
DWORD h32;
|
|
DWORD h_32;
|
|
|
|
// Apps can specify a NULL handle. For instance hDC = NULL => DISPLAY
|
|
if(h16 == 0)
|
|
return(0);
|
|
|
|
// right shift out our left shift thing
|
|
index = (WORD)(h16 >> 2);
|
|
|
|
if((index < FIRST_ALLOWABLE_INDEX) || (index > gLastAllowableIndex)) {
|
|
|
|
// Some WM_CTLCOLOR messages return wierd values for brushes
|
|
WOW32WARNMSG((FALSE),"WOW::hConvert16to32:Bad index value!\n");
|
|
|
|
// bad handles get mapped to 0
|
|
return(0);
|
|
}
|
|
|
|
h32 = (DWORD)pGdiH16MappingTable[index].h32;
|
|
|
|
WOW32WARNMSG((h32),"WOW::hConvert16to32:h32 missing from table!\n");
|
|
|
|
// We might get this because an app is using a handle it already deleted.
|
|
// These can probably be ignored if they come from ReleaseCachedDCs().
|
|
WOW32WARNMSG((pGdiH32MappingTable[LOWORD(h32)].h16index == index),
|
|
"WOW::hConvert16to32:indicies don't jive!\n");
|
|
|
|
// Update the uniqueness bits to match what they currently are in GDI32's
|
|
// handle table. This will give us the "previous" behavior.
|
|
// See bug #498038 -- we might possibly want to remove this.
|
|
h_32 = h32 & 0x0000FFFF;
|
|
h_32 = h_32 | (DWORD)(((PENTRYWOW)gpGdiHandleInfo)[h_32].FullUnique) << 16;
|
|
if(h32 != h_32) {
|
|
WOW32WARNMSG((FALSE),"WOW::hConvert16to32:uniqueness bits !=\n");
|
|
|
|
h32 = h_32;
|
|
pGdiH16MappingTable[index].h32 = (HANDLE)h32;
|
|
}
|
|
|
|
return((HANDLE)h32);
|
|
}
|
|
|
|
|
|
|
|
// This routine converts a GDI32 handle to a 16bit GDI handle
|
|
HAND16 hConvert32to16(DWORD h32)
|
|
{
|
|
WORD index;
|
|
WORD State;
|
|
HANDLE h_32;
|
|
|
|
// A handle == 0 isn't necessarily bad, we just don't do anything with it.
|
|
if(h32 == 0) {
|
|
return(0);
|
|
}
|
|
|
|
// See if we have already mapped a 16-bit version of this handle
|
|
index = pGdiH32MappingTable[LOWORD(h32)].h16index;
|
|
h_32 = pGdiH16MappingTable[index].h32;
|
|
State = pGdiH16MappingTable[index].State;
|
|
|
|
// If it looks like we may have already registered this handle...
|
|
if(index) {
|
|
|
|
WOW32ASSERTMSG((index <= gLastAllowableIndex),
|
|
"WOW::hConvert32to16:Bad index!\n");
|
|
|
|
// Verify the handle indicies match
|
|
if(LOWORD(h32) == LOWORD(h_32)) {
|
|
|
|
// If the 16-bit mapping is marked "IN_USE" will be true for
|
|
// two reasons:
|
|
// 1. The mapping is still valid
|
|
// 2. h32 got deleted without our knowledge and is comming back as
|
|
// a recycled handle. We might as well use the same mapping as
|
|
// before.
|
|
if(State == IN_USE) {
|
|
|
|
// All we need to do is effectively update the uniqueness bits
|
|
// in the 16-bit table entry.
|
|
if(HIWORD(h32) != HIWORD(h_32)) {
|
|
LOGDEBUG(12, ("WOW::hConvert32to16:recycled handle!\n"));
|
|
pGdiH16MappingTable[index].h32 = (HANDLE)h32;
|
|
}
|
|
}
|
|
|
|
// If the mapping was marked for deletion, let's renew it. h32 has
|
|
// been recycled. We might as well use the same mapping as before.
|
|
else if(State == H16_DELETED) {
|
|
pGdiH16MappingTable[index].h32 = (HANDLE)h32;
|
|
pGdiH16MappingTable[index].State = IN_USE;
|
|
gH16_deleted_count--;
|
|
}
|
|
// else h32 is a GDI_STOCK_OBJECT in which case the index is OK
|
|
else if(State != GDI_STOCK_OBJECT) {
|
|
WOW32ASSERTMSG((FALSE),"WOW::hConvert32to16:SLOT_FREE!\n");
|
|
return(0); // debug this
|
|
}
|
|
}
|
|
|
|
// Else if the handle indicies don't match, the h32 got deleted without
|
|
// our knowledge and is now coming back as a recycled handle. We'll use
|
|
// the same mapping as before.
|
|
else {
|
|
pGdiH16MappingTable[index].h32 = (HANDLE)h32;
|
|
pGdiH16MappingTable[index].State = IN_USE;
|
|
}
|
|
}
|
|
|
|
// looks like we need to create a new mapping
|
|
else {
|
|
|
|
index = MapGdi32Handle((HANDLE)h32, IN_USE);
|
|
}
|
|
|
|
// If we couldn't get an index, go see if we can reclaim some entries and
|
|
// find one.
|
|
if(!index) {
|
|
if(ReclaimTableEntries()) {
|
|
index = MapGdi32Handle((HANDLE)h32, IN_USE);
|
|
}
|
|
}
|
|
|
|
if((index < FIRST_ALLOWABLE_INDEX) || (index > gLastAllowableIndex)) {
|
|
#ifdef DEBUG
|
|
if(index < FIRST_ALLOWABLE_INDEX) {
|
|
WOW32ASSERTMSG((FALSE),"WOW::hConvert32to16:index too small!\n");
|
|
}
|
|
else {
|
|
WOW32ASSERTMSG((FALSE),"WOW::hConvert32to16:index too big!\n");
|
|
}
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
// Do our left shift thing and we're done
|
|
return(index << 2);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
WORD MapGdi32Handle(HANDLE h32, WORD State)
|
|
{
|
|
WORD index = 0;
|
|
|
|
|
|
// If all free entries are gone -- nothing to do
|
|
if(ghGdi16NextFree != END_OF_LIST) {
|
|
|
|
index = ghGdi16NextFree;
|
|
|
|
ghGdi16NextFree = pGdiH16MappingTable[index].NextFree;
|
|
|
|
#ifdef DEBUG
|
|
if(ghGdi16NextFree == END_OF_LIST) {
|
|
gprevNextFree = index;
|
|
WOW32WARNMSG((FALSE),"WOW::MapGdi32Handle:Bad NextFree!\n");
|
|
}
|
|
#endif
|
|
// Set the state (either IN_USE or GDI_STOCK_OBJECT)
|
|
pGdiH16MappingTable[index].State = State;
|
|
|
|
// Map the 32bit handle with the 16-bit handle
|
|
pGdiH16MappingTable[index].h32 = h32;
|
|
pGdiH32MappingTable[LOWORD(h32)].h16index = index;
|
|
|
|
#ifdef DEBUG
|
|
gAllocatedHandleCount++;
|
|
#endif
|
|
}
|
|
|
|
return(index);
|
|
}
|
|
|
|
|
|
|
|
// Requires that index & h32 are both non-null.
|
|
// Assumes that index is an *index* and *not* an h16 (not an index << 2).
|
|
// bReclaim specifies that the handle needs to be added to the freelist.
|
|
BOOL DeleteMappedGdi32Handle(HANDLE h32, WORD index, BOOL bReclaim)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
if(OkToDeleteThis(h32, index, bReclaim)) {
|
|
|
|
// We don't actually want to *remove* the mapping from our table unless
|
|
// we are reclaiming entries. Lame apps call us with old deleted handles
|
|
// and we want to pass on the same mapping that they had when the handle
|
|
// was still good. This also allows us to reuse old mappings for h32
|
|
// handles that get recycled.
|
|
if(bReclaim) {
|
|
|
|
// re-initialized the slot
|
|
pGdiH16MappingTable[index].State = SLOT_FREE;
|
|
pGdiH16MappingTable[index].h32 = NULL;
|
|
pGdiH16MappingTable[index].NextFree = END_OF_LIST;
|
|
|
|
// add this slot to the end of the free list
|
|
pGdiH16MappingTable[ghGdi16LastFree].NextFree = index;
|
|
|
|
pGdiH32MappingTable[LOWORD(h32)].h16index = 0;
|
|
|
|
ghGdi16LastFree = index;
|
|
|
|
#ifdef DEBUG
|
|
if(gAllocatedHandleCount > 0) gAllocatedHandleCount--;
|
|
#endif
|
|
}
|
|
|
|
// else just mark this as potentially deleted
|
|
else {
|
|
pGdiH16MappingTable[index].State = H16_DELETED;
|
|
gH16_deleted_count++;
|
|
}
|
|
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return(bRet);
|
|
}
|
|
|
|
|
|
|
|
// Check list of "What can go wrong?'s"
|
|
BOOL OkToDeleteThis(HANDLE h32, WORD index, BOOL bReclaim)
|
|
{
|
|
|
|
HANDLE h_32;
|
|
WORD NextFree;
|
|
WORD index16;
|
|
WORD State;
|
|
|
|
// If we see this, it may be an app error calling DeleteObject(0).
|
|
if(index == 0) {
|
|
WOW32WARNMSG((FALSE),"WOW::OkToDeleteThis:Null index");
|
|
return(FALSE);
|
|
}
|
|
|
|
// Debug why we get this
|
|
if(h32 == NULL) {
|
|
WOW32ASSERTMSG((FALSE),"WOW::OkToDeleteThis:Null h32\n");
|
|
return(FALSE);
|
|
}
|
|
|
|
// Debug why we get this. May be just an app being stupid
|
|
if((index < FIRST_ALLOWABLE_INDEX) || (index > gLastAllowableIndex)) {
|
|
WOW32ASSERTMSG((FALSE),"WOW::OkToDeleteThis:index bad\n");
|
|
return(FALSE);
|
|
}
|
|
|
|
h_32 = pGdiH16MappingTable[index].h32;
|
|
State = pGdiH16MappingTable[index].State;
|
|
NextFree = pGdiH16MappingTable[index].NextFree;
|
|
index16 = pGdiH32MappingTable[LOWORD(h32)].h16index;
|
|
|
|
// Don't remove stock objects from the table!
|
|
if(State == GDI_STOCK_OBJECT) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// Check for the "IN_USE" flag. If it isn't currently in use then it is
|
|
// already in the free list and we don't want to mess with it. Apps
|
|
// have been known to call DeleteObject() twice on the same handle.
|
|
if(!bReclaim && (State != IN_USE)) {
|
|
WOW32WARNMSG((FALSE),"WOW::OkToDeleteThis:Not IN_USE\n");
|
|
return(FALSE);
|
|
}
|
|
|
|
// We should allow this since the index is obviously pointing to an old h32.
|
|
if(h32 != h_32) {
|
|
WOW32WARNMSG((FALSE),"WOW::OkToDeleteThis:h32 != h_32\n");
|
|
return(TRUE);
|
|
}
|
|
|
|
// Don't mark it for delete if the object is still valid.
|
|
if(GetObjectType(h32)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// Debug this. We should probably let the table repair itself.
|
|
if(index16 == 0) {
|
|
WOW32ASSERTMSG((FALSE),"WOW::OkToDeleteThis:index=0 in h32 table\n");
|
|
return(TRUE);
|
|
}
|
|
#endif
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
|
|
// This should be called by functions outside this file to delete mapped h16's.
|
|
void DeleteWOWGdiHandle(HANDLE h32, HAND16 h16)
|
|
{
|
|
WORD index;
|
|
|
|
// convert h16 back into table index
|
|
index = (WORD)(h16 >> 2);
|
|
|
|
DeleteMappedGdi32Handle(h32, index, FALSE);
|
|
}
|
|
|
|
|
|
|
|
|
|
// This should be called by functions outside this file to retrieve the WOWInfo
|
|
// associated with an h32.
|
|
// Returns:
|
|
// The 16-bit mapping for this h32
|
|
// 0 - if h32 is valid but not mapped in our table
|
|
// -1 - if h32 is 0 or bad (BAD_GDI32_HANDLE)
|
|
HAND16 IsGDIh32Mapped(HANDLE h32)
|
|
{
|
|
WORD index;
|
|
HANDLE h_32 = NULL;
|
|
|
|
if(h32) {
|
|
|
|
if(GetObjectType(h32)) {
|
|
|
|
index = pGdiH32MappingTable[LOWORD(h32)].h16index;
|
|
|
|
if(index) {
|
|
h_32 = pGdiH16MappingTable[index].h32;
|
|
}
|
|
|
|
if(h_32 == h32) {
|
|
return((HAND16)index<<2);
|
|
}
|
|
return(0);
|
|
}
|
|
}
|
|
return(BAD_GDI32_HANDLE);
|
|
}
|
|
|
|
|
|
#if 0
|
|
// This is really for a sanity check that the GDI guys haven't increased the
|
|
// GdiProcessHandleQuota past 16K.
|
|
int DisplayYouShouldNotDoThatMsg(int nMsg)
|
|
{
|
|
CHAR szWarn[512];
|
|
CHAR szText[256];
|
|
|
|
LoadString(hmodWOW32,
|
|
iszYouShouldNotDoThat,
|
|
szWarn,
|
|
sizeof(szWarn)/sizeof(CHAR));
|
|
|
|
LoadString(hmodWOW32,
|
|
nMsg,
|
|
szText,
|
|
sizeof(szText)/sizeof(CHAR));
|
|
|
|
if((strlen(szWarn) + strlen(szText)) < 512) {
|
|
strcat(szWarn, szText);
|
|
}
|
|
|
|
LoadString(hmodWOW32,
|
|
iszHeavyUse,
|
|
szText,
|
|
sizeof(szText)/sizeof(CHAR));
|
|
|
|
if((strlen(szWarn) + strlen(szText)) < 512) {
|
|
strcat(szWarn, szText);
|
|
}
|
|
|
|
return(MessageBox(NULL,
|
|
szWarn,
|
|
NULL,
|
|
MB_YESNO |
|
|
MB_DEFBUTTON2 |
|
|
MB_SYSTEMMODAL |
|
|
MB_ICONEXCLAMATION));
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
BOOL InitializeGdiHandleMappingTable(void)
|
|
{
|
|
HKEY hKey = 0;
|
|
DWORD dwType;
|
|
DWORD cbSize = sizeof(DWORD);
|
|
CHAR szError[256];
|
|
|
|
gpGdiHandleInfo = GdiQueryTable();
|
|
|
|
// The GDI per-process handle limit has to be obtained from the registry.
|
|
// ie. It can be changed by a user! (not doc'd but you know how that goes)
|
|
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) == ERROR_SUCCESS) {
|
|
|
|
RegQueryValueEx(hKey,
|
|
"GdiProcessHandleQuota",
|
|
0,
|
|
&dwType,
|
|
(LPBYTE)&gMaxGdiHandlesPerProcess,
|
|
&cbSize);
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
WOW32ASSERTMSG((gMaxGdiHandlesPerProcess != 0),
|
|
"WOW::InitializeGdiHandleMappingTable:Default GDI max!\n");
|
|
|
|
// We have to be <= ~16K or we wouldn't have to do all this stuff.
|
|
if(gMaxGdiHandlesPerProcess > MAX_GDI16_HANDLES) {
|
|
|
|
// limit them at the max.
|
|
gMaxGdiHandlesPerProcess = MAX_GDI16_HANDLES;
|
|
}
|
|
|
|
// Allocate the 32-bit handle mapping table.
|
|
hGdi32TableHeap = HeapCreate(HEAP_NO_SERIALIZE,
|
|
GDI32_HANDLE_TABLE_SIZE,
|
|
GROW_HEAP_AS_NEEDED);
|
|
|
|
if(hGdi32TableHeap == NULL) {
|
|
goto IGHMT_error;
|
|
}
|
|
|
|
pGdiH32MappingTable = HeapAlloc(hGdi32TableHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
GDI32_HANDLE_TABLE_SIZE);
|
|
|
|
if(pGdiH32MappingTable == NULL) {
|
|
goto IGHMT_error;
|
|
}
|
|
|
|
pGdiH16MappingTable = AllocGDI16Table();
|
|
|
|
if(pGdiH16MappingTable == NULL) {
|
|
goto IGHMT_error;
|
|
}
|
|
|
|
// Add the stock objects as the first entries in the GDI handle mapping
|
|
// tables.
|
|
RegisterStockObjects();
|
|
|
|
return(TRUE);
|
|
|
|
IGHMT_error:
|
|
cbSize = LoadString(hmodWOW32,
|
|
iszStartupFailed,
|
|
szError,
|
|
sizeof(szError)/sizeof(CHAR));
|
|
|
|
if((cbSize == 0) || (cbSize >= 256)) {
|
|
|
|
strcpy(szError, "Not enough memory to load 16-bit subsystem.");
|
|
}
|
|
|
|
WOWSysErrorBox(NULL, szError, SEB_OK, 0, 0);
|
|
|
|
if(pGdiH32MappingTable) {
|
|
HeapFree(hGdi32TableHeap, HEAP_NO_SERIALIZE, pGdiH32MappingTable);
|
|
}
|
|
|
|
if(hGdi32TableHeap) {
|
|
HeapDestroy(hGdi32TableHeap);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
|
|
PGDIH16MAP AllocGDI16Table(void)
|
|
{
|
|
SIZE_T dwSize;
|
|
PGDIH16MAP pTable16;
|
|
|
|
#ifdef DEBUG
|
|
SYSTEM_INFO sSysInfo;
|
|
|
|
GetSystemInfo(&sSysInfo);
|
|
|
|
// If either of these go off we need to come up with a way to align our
|
|
// struct within memory page sizes.
|
|
WOW32ASSERTMSG((!(sSysInfo.dwPageSize % sizeof(GDIH16MAP))),
|
|
"WOW::AllocGDI16Table:Page alignment issue!\n");
|
|
WOW32ASSERTMSG(
|
|
(!((sizeof(GDIH16MAP) * GDI16_HANDLES_PER_PAGE) % sSysInfo.dwPageSize)),
|
|
"WOW::AllocGDI16Table:Page alignment issue 2!\n");
|
|
#endif
|
|
|
|
// Reserve the biggest table we'll ever need.
|
|
// (This is a call to VirtualAlloc() in disguise).
|
|
dwSize = MAX_GDI16_HANDLE_TABLE_SIZE;
|
|
pTable16 = NULL;
|
|
if(!NT_SUCCESS(NtAllocateVirtualMemory(ghProcess,
|
|
(PVOID *)&pTable16,
|
|
0,
|
|
&dwSize,
|
|
MEM_RESERVE,
|
|
PAGE_READWRITE))) {
|
|
WOW32ASSERTMSG((FALSE),
|
|
"WOW::AllocGDI16Table:Alloc reserve failed!\n");
|
|
return(NULL);
|
|
}
|
|
|
|
// Commit the first "table page"
|
|
gdwPageCommitSize = 0;
|
|
gLastAllowableIndex = (WORD)-1; // cheese!
|
|
ghGdi16NextFree = END_OF_LIST;
|
|
if(!CommitNewGdi16TablePage(pTable16)) {
|
|
dwSize = 0;
|
|
NtFreeVirtualMemory(ghProcess,
|
|
(PVOID *)&pTable16,
|
|
&dwSize,
|
|
MEM_RELEASE);
|
|
return(NULL);
|
|
}
|
|
|
|
ghGdi16NextFree = FIRST_ALLOWABLE_INDEX; // adjust this for init case
|
|
ghGdi16LastFree = gLastAllowableIndex;
|
|
gH16_deleted_count = 0;
|
|
|
|
return(pTable16);
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CommitNewGdi16TablePage(PGDIH16MAP pTable16)
|
|
{
|
|
WORD index;
|
|
WORD wFirstNewIndex, wLastNewIndex;
|
|
SIZE_T dwSize;
|
|
PVOID p;
|
|
|
|
// If we've allocated the last table page already -- we can't grow any more.
|
|
if(gdwPageCommitSize >= MAX_GDI16_HANDLE_TABLE_SIZE) {
|
|
WOW32WARNMSG((FALSE),
|
|
"WOW::CommitNewGDI16TablePage:End of table!\n");
|
|
return(FALSE);
|
|
}
|
|
|
|
// Try to grow the number of comitted pages in our table.
|
|
dwSize = GDI16_HANDLE_PAGE_SIZE;
|
|
p = (PVOID)(((LPBYTE)pTable16) + gdwPageCommitSize);
|
|
if(!NT_SUCCESS(NtAllocateVirtualMemory(ghProcess,
|
|
&p,
|
|
0,
|
|
&dwSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE))) {
|
|
WOW32ASSERTMSG((FALSE),
|
|
"WOW::CommitNewGDI16TablePage:Commit failed!\n");
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
WOW32ASSERTMSG((dwSize == GDI16_HANDLE_PAGE_SIZE),
|
|
"WOW::CommitNewGDI16TablePage:Page boundary mismatch!\n");
|
|
|
|
// Build the free list in the new page.
|
|
// Note: NtAllocateVirtualMemory() zero init's memory, therefore the h32 and
|
|
// State members of each GDIH16MAP entry are NULL & SLOT_FREE by default.
|
|
wFirstNewIndex = gLastAllowableIndex + 1;
|
|
wLastNewIndex = wFirstNewIndex + GDI16_HANDLES_PER_PAGE - 1;
|
|
for(index = wFirstNewIndex; index < wLastNewIndex; index++) {
|
|
pTable16[index].NextFree = index + 1;
|
|
}
|
|
gLastAllowableIndex += GDI16_HANDLES_PER_PAGE;
|
|
|
|
// Put these new entries at the head of the free list.
|
|
pTable16[gLastAllowableIndex].NextFree = ghGdi16NextFree;
|
|
ghGdi16NextFree = wFirstNewIndex;
|
|
|
|
gdwPageCommitSize += GDI16_HANDLE_PAGE_SIZE;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the list of stock objects to begining of our table.
|
|
void RegisterStockObjects(void)
|
|
{
|
|
WORD index = 0;
|
|
HANDLE h32;
|
|
int i;
|
|
|
|
for(i = WHITE_BRUSH; i <= STOCK_MAX; i++) {
|
|
|
|
h32 = GetStockObject(i);
|
|
|
|
// currently there is no stock object ordinal == 9
|
|
if(h32) {
|
|
|
|
// Marking the State as GDI_STOCK_OBJECT assures us that stock
|
|
// objects won't get deleted from the table.
|
|
index = MapGdi32Handle(h32, GDI_STOCK_OBJECT);
|
|
}
|
|
}
|
|
gFirstNonStockObject = index + 1;
|
|
gwNextReclaimStart = gFirstNonStockObject;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Reclaim some of the lazily deleted handles (State == H16_DELETE) into the
|
|
// free list. Also attempt to clean up handles that may have leaked and are
|
|
// no longer valid.
|
|
BOOL ReclaimTableEntries(void)
|
|
{
|
|
WORD index;
|
|
WORD State;
|
|
WORD wReclaimStart, wReclaimEnd;
|
|
WORD PrevLastFree = ghGdi16LastFree;
|
|
HANDLE h32;
|
|
WORD i;
|
|
int cFree;
|
|
BOOL bFirst = TRUE;
|
|
|
|
// Note that we attempt to somewhat preserve the lazy-deletion scheme
|
|
// to avoid putting a large hiccup in the scheme.
|
|
|
|
// First determine if we can reclaim without deleting too many H16_DELETE
|
|
// entries. If we can't keep a minimal number around, it's time to commit
|
|
// a new table page.
|
|
if(gH16_deleted_count < (gGdi16ReclaimSize * 2)) {
|
|
if(CommitNewGdi16TablePage(pGdiH16MappingTable)) {
|
|
return(TRUE);
|
|
}
|
|
// if the commit failed, all we can do is reclaim
|
|
}
|
|
|
|
// This is an attempt to keep from always reclaiming from the front of the
|
|
// table. It might not be that important to do this as things get pretty
|
|
// well shuffled over time. Probably advisable during table's early life.
|
|
// The first time through we start at where we left off last time. The
|
|
// 2nd time through we start from the beginning.
|
|
wReclaimStart = gwNextReclaimStart;
|
|
wReclaimEnd = gLastAllowableIndex;
|
|
cFree = 0;
|
|
for(i = 0; i < 2; i++) {
|
|
for(index = wReclaimStart; index <= wReclaimEnd; index++){
|
|
|
|
State = pGdiH16MappingTable[index].State;
|
|
h32 = pGdiH16MappingTable[index].h32;
|
|
|
|
// If it is marked as deleted...
|
|
if(State == H16_DELETED) {
|
|
|
|
// ...destroy the mapping and add it to the free list
|
|
if(DeleteMappedGdi32Handle(h32, index, TRUE)) {
|
|
cFree++;
|
|
gH16_deleted_count--;
|
|
|
|
// If this is the first index we're reclaiming and the free
|
|
// list is empty, make index to the new previous lastfree
|
|
// the new start of the free list. This is kind of tricky.
|
|
// It will be the case where the call to MapGdi32Handle()
|
|
// returns index == 0 in hConvert32to16() and this function
|
|
// is called as a result. What has happened is that the
|
|
// ghGdi16NextFree ptr has caught up with ghGdi16LastFree
|
|
// ptr and then we get another call to hConvert32to16() to
|
|
// map a new h32. ghGdi16NextFree is now == 0 at this point
|
|
// so we fail the call to MapGdi32Handle(). This forces this
|
|
// reclaim function to be called. Here ghGdi16NextFree is
|
|
// set back to ghGdi16LastFree while ghGdi16LastFree will be
|
|
// set to the value of the first handle to be reclaimed via
|
|
// DeleteMappedGdi32Handle(). While in this reclaim loop,
|
|
// the freelist will quickly grow again and there will be
|
|
// plenty of room between ghGdi16NextFree & ghGdi16LastFree
|
|
// again.
|
|
if(bFirst && (ghGdi16NextFree == END_OF_LIST)) {
|
|
ghGdi16NextFree = PrevLastFree;
|
|
}
|
|
bFirst = FALSE;
|
|
|
|
// if we've reclaimed enough, we're done.
|
|
if(cFree >= gGdi16ReclaimSize) {
|
|
gwNextReclaimStart = index + 1;
|
|
goto Done;
|
|
}
|
|
}
|
|
}
|
|
// else if it is marked in use...
|
|
else if(State == IN_USE) {
|
|
|
|
// ...see if the handle is still valid, if not, it is a leak.
|
|
if(!GetObjectType(h32)) {
|
|
|
|
// Mark it H16_DELETED.
|
|
DeleteMappedGdi32Handle(h32, index, FALSE);
|
|
}
|
|
}
|
|
|
|
} // end for
|
|
|
|
wReclaimEnd = wReclaimStart;
|
|
wReclaimStart = gFirstNonStockObject;
|
|
|
|
// If we started at the beginning the first time, no sense in doing
|
|
// it again.
|
|
if(wReclaimEnd == gFirstNonStockObject) {
|
|
goto Done;
|
|
}
|
|
} // end for
|
|
|
|
gwNextReclaimStart = index;
|
|
|
|
Done:
|
|
if(gwNextReclaimStart == gLastAllowableIndex) {
|
|
gwNextReclaimStart = gFirstNonStockObject;
|
|
}
|
|
|
|
WOW32ASSERTMSG((cFree),"WOW::ReclaimTableEntries:cFree = 0!\n");
|
|
return(cFree);
|
|
}
|
|
|
|
|
|
|
|
// NOTE: This should only be called if nWOWTasks == 1!
|
|
// If the only task running is wowexec, we can cleanup any handles that
|
|
// the WOW process leaked.
|
|
// Note: This can break the debug wowexec if anybody ever changes it to use
|
|
// any GDI handles other than stock objects.
|
|
void RebuildGdiHandleMappingTables(void)
|
|
{
|
|
SIZE_T dwSize;
|
|
PGDIH16MAP pTable16 = pGdiH16MappingTable;
|
|
#if 0
|
|
// We should look at this to see if we can clean up any leaks for our process in
|
|
// GDI32. We have to be careful though because there may be handles that were
|
|
// allocated on our behalf from such components as USER32 who might have cached
|
|
// the handle. If we call DeleteObject() on a handle that is in a USER32 cache
|
|
// it might be disasterous. In short, we would like to do this but probably
|
|
// can't.
|
|
DWORD dwType;
|
|
HANDLE h32;
|
|
WORD index;
|
|
|
|
// Go free any GDI handles that this process might have.
|
|
for(index = FIRST_ALLOWABLE_INDEX; index <= gLastAllowableIndex; index++) {
|
|
|
|
h32 = pGdiH16MappingTable[index].h32;
|
|
|
|
// if the handle is still valid in our process...
|
|
dwType = GetObjectType(h32);
|
|
if(h32 && dwType) {
|
|
|
|
// formally delete the handle
|
|
switch(dwType) {
|
|
|
|
// No way to tell which DC allocation mechanism this came
|
|
// from (CreateDC(), BeginPaint(), GetxxxDC() etc). We can
|
|
// really mess things up if we call the wrong delete
|
|
// function, so we'll just let the GDI32 & USER32 process
|
|
// clean-up code deal with these.
|
|
case OBJ_DC:
|
|
break;
|
|
|
|
// The rest we can use DeleteObject()
|
|
default:
|
|
DeleteObject(h32);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// De-commit entire 16-bit handle table & re-build both tables. This could
|
|
// burn us if the debug version of wowexec is running with its window open
|
|
// and they get a GDI handle but I don't think it is likely. We're also
|
|
// somewhat protected by the fact the GDI16 caches the stock objects.
|
|
dwSize = gdwPageCommitSize;
|
|
if(NT_SUCCESS(NtFreeVirtualMemory(ghProcess,
|
|
(PVOID *)&pTable16,
|
|
&dwSize,
|
|
MEM_DECOMMIT))) {
|
|
|
|
RtlZeroMemory(pGdiH32MappingTable, GDI32_HANDLE_TABLE_SIZE);
|
|
|
|
// Commit the first "table page" again
|
|
gdwPageCommitSize = 0;
|
|
gLastAllowableIndex = (WORD)-1; // cheese!
|
|
ghGdi16NextFree = END_OF_LIST;
|
|
#ifdef DEBUG
|
|
gAllocatedHandleCount = 0;
|
|
#endif
|
|
if(!CommitNewGdi16TablePage(pTable16)) {
|
|
dwSize = 0;
|
|
NtFreeVirtualMemory(ghProcess,
|
|
(PVOID *)&pTable16,
|
|
&dwSize,
|
|
MEM_RELEASE);
|
|
WOW32ASSERTMSG((FALSE),
|
|
"WOW::RebuildGdiHandleMappingTables:Panic!\n");
|
|
return;
|
|
}
|
|
|
|
ghGdi16NextFree = FIRST_ALLOWABLE_INDEX; // adjust this for init case
|
|
ghGdi16LastFree = gLastAllowableIndex;
|
|
gH16_deleted_count = 0;
|
|
|
|
// re-register the stock object & we're on our way!
|
|
RegisterStockObjects();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void DeleteGdiHandleMappingTables(void)
|
|
{
|
|
SIZE_T dwSize;
|
|
|
|
dwSize = 0;
|
|
NtFreeVirtualMemory(ghProcess,
|
|
(PVOID *)&pGdiH16MappingTable,
|
|
&dwSize,
|
|
MEM_RELEASE);
|
|
|
|
HeapFree(hGdi32TableHeap, HEAP_NO_SERIALIZE, pGdiH32MappingTable);
|
|
HeapDestroy(hGdi32TableHeap);
|
|
}
|
|
|
|
|
|
|
|
|
|
// We probably don't need to worry about this buffer being too small since we're
|
|
// really only interested in the predefined standard classes which tend to
|
|
// be rather short-named.
|
|
#define MAX_CLASSNAME_LEN 64
|
|
|
|
// There is a time frame (from when an app calls CreateWindow until USER32 gets
|
|
// a message at one of its WndProc's for the window - see FritzS) during which
|
|
// the fnid (class type) can't be set officially for the window. If the
|
|
// GETICLASS macro is invoked during this period, it will be unable to find the
|
|
// iClass for windows created on any of the standard control classes using the
|
|
// fast fnid index method (see walias.h). Once the time frame is passed, the
|
|
// fast fnid method will work fine for these windows.
|
|
//
|
|
// This is manifested in apps that set CBT hooks and try to subclass the
|
|
// standard classes while in their hook proc. See bug #143811.
|
|
|
|
INT GetIClass(PWW pww, HWND hwnd)
|
|
{
|
|
INT iClass;
|
|
DWORD dwClassAtom;
|
|
|
|
// if it is a standard class
|
|
if(((pww->fnid & 0xfff) >= FNID_START) &&
|
|
((pww->fnid & 0xfff) <= FNID_END)) {
|
|
|
|
// return the class id for this initialized window
|
|
iClass = pfnOut.aiWowClass[( pww->fnid & 0xfff) - FNID_START];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
iClass = WOWCLASS_WIN16; // default return: app private class
|
|
|
|
dwClassAtom = GetClassLong(hwnd, GCW_ATOM);
|
|
|
|
if(dwClassAtom) {
|
|
iClass = GetStdClassNumber((PSZ)dwClassAtom);
|
|
}
|
|
}
|
|
|
|
return(iClass);
|
|
}
|
|
|