Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3080 lines
83 KiB

/*++
*
* WOW v1.0
*
* Copyright (c) 1991, Microsoft Corporation
*
* WOW32.C
* WOW32 16-bit API support
*
* History:
* Created 27-Jan-1991 by Jeff Parsons (jeffpar)
* Multi-Tasking 23-May-1991 Matt Felton [mattfe]
* WOW as DLL 06-Dec-1991 Sudeep Bharati (sudeepb)
* Cleanup and rework multi tasking feb 6 (mattfe)
* added notification thread for task creation mar-11 (mattfe)
* added basic exception handling for retail build apr-3 92 mattfe
* use host_ExitThread apr-17 92 daveh
* Hung App Support june-22 82 mattfe
--*/
#include "precomp.h"
#pragma hdrstop
#include "wktbl.h"
#include "wutbl.h"
#include "wgtbl.h"
#include "wstbl.h"
#include "wkbtbl.h"
#include "wshltbl.h"
#include "wmmtbl.h"
#include "wsocktbl.h"
#include "wthtbl.h"
#include "wowit.h"
#include <stdarg.h>
#include <ntcsrdll.h>
#include <tsappcmp.h>
/* Function Prototypes */
DWORD W32SysErrorBoxThread2(PTDB pTDB);
VOID StartDebuggerForWow(VOID);
BOOLEAN LoadCriticalStringResources(void);
// For Dynamic Patch Module support
extern PFAMILY_TABLE *pgDpmWowFamTbls;
extern PDPMMODULESETS *pgDpmWowModuleSets;
extern DECLSPEC_IMPORT ULONG *ExpLdt;
#define LDT_DESC_PRESENT 0x8000
#define STD_SELECTOR_BITS 0x7
MODNAME(wow32.c);
// for logging iloglevel to a file
#ifdef DEBUG
CHAR szLogFile[128];
int fLog;
HANDLE hfLog;
UCHAR gszAssert[256];
#endif
/* iloglevel = 16 MAX the world (all 16 bit kernel internal calls
* iloglevel = 14 All internal WOW kernel Calls
* ilogeveel = 12 All USER GDI call + return Codes
* iloglevel = 5 Returns From Calls
* iloglevel = 3 Calling Parameters
*/
INT flOptions; // command line optin
#ifdef DEBUG
INT iLogLevel; // logging level; 0 implies none
INT fDebugWait=0; // Single Step, 0 = No single step
#endif
HANDLE hmodWOW32;
HANDLE hHostInstance;
#ifdef DEBUG
INT fLogFilter = -1; // Logging Code Fiters
WORD fLogTaskFilter = (WORD)-1; // Filter Logging for Specific TaskID
#endif
#ifdef DEBUG
BOOL fSkipLog; // TRUE to temporarily skip certain logging
INT iReqLogLevel; // Current Output LogLevel
INT iCircBuffer = CIRC_BUFFERS-1; // Current Buffer
CHAR achTmp[CIRC_BUFFERS][TMP_LINE_LEN] = {" "}; // Circular Buffer
CHAR *pachTmp = &achTmp[0][0];
WORD awfLogFunctionFilter[FILTER_FUNCTION_MAX] = {0xffff,0,0,0,0,0,0,0,0,0}; // Specific Filter API Array
PWORD pawfLogFunctionFilter = awfLogFunctionFilter;
INT iLogFuncFiltIndex; // Index Into Specific Array for Debugger Extensions
#endif
#ifdef DEBUG_MEMLEAK
CRITICAL_SECTION csMemLeak;
#endif
UINT iW32ExecTaskId = (UINT)-1; // Base Task ID of Task Being Exec'd
UINT nWOWTasks = 0; // # of WOW tasks running
BOOL fBoot = TRUE; // TRUE During the Boot Process
HANDLE ghevWaitCreatorThread = (HANDLE)-1; // Used to Syncronize creation of a new thread
BOOL fWowMode; // Flag used to determine wow mode.
// currently defaults to FALSE (real mode wow)
// This is used by the memory access macros
// to properly form linear addresses.
// When running on an x86 box, it will be
// initialized to the mode the first wow
// bop call is made in. This flag can go
// away when we no longer want to run real
// mode wow. (Daveh 7/25/91)
HANDLE hWOWHeap;
HANDLE ghProcess; // WOW Process Handle
PFNWOWHANDLERSOUT pfnOut;
PTD * pptdWOA;
PTD gptdShell;
DWORD fThunkStrRtns; // used as a BOOL
BOOL gfDebugExceptions; // set to 1 in debugger to
// enable debugging of W32Exception
BOOL gfIgnoreInputAssertGiven;
DWORD dwSharedWowTimeout;
WORD gwKrnl386CodeSeg1; // code segs of krnl386.exe
WORD gwKrnl386CodeSeg2;
WORD gwKrnl386CodeSeg3;
WORD gwKrnl386DataSeg1;
extern PFAMILY_TABLE *pgDpmWowFamTbls;
extern PDPMMODULESETS *pgDpmWowModuleNames;
#ifndef _X86_
PUCHAR IntelMemoryBase; // Start of emulated CPU's memory
#endif
DWORD gpsi = 0;
DWORD gpfn16GetProcModule;
/* for WinFax Lite install hack -- see wow32fax.c */
char szWINFAX[] = "WINFAX";
char szModem[] = "modem";
char szINSTALL[] = "INSTALL";
char szWINFAXCOMx[80];
BOOL gbWinFaxHack = FALSE;
#define TOOLONGLIMIT _MAX_PATH
#define WARNINGMSGLENGTH 255
PSZ aszCriticalStrings[CRITICAL_STRING_COUNT];
char szEmbedding[] = "embedding";
char szDevices[] = "devices";
char szBoot[] = "boot";
char szShell[] = "shell";
char szServerKey[] = "protocol\\StdFileEditing\\server";
char szPicture[] = "picture";
char szPostscript[] = "postscript";
char szZapfDingbats[] = "ZAPFDINGBATS";
char szZapf_Dingbats[] = "ZAPF DINGBATS";
char szSymbol[] = "SYMBOL";
char szTmsRmn[] = "TMS RMN";
char szHelv[] = "HELV";
char szMavisCourier[]= "MAVIS BEACON COURIER FP";
char szWinDotIni[] = "win.ini";
char szSystemDotIni[] = "system.ini";
char szExplorerDotExe[] = "Explorer.exe";
char szDrWtsn32[] = "drwtsn32";
PSTR pszWinIniFullPath = NULL;
PSTR pszWindowsDirectory = NULL;
PSTR pszSystemDirectory = NULL;
PWSTR pszSystemDirectoryW = NULL;
BOOL gbDBCSEnable = FALSE;
DWORD cbSystemDirLen = 0; // Len of *SHORT* path to system32 dir not incl NULL
DWORD cbSystemDirLenW = 0;// # WCHARS in *LONG* Wpath to sys32 dir not incl NULL
DWORD cbWindowsDirLen = 0; // Len of short path to c:\windows dir not incl NULL
DWORD cbWinIniFullPathLen = 0; // Len of short path to win.ini not incl NULL
#ifdef FE_SB
char szSystemMincho[] = {(char) 0xbc, (char) 0xbd, (char) 0xc3, (char) 0xd1,
(char) 0x96, (char) 0xbe, (char) 0x92, (char) 0xa9,
(char) 0 };
char szMsMincho[] = { (char) 0x82, (char) 0x6c, (char) 0x82, (char) 0x72,
(char) 0x20, (char) 0x96, (char) 0xbe, (char) 0x92,
(char) 0xa9, (char) 0};
#endif
extern CRITICAL_SECTION VdmLoadCritSec;
extern LIST_ENTRY TimerList;
extern BOOL InitializeGdiHandleMappingTable(void);
extern void DeleteGdiHandleMappingTables(void);
PVOID pfnGetVersionExA; // Used with Version Lie hack. Function pointer to GetVersionExA
// See WK32GetProcAddress32W in wkgthunk.c
PVOID pfnCreateDirectoryA; // Used with GtCompCreateDirectoryA in wkgthunk.c
PVOID pfnLoadLibraryA; // Used with GtCompLoadLibraryA in wkgthunk.c
PVOID pfnCreateFileA; // Used with GtCompCreateFileA in wkgthunk.c
PVOID pfnMoveFileA; // Used with GtCompMoveFileA in wkgthunk.c
// Given to us by the terminal server folks to detect if we are in TS.
BOOL IsTerminalAppServer(void)
{
OSVERSIONINFOEX osVersionInfo;
DWORDLONG dwlConditionMask = 0;
BOOL fIsWTS;
osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
fIsWTS = GetVersionEx((OSVERSIONINFO *)&osVersionInfo) &&
(osVersionInfo.wSuiteMask & VER_SUITE_TERMINAL) &&
!(osVersionInfo.wSuiteMask & VER_SUITE_SINGLEUSERTS);
return fIsWTS;
}
BOOLEAN
W32DllInitialize(
IN PVOID DllHandle,
IN ULONG Reason,
IN PCONTEXT Context OPTIONAL
)
/*++
Routine Description: DllMain function called during ntvdm's
LoadLibrary("wow32")
Arguments:
DllHandle - set global hmodWOW32
Reason - Attach or Detach
Context - Not Used
Return Value:
STATUS_SUCCESS
--*/
{
HMODULE hKrnl32dll;
UNREFERENCED_PARAMETER(Context);
hmodWOW32 = DllHandle;
switch ( Reason ) {
case DLL_PROCESS_ATTACH:
if (!CreateSmallHeap()) {
return FALSE;
}
if ((hWOWHeap = HeapCreate (0,
INITIAL_WOW_HEAP_SIZE,
GROW_HEAP_AS_NEEDED)) == NULL)
return FALSE;
//
// Set up a global WindowsDirectory to be used by other WOW functions.
//
{
char szBuf[MAX_PATH];
int ccb;
ccb = GetSystemDirectory(szBuf, sizeof szBuf);
if (ccb == 0 || ccb >= MAX_PATH)
{
LOGDEBUG(0,("W32INIT ERROR: system path failed\n"));
return(FALSE);
}
ccb++;
pszSystemDirectoryW = malloc_w_or_die(ccb * sizeof(WCHAR));
// Returns length in WCHARS
cbSystemDirLenW = GetSystemDirectoryW(pszSystemDirectoryW, ccb);
WOW32ASSERTMSG((ccb > (INT)cbSystemDirLenW),
("WOW::DLL_PROCESS_ATTACH:System dir mis-match\n"));
cbSystemDirLen = GetShortPathName(szBuf, szBuf, sizeof szBuf);
if (cbSystemDirLen == 0 || cbSystemDirLen >= MAX_PATH)
{
LOGDEBUG(0,("W32INIT ERROR: system path failed 2\n"));
return(FALSE);
}
ccb = cbSystemDirLen + 1;
pszSystemDirectory = malloc_w_or_die(ccb);
RtlCopyMemory(pszSystemDirectory, szBuf, ccb);
if(!GetSystemWindowsDirectory(szBuf, sizeof szBuf) ) {
WOW32ASSERTMSG(FALSE, "WOW32: couldnt get windows directory, terminating.\n");
WOWStartupFailed(); // never returns.
}
GetShortPathName(szBuf, szBuf, sizeof szBuf);
cbWindowsDirLen = strlen(szBuf);
ccb = cbWindowsDirLen + 1;
pszWindowsDirectory = malloc_w_or_die(ccb);
RtlCopyMemory(pszWindowsDirectory, szBuf, ccb);
pszWinIniFullPath = malloc_w_or_die(ccb + 8); // "\win.ini"
cbWinIniFullPathLen = cbWindowsDirLen + 8;
RtlCopyMemory(pszWinIniFullPath, szBuf, ccb);
pszWinIniFullPath[ ccb - 1 ] = '\\';
RtlCopyMemory(pszWinIniFullPath + ccb, szWinDotIni, 8);
}
// initialize hook stubs data.
W32InitHookState(hmodWOW32);
// initialize the thunk table offsets. do it here so the debug process
// gets them.
InitThunkTableOffsets();
//
// initialization for named pipe handling in file thunks
//
InitializeCriticalSection(&VdmLoadCritSec);
//
// Load Critical Error Strings
//
if (!LoadCriticalStringResources()) {
MessageBox(NULL, "The Win16 subsystem could not load critical string resources from wow32.dll, terminating.",
"Win16 subsystem load failure", MB_ICONEXCLAMATION | MB_OK);
return FALSE;
}
W32EWExecer();
InitializeListHead(&TimerList);
if (IsTerminalAppServer()) {
//
// Load tsappcmp.dll
//
HANDLE dllHandle = SafeLoadLibrary (L"tsappcmp.dll");
if (dllHandle) {
gpfnTermsrvCORIniFile = (PTERMSRVCORINIFILE) GetProcAddress(
dllHandle,
"TermsrvCORIniFile"
);
ASSERT(gpfnTermsrvCORIniFile != NULL);
}
}
//
// WHISTLER RAID BUG 366613
// Used for redirecting WK32GetProcAddress32W call on GetVersionExA
//
hKrnl32dll = GetModuleHandle("Kernel32.dll");
if(hKrnl32dll)
{
pfnGetVersionExA = GetProcAddress(hKrnl32dll, "GetVersionExA");
pfnCreateDirectoryA = GetProcAddress(hKrnl32dll, "CreateDirectoryA");
pfnLoadLibraryA = GetProcAddress(hKrnl32dll, "LoadLibraryA");
pfnCreateFileA = GetProcAddress(hKrnl32dll, "CreateFileA");
pfnMoveFileA = GetProcAddress(hKrnl32dll, "MoveFileA");
}
break;
case DLL_THREAD_ATTACH:
IsDebuggerAttached(); // Yes, this routine has side-effects.
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
/*
* Tell base he can nolonger callback to us.
*/
RegisterWowBaseHandlers(NULL);
DeleteCriticalSection(&VdmLoadCritSec);
DeleteGdiHandleMappingTables();
HeapDestroy (hWOWHeap);
break;
default:
break;
}
return TRUE;
}
BOOLEAN
LoadCriticalStringResources(
void
)
/*++
Routine Description: Loads strings we want around even if we can't allocate
memory. Called during wow32 DLL load.
Arguments:
none
Return Value:
TRUE if all strings loaded and aszCriticalStrings initialized.
--*/
{
int i, n;
PSZ psz, pszStringBuffer;
DWORD cbTotal;
DWORD cbUsed;
DWORD cbStrLen;
DWORD rgdwStringOffset[CRITICAL_STRING_COUNT];
//
// Allocate too much memory for strings (maximum possible) at first,
// reallocate to the real size when we're done loading strings.
//
cbTotal = CRITICAL_STRING_COUNT * CCH_MAX_STRING_RESOURCE;
psz = pszStringBuffer = malloc_w(cbTotal);
if ( ! psz ) {
return FALSE;
}
cbUsed = 0;
for ( n = 0; n < CRITICAL_STRING_COUNT; n++ ) {
//
// LoadString return value doesn't count null terminator.
//
cbStrLen = LoadString(hmodWOW32, n, psz, CCH_MAX_STRING_RESOURCE);
if ( ! cbStrLen ) {
return FALSE;
}
rgdwStringOffset[n] = cbUsed;
psz += cbStrLen + 1;
cbUsed += cbStrLen + 1;
}
// Now, alloc a smaller buffer of the correct size
// Note: HeapRealloc(IN_PLACE) won't work because allocations are
// page-sorted by size -- meaning that changing the size will cause
// the memory to move to a new page.
psz = malloc_w(cbUsed);
// copy the strings into the smaller buffer
// if we can't alloc the smaller buffer, just go with the big one
if (psz) {
RtlCopyMemory(psz, pszStringBuffer, cbUsed);
free_w(pszStringBuffer);
pszStringBuffer = psz;
}
// save the offsets in the critical string array
for (i = 0; i < n; i++) {
aszCriticalStrings[i] = pszStringBuffer + rgdwStringOffset[i];
}
return TRUE;
}
//***************************************************************************
// Continues ExitWindowsExec api call after logoff and subsequent logon
// Uses Events to synchronize across all wow vdms
//
//***************************************************************************
BOOL W32EWExecer(VOID)
{
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
BOOL CreateProcessStatus;
BYTE abT[REGISTRY_BUFFER_SIZE];
if (W32EWExecData(EWEXEC_QUERY, (LPSTR)abT, sizeof(abT))) {
HANDLE hevT;
if (hevT = CreateEvent(NULL, TRUE, FALSE, WOWSZ_EWEXECEVENT)) {
if (GetLastError() == 0) {
W32EWExecData(EWEXEC_DEL, (LPSTR)NULL, 0);
LOGDEBUG(0, ("WOW:Execing dos app - %s\r\n", abT));
RtlZeroMemory((PVOID)&StartupInfo, (DWORD)sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow = SW_NORMAL;
CreateProcessStatus = CreateProcess(
NULL,
abT,
NULL, // security
NULL, // security
FALSE, // inherit handles
CREATE_NEW_CONSOLE | CREATE_DEFAULT_ERROR_MODE,
NULL, // environment strings
NULL, // current directory
&StartupInfo,
&ProcessInformation
);
if (CreateProcessStatus) {
WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
CloseHandle( ProcessInformation.hProcess );
CloseHandle( ProcessInformation.hThread );
}
SetEvent(hevT);
}
else {
WaitForSingleObject(hevT, INFINITE);
}
CloseHandle(hevT);
}
}
return 0;
}
//***************************************************************************
// W32EWExecData -
// sets/resets the 'commandline', ie input to ExitWindowssExec api in the
// registry - 'WOW' key 'EWExec' value
//
//***************************************************************************
BOOL W32EWExecData(DWORD fnid, LPSTR lpData, DWORD cb)
{
BOOL bRet = FALSE;
BYTE abT[REGISTRY_BUFFER_SIZE];
switch (fnid) {
case EWEXEC_SET:
bRet = WriteProfileString(WOWSZ_EWEXECVALUE,
WOWSZ_EWEXECVALUE,
lpData);
break;
case EWEXEC_DEL:
bRet = WriteProfileString(WOWSZ_EWEXECVALUE,
NULL, NULL);
break;
case EWEXEC_QUERY:
if (bRet = GetProfileString(WOWSZ_EWEXECVALUE,
WOWSZ_EWEXECVALUE,
"", abT, sizeof(abT))) {
cb = min(cb, sizeof(abT));
cb = min(cb, strlen(abT)+1);
strncpy(lpData, abT, cb);
lpData[cb-1] = '\0';
}
break;
default:
WOW32ASSERT(FALSE);
break;
}
return !!bRet;
}
/* W32Init - Initialize WOW support
*
* ENTRY
*
* EXIT
* TRUE if successful, FALSE if not
*/
BOOL W32Init(VOID)
{
HKEY WowKey;
DWORD cb;
DWORD dwType;
PTD ptd;
PFNWOWHANDLERSIN pfnIn;
LANGID LangID;
#ifndef _X86_
//
// This is the one and only call to Sim32GetVDMPointer in WOW32.
// All other cases should use WOWGetVDMPointer. This one is necessary
// to set up the base memory address used by GetRModeVDMPointerMacro.
// (There's also a call in GetPModeVDMPointerAssert, but that's in
// the checked build only and only as a fallback mechanism.)
//
IntelMemoryBase = Sim32GetVDMPointer(0,0,0);
#endif
// Set the global DPM tables.
BuildGlobalDpmStuffForWow(pgDpmWowFamTbls, pgDpmWowModuleSets);
InitGlobalDpmTables(pgDpmWowFamTbls, NUM_WOW_FAMILIES_HOOKED);
fWowMode = ((getMSW() & MSW_PE) ? TRUE : FALSE);
// Boost the HourGlass
ShowStartGlass(10000);
LangID = GetSystemDefaultLangID();
if (PRIMARYLANGID(LangID) == LANG_JAPANESE ||
PRIMARYLANGID(LangID) == LANG_KOREAN ||
PRIMARYLANGID(LangID) == LANG_CHINESE ) {
gbDBCSEnable = TRUE;
}
// Give USER32 our entry points
RtlZeroMemory(&pfnIn, sizeof(pfnIn));
pfnIn.pfnLocalAlloc = W32LocalAlloc;
pfnIn.pfnLocalReAlloc = W32LocalReAlloc;
pfnIn.pfnLocalLock = W32LocalLock;
pfnIn.pfnLocalUnlock = W32LocalUnlock;
pfnIn.pfnLocalSize = W32LocalSize;
pfnIn.pfnLocalFree = W32LocalFree;
pfnIn.pfnGetExpWinVer = W32GetExpWinVer;
pfnIn.pfn16GlobalAlloc = W32GlobalAlloc16;
pfnIn.pfn16GlobalFree = W32GlobalFree16;
pfnIn.pfnEmptyCB = W32EmptyClipboard;
pfnIn.pfnFindResourceEx = W32FindResource;
pfnIn.pfnLoadResource = W32LoadResource;
pfnIn.pfnFreeResource = W32FreeResource;
pfnIn.pfnLockResource = W32LockResource;
pfnIn.pfnUnlockResource = W32UnlockResource;
pfnIn.pfnSizeofResource = W32SizeofResource;
pfnIn.pfnWowWndProcEx = (PFNWOWWNDPROCEX)W32Win16WndProcEx;
pfnIn.pfnWowDlgProcEx = (PFNWOWDLGPROCEX)W32Win16DlgProcEx;
pfnIn.pfnWowEditNextWord = W32EditNextWord;
pfnIn.pfnWowCBStoreHandle = WU32ICBStoreHandle;
pfnIn.pfnGetProcModule16 = WOWGetProcModule16;
pfnIn.pfnWowMsgBoxIndirectCallback = WowMsgBoxIndirectCallback;
pfnIn.pfnWowIlstrsmp = WOWlstrcmp16;
pfnIn.pfnWOWTellWOWThehDlg = WOWTellWOWThehDlg;
pfnIn.pfnWowTask16SchedNotify = NULL;
gpsi = UserRegisterWowHandlers(&pfnIn, &pfnOut);
RegisterWowBaseHandlers(W32DDEFreeGlobalMem32);
// Allocate a Temporary TD for the first thread
ptd = CURRENTPTD() = malloc_w_or_die(sizeof(TD));
RtlZeroMemory(ptd, sizeof(*ptd));
InitializeCriticalSection(&ptd->csTD);
// Create Global Wait Event - Used During Task Creation To Syncronize with New Thread
if (!(ghevWaitCreatorThread = CreateEvent(NULL, FALSE, FALSE, NULL))) {
LOGDEBUG(0,(" W32INIT ERROR: event creation failure\n"));
return FALSE;
}
if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Control\\WOW",
0,
KEY_QUERY_VALUE,
&WowKey
) != 0){
LOGDEBUG(0,(" W32INIT ERROR: Registry Opening failed\n"));
return FALSE;
}
//
// If present, read the SharedWowTimeout value and convert
// from seconds to milliseconds, which is what SetTimer
// uses. Maximum interval for SetTimer is 0x7fffffff.
// No need to enforce a minimum, as SetTimer treats a
// zero timeout as a one millsecond timeout.
//
cb = sizeof(dwSharedWowTimeout);
if ( ! RegQueryValueEx(WowKey,
"SharedWowTimeout",
NULL,
&dwType,
(LPBYTE) &dwSharedWowTimeout,
&cb) && REG_DWORD == dwType) {
//
// Prevent overflow in the conversion to millseconds below.
// This caps the timeout to 2,147,483 seconds, or 24.8 days.
//
dwSharedWowTimeout = min( dwSharedWowTimeout,
(0x7fffffff / 1000) );
} else {
//
// Didn't find SharedWowTimeout value or it's the wrong type.
//
dwSharedWowTimeout = 1 * 60 * 60; // 1 hour in seconds
}
dwSharedWowTimeout *= 1000;
//
// If present (it usually isn't) read ThunkNLS value entry.
//
cb = sizeof(fThunkStrRtns);
if (RegQueryValueEx(WowKey,
"ThunkNLS",
NULL,
&dwType,
(LPBYTE) &fThunkStrRtns,
&cb) || dwType != REG_DWORD) {
//
// Didn't find the registry value or it's the wrong type,
// so we use the default behavior which is to thunk outside the
// US.
//
fThunkStrRtns = GetSystemDefaultLCID() !=
MAKELCID(
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
SORT_DEFAULT
);
} else {
//
// We did find a ThunkNLS value in the registry, warn on debug builds
// to save testers and developers who turn it on for one bug but forget
// to turn it back off.
//
#ifdef DEBUG
OutputDebugString("WOW Warning: ThunkNLS registry value overriding default NLS tranlation.\n");
#endif
}
//
// Initialize list of known DLLs used by WK32WowIsKnownDLL
// from the registry.
//
WK32InitWowIsKnownDLL(WowKey);
RegCloseKey (WowKey);
//
// Initialize param mapping cache
//
//
InitParamMap();
//
// Set our GDI batching limit from win.ini. This is useful for SGA and
// other performance measurements which require each API to do its own
// work. To set the batching size to 1, which is most common, put the
// following in win.ini:
//
// [WOW]
// BatchLimit=1
//
// or using ini:
//
// ini WOW.BatchLimit = 1
//
// Note that this code only changes the batch limit if the above
// line is in win.ini, otherwise we use default batching. It's
// important that this code be in the free build to be useful.
//
{
extern DWORD dwWOWBatchLimit; // declared in wkman.c
dwWOWBatchLimit = GetProfileInt("WOW", // section
"BatchLimit", // key
0 // default if not found
);
}
ghProcess = NtCurrentProcess();
// setup the GDI table for handle conversion
if(InitializeGdiHandleMappingTable() == FALSE)
return(FALSE);
#ifdef DEBUG
#ifdef i386
if (IsDebuggerAttached()) {
if (GetProfileInt("WOWDebug", "debugbreaks", 0))
*pNtVDMState |= VDM_BREAK_DEBUGGER;
if (GetProfileInt("WOWDebug", "exceptions", 0))
*pNtVDMState |= VDM_BREAK_EXCEPTIONS;
}
#endif
if (IsDebuggerAttached() && (flOptions & OPT_BREAKONNEWTASK)) {
OutputDebugString("\nW32Init - Initialization Complete, Set any Breakpoints Now, type g to continue\n\n");
DbgBreakPoint();
}
#endif
// Initialize ClipBoard formats structure.
InitCBFormats ();
// This is to initialize the InquireVisRgn for FileMaker Pro 2.0
// InquireVisRgn is an undocumented API Win 3.1 API.
InitVisRgn();
// HUNG APP SUPPORT
if (!WK32InitializeHungAppSupport()) {
LOGDEBUG(LOG_ALWAYS, ("W32INIT Error: InitializeHungAppSupport Failed"));
return FALSE;
}
SetPriorityClass(ghProcess, NORMAL_PRIORITY_CLASS);
#ifdef DEBUG_MEMLEAK
// for memory leak support
InitializeCriticalSection(&csMemLeak);
#endif
// 9x Special Path Map Initialization
// i.e. c:\winnt\startm~1 will be c:\documents and settings\all users\start menu
W32Init9xSpecialPath();
return TRUE;
}
/* Thunk Dispatch Table
*
*
*/
#ifdef DEBUG_OR_WOWPROFILE
PA32 awThunkTables[] = {
{W32TAB(aw32WOW, "All ", cAPIThunks)}
};
#endif
#ifdef DEBUG_OR_WOWPROFILE // define symbols for API profiling only (debugger extension)
INT iThunkTableMax = NUMEL(awThunkTables);
PPA32 pawThunkTables = awThunkTables;
#endif // WOWPROFILE
/* WOW32UnimplementedAPI - Error Thunk is Not Implemented
*
* Stub thunk table entry for all unimplemented APIs on
* the checked build, and on the free build NOPAPI and
* LOCALAPI entries point here as well.
*
* ENTRY
*
* EXIT
*
*/
ULONG FASTCALL WOW32UnimplementedAPI(PVDMFRAME pFrame)
{
#ifdef DEBUG
INT iFun;
iFun = pFrame->wCallID;
LOGDEBUG(2,("WOW32: Warning! %s: Function %i %s is not implemented.\n",
GetModName(iFun),
GetOrdinal(iFun),
aw32WOW[iFun].lpszW32
));
//
// After complaining once about each API, patch the thunk table so
// future calls to the API will (mostly) silently slip by in WOW32NopAPI.
//
aw32WOW[iFun].lpfnW32 = WOW32NopAPI;
#else
UNREFERENCED_PARAMETER(pFrame);
#endif
return FALSE;
}
#ifdef DEBUG
/* WOW32Unimplemented95API - Error Thunk is Not Implemented
*
* Stub thunk table entry for Win95 unimplemented APIs on
* the checked build, and for now on the free build as well.
*
* ENTRY
*
* EXIT
*
*/
ULONG FASTCALL WOW32Unimplemented95API(PVDMFRAME pFrame)
{
INT iFun;
iFun = pFrame->wCallID;
WOW32ASSERTMSGF (FALSE, ("New-for-Win95/NT5 %s API %s #%i not implemented, contact DaveHart.\n",
GetModName(iFun),
aw32WOW[iFun].lpszW32,
GetOrdinal(iFun)
));
//
// After complaining once about each API, patch the thunk table so
// future calls to the API will silently slip by.
//
aw32WOW[iFun].lpfnW32 = NOPAPI;
return FALSE;
}
/* WOW32NopAPI - Thunk to do nothing - checked build only.
*
* All Function tables point here for APIs which should do nothing.
*
* ENTRY
*
* EXIT
*
*/
ULONG FASTCALL WOW32NopAPI(PVDMFRAME pFrame)
{
INT iFun;
iFun = pFrame->wCallID;
LOGDEBUG(4,("%s: Function %i %s is NOP'd\n", GetModName(iFun), GetOrdinal(iFun), aw32WOW[iFun].lpszW32));
return FALSE;
}
/* WOW32LocalAPI - ERROR Should Have Been Handled in 16 BIT
* Checked build only
*
* All Function tables point here for Local API Error Messages
*
* ENTRY
* Module startup registers:
*
* EXIT
*
*
*/
ULONG FASTCALL WOW32LocalAPI(PVDMFRAME pFrame)
{
INT iFun;
iFun = pFrame->wCallID;
WOW32ASSERTMSGF (FALSE, ("Error - %s: Function %i %s should be handled by 16-bit code\n",
GetModName(iFun),
GetOrdinal(iFun),
aw32WOW[iFun].lpszW32
));
return FALSE;
}
#endif // DEBUG
LPFNW32 FASTCALL W32PatchCodeWithLpfnw32(PVDMFRAME pFrame , LPFNW32 lpfnW32 )
{
VPVOID vpCode;
LPBYTE lpCode;
#ifdef DEBUG
INT iFun = pFrame->wCallID;
#endif
#ifdef DEBUG_OR_WOWPROFILE
//
// On checked builds do not patch calls to the 4 special
// thunks above, since many entries will point to each one,
// the routines could not easily distinguish which 16-bit
// entrypoint was called.
//
if (flOptions & OPT_DONTPATCHCODE ||
lpfnW32 == UNIMPLEMENTEDAPI ||
lpfnW32 == UNIMPLEMENTED95API ||
lpfnW32 == NOPAPI ||
lpfnW32 == LOCALAPI ) {
goto Done;
}
#endif
//
// just return the thunk function if called in real mode
//
if (!fWowMode) {
goto Done;
}
// the thunk looks like so.
//
// push HI_WCALLID (3bytes) - 0th byte is opcode.
// push 0xfnid (3bytes)
// call wow16call (5bytes)
// ThunksCSIP:
//
// point to the 1st word (the hiword)
vpCode = (DWORD)pFrame->wThunkCSIP - (0x5 + 0x3 + 0x2);
WOW32ASSERT(HI_WCALLID == 0); // we need to revisit wow32.c if this
// value is changed to a non-zero value
WOW32ASSERT(HIWORD(iFun) == HI_WCALLID);
GETVDMPTR(vpCode, 0x2 + 0x3, lpCode);
WOW32ASSERT(lpCode != NULL);
WOW32ASSERT(*(PWORD16)(lpCode) == HIWORD(iFun));
WOW32ASSERT(*(PWORD16)(lpCode+0x3) == LOWORD(iFun));
*((PWORD16)lpCode) = HIWORD((DWORD)lpfnW32);
lpCode += 0x3; // seek to the 2nd word (the loword)
*((PWORD16)lpCode) = LOWORD((DWORD)lpfnW32);
FLUSHVDMCODEPTR(vpCode, 0x2 + 0x3, lpCode);
FREEVDMPTR(lpCode);
Done:
return lpfnW32;
}
/* W32Dispatch - Recipient of all WOW16 API calls (sort of)
*
* "sort of" means that the word "all" above hasn't been true since 8/93:
* 1. Most calls to the 16-bit kernel are handled by krnl386.exe on the
* 16-bit side (this has always been true).
* 2. A FEW (MulDiv, GetMetaFileBits, SetMetaFileBits) GDI API's are thunked
* by GDI.exe in 16-bit land.
* 3. There is an "Interpreted Thunk" mechanism which thunks API's that have
* relatively simple parameter thunks (ie. int16 -> int32, str16->str32,
* and no structs) and require no special hacks. The code for these thunks
* is generated at compile time. See mvdm\wow32\genwowit.txt for a brief
* description of how this works. See wow.it for the list of API's that are
* currently handled by this mechanism. If an API does require a special
* hack, it will need to be yanked from the list in wow.it and the dispatch
* table macro, IT() (currently only used in: wgtbl2.h, wkbdtbl2.h,
* wktbl2.h, and wutbl2.h), must be updated as appropriate. The new thunk
* will have to be hand coded as the rest of our thunks are.
* 4. On CHECKED x86 builds & ALL RISC builds, all API's not subject to the
* above exceptions are dispatched through this function.
* - That's about it -- until we change it again, in which case this note
* could be terribly misleading. cmjones 10/08/97
*
* Having said that:
* This routine dispatches to the relavant WOW thunk routine via
* jump tables wktbl.c wutbl.c wgtbl.c based on a function id on the 16 bit
* stack.
*
* In debug versions it also calls routines to log parameters.
*
* ENTRY
* None (x86 registers contain parameters)
*
* EXIT
* None (x86 registers/memory updated appropriately)
*/
VOID W32Dispatch()
{
INT iFun;
ULONG ulReturn;
DWORD dwThunkCSIP;
VPVOID vpCurrentStack;
register PTD ptd;
register PVDMFRAME pFrame;
#ifdef DEBUG_OR_WOWPROFILE
INT iFunT;
#endif
#ifdef WOWPROFILE
LONGLONG dwTics;
#endif
try {
vpCurrentStack = VDMSTACK(); // Get 16 bit ss:sp
// Use WOWGetVDMPointer here since we can get called in RealMode on
// Errors
pFrame = WOWGetVDMPointer(vpCurrentStack, sizeof(VDMFRAME), fWowMode);
ptd = CURRENTPTD(); // Setup Task Pointer
ptd->vpStack = vpCurrentStack; // Save 16 bit ss:sp
// ssync 16-bit & 32-bit common dialog structs (see wcommdlg.c)
if(ptd->CommDlgTd) {
dwThunkCSIP = (DWORD)(pFrame->wThunkCSIP);
Ssync_WOW_CommDlg_Structs(ptd->CommDlgTd, w16to32, dwThunkCSIP);
}
WOW32ASSERT( FIELD_OFFSET(TD,vpStack) == 0 );
LOGARGS(3,pFrame); // Perform Function Logging
iFun = pFrame->wCallID;
#ifdef DEBUG_OR_WOWPROFILE
iFunT = ISFUNCID(iFun) ? iFun : GetFuncId(iFun) ;
#endif
if (ISFUNCID(iFun)) {
#ifdef DEBUG
if (cAPIThunks && iFunT >= cAPIThunks) {
LOGDEBUG(LOG_ALWAYS,("W32Dispatch: Task %04x thunked to function %d, cAPIThunks = %d.\n",
pFrame->wTDB, iFunT, cAPIThunks));
WOW32ASSERT(FALSE);
}
#endif
iFun = (INT)aw32WOW[iFun].lpfnW32;
if ( ! HIWORD(iFun)) {
#ifdef WOWPROFILE // For API profiling only (debugger extension)
dwTics = GetWOWTicDiff(0I64);
#endif // WOWPROFILE
ulReturn = InterpretThunk(pFrame, iFun);
goto AfterApiCall;
} else {
W32PatchCodeWithLpfnw32(pFrame, (LPFNW32)iFun);
}
}
#ifdef WOWPROFILE // For API profiling only (debugger extension)
dwTics = GetWOWTicDiff(0I64);
#endif // WOWPROFILE
ulReturn = (*((LPFNW32)iFun))(pFrame); // Dispatch to Thunk
AfterApiCall:
// ssync 16-bit & 32-bit common dialog structs (see wcommdlg.c)
if(ptd->CommDlgTd) {
Ssync_WOW_CommDlg_Structs(ptd->CommDlgTd, w32to16, dwThunkCSIP);
}
#ifdef WOWPROFILE // For API profiling only (debugger extension)
dwTics = GetWOWTicDiff(dwTics);
iFun = iFunT;
// add time ellapsed for call to total
aw32WOW[iFun].cTics += dwTics;
aw32WOW[iFun].cCalls++; // inc # times this API called
#endif // WOWPROFILE
FREEVDMPTR(pFrame); // Set the 16-bit return code
GETFRAMEPTR(ptd->vpStack, pFrame);
LOGRETURN(5,pFrame,ulReturn); // Log return Values
pFrame->wAX = LOW(ulReturn); // Pass Back Return Value form thunk
pFrame->wDX = HIW(ulReturn);
#ifdef DEBUG
// If OPT_DEBUGRETURN is set, diddle the RetID as approp.
if (flOptions & OPT_DEBUGRETURN) {
if (pFrame->wRetID == RET_RETURN) {
pFrame->wRetID = RET_DEBUGRETURN;
flOptions &= ~OPT_DEBUGRETURN;
}
}
// Put the current logging level where 16-bit code can get it
// Use ROMBIOS Hard DISK information as a safe address
*(PBYTE)GetVDMAddr(0x0040,0x0042) = (BYTE)(iLogLevel/10+'0');
*(PBYTE)GetVDMAddr(0x0040,0x0043) = (BYTE)(iLogLevel%10+'0');
#endif // DEBUG
FREEVDMPTR(pFrame);
SETVDMSTACK(ptd->vpStack);
} except (W32Exception(GetExceptionCode(), GetExceptionInformation())) {
}
}
/* W32Exception - Handle WOW32 thread exceptions
*
* ENTRY
* None (x86 registers contain parameters)
*
* EXIT
* None (x86 registers/memory updated appropriately)
*
*/
INT W32Exception(DWORD dwException, PEXCEPTION_POINTERS pexi)
{
PTD ptd;
PVDMFRAME pFrame;
int len;
DWORD dwButtonPushed;
char szTask[9];
HMODULE hModule;
char szModule[_MAX_PATH + 1];
PSZ pszModuleFilePart;
PSZ pszErrorFormatString;
char szErrorMessage[TOOLONGLIMIT + 4*WARNINGMSGLENGTH];
char szDialogText[TOOLONGLIMIT + 4*WARNINGMSGLENGTH];
PTDB pTDB;
NTSTATUS Status;
HANDLE DebugPort;
PRTL_CRITICAL_SECTION PebLockPointer;
CHAR AeDebuggerCmdLine[256];
CHAR AeAutoDebugString[8];
BOOL AeAutoDebug;
WORD wDebugButton;
if (!gfDebugExceptions) {
//
// If the process is being debugged, just let the exception happen
// so that the debugger can see it. This way the debugger can ignore
// all first chance exceptions.
//
DebugPort = (HANDLE)NULL;
Status = NtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugPort,
(PVOID)&DebugPort,
sizeof(DebugPort),
NULL
);
if ( NT_SUCCESS(Status) && DebugPort) {
//
// Process is being debugged.
// Return a code that specifies that the exception
// processing is to continue
//
return EXCEPTION_CONTINUE_SEARCH;
}
}
//
// NtClose can raise exceptions if NtGlobalFlag is set for it.
// We want to ignore these exceptions if we're not being debugged,
// since the errors will be returned from the APIs and we generally
// don't have control over what handles the app closes. (Well, that's
// not true for file I/O, but it is true for RegCloseKey.)
//
if (STATUS_INVALID_HANDLE == dwException ||
STATUS_HANDLE_NOT_CLOSABLE == dwException) {
return EXCEPTION_CONTINUE_EXECUTION;
}
//
// See if a debugger has been programmed in. If so, use the
// debugger specified. If not then there is no AE Cancel support
// DEVL systems will default the debugger command line. Retail
// systems will not.
//
// The above paragraph was copied from the system exception
// popup in base. It is no longer true. On retail systems,
// AeDebug.Auto is set to 1 and AeDebug.Debugger is
// "drwtsn32 -p %ld -e %ld -g".
//
// This means if we support AeDebug for stress, customers don't see
// our exception popup and misalignment handling -- instead they get
// a nearly-useless drwtsn32.log and popup.
//
// SO, we check for this situation and act as if no debugger was
// enabled.
//
wDebugButton = 0;
AeAutoDebug = FALSE;
//
// If we are holding the PebLock, then the createprocess will fail
// because a new thread will also need this lock. Avoid this by peeking
// inside the PebLock and looking to see if we own it. If we do, then just allow
// a regular popup.
//
PebLockPointer = NtCurrentPeb()->FastPebLock;
if ( PebLockPointer->OwningThread != NtCurrentTeb()->ClientId.UniqueThread ) {
try {
if ( GetProfileString(
"AeDebug",
"Debugger",
NULL,
AeDebuggerCmdLine,
sizeof(AeDebuggerCmdLine)-1
) ) {
wDebugButton = SEB_CANCEL;
if ( GetProfileString(
"AeDebug",
"Auto",
"0",
AeAutoDebugString,
sizeof(AeAutoDebugString)-1
) ) {
if ( !WOW32_strcmp(AeAutoDebugString,"1") ) {
AeAutoDebug = TRUE;
}
}
}
} except (EXCEPTION_EXECUTE_HANDLER) {
wDebugButton = 0;
AeAutoDebug = FALSE;
}
}
//
// See comment above about drwtsn32
//
if (AeAutoDebug &&
!WOW32_strnicmp(AeDebuggerCmdLine, szDrWtsn32, (sizeof szDrWtsn32) - 1)) {
wDebugButton = 0;
AeAutoDebug = FALSE;
}
ptd = CURRENTPTD();
GETFRAMEPTR(ptd->vpStack, pFrame);
pTDB = (PVOID)SEGPTR(ptd->htask16,0);
//
// Get a zero-terminated copy of the Win16 task name.
//
RtlZeroMemory(szTask, sizeof(szTask));
RtlCopyMemory(szTask, pTDB->TDB_ModName, sizeof(szTask)-1);
//
// Translate exception address to module name in szModule.
//
len = strlen(CRITSTR(TheWin16Subsystem)) + 1;
len = min(len, sizeof(szModule));
strncpy(szModule, CRITSTR(TheWin16Subsystem), len);
szModule[len-1] = '\0';
RtlPcToFileHeader(pexi->ExceptionRecord->ExceptionAddress, (PVOID *)&hModule);
GetModuleFileName(hModule, szModule, sizeof(szModule));
pszModuleFilePart = WOW32_strrchr(szModule, '\\');
if (pszModuleFilePart) {
pszModuleFilePart++;
} else {
pszModuleFilePart = szModule;
}
//
// Format error message into szErrorMessage
//
switch (dwException) {
case EXCEPTION_ACCESS_VIOLATION:
pszErrorFormatString = CRITSTR(CausedAV);
break;
case EXCEPTION_STACK_OVERFLOW:
pszErrorFormatString = CRITSTR(CausedStackOverflow);
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
pszErrorFormatString = CRITSTR(CausedAlignmentFault);
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
case EXCEPTION_PRIV_INSTRUCTION:
pszErrorFormatString = CRITSTR(CausedIllegalInstr);
break;
case EXCEPTION_IN_PAGE_ERROR:
pszErrorFormatString = CRITSTR(CausedInPageError);
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
pszErrorFormatString = CRITSTR(CausedIntDivideZero);
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_STACK_CHECK:
case EXCEPTION_FLT_UNDERFLOW:
pszErrorFormatString = CRITSTR(CausedFloatException);
break;
default:
pszErrorFormatString = CRITSTR(CausedException);
}
_snprintf(szErrorMessage,
sizeof(szErrorMessage)-1,
pszErrorFormatString,
szTask,
pszModuleFilePart,
pexi->ExceptionRecord->ExceptionAddress,
dwException
);
szErrorMessage[sizeof(szErrorMessage)-1] = '\0';
LOGDEBUG(LOG_ALWAYS, ("W32Exception:\n%s\n",szErrorMessage));
//
// Format dialog text into szDialogText and display.
//
if (AeAutoDebug) {
dwButtonPushed = 2;
} else {
if (wDebugButton == SEB_CANCEL) {
_snprintf(szDialogText,
sizeof(szDialogText)-1,
"%s\n%s\n%s\n%s\n",
szErrorMessage,
CRITSTR(ChooseClose),
CRITSTR(ChooseCancel),
(dwException == EXCEPTION_DATATYPE_MISALIGNMENT)
? CRITSTR(ChooseIgnoreAlignment)
: CRITSTR(ChooseIgnore)
);
szDialogText[sizeof(szDialogText)-1] = '\0';
} else {
_snprintf(szDialogText,
sizeof(szDialogText)-1,
"%s\n%s\n%s\n",
szErrorMessage,
CRITSTR(ChooseClose),
(dwException == EXCEPTION_DATATYPE_MISALIGNMENT)
? CRITSTR(ChooseIgnoreAlignment)
: CRITSTR(ChooseIgnore)
);
szDialogText[sizeof(szDialogText)-1] = '\0';
}
dwButtonPushed = WOWSysErrorBox(
CRITSTR(ApplicationError),
szDialogText,
SEB_CLOSE,
wDebugButton,
SEB_IGNORE | SEB_DEFBUTTON
);
}
//
// If CANCEL is chosen Launch Debugger.
//
if (dwButtonPushed == 2) {
BOOL b;
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
CHAR CmdLine[256];
NTSTATUS ntStatus;
HANDLE EventHandle;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
EventHandle = CreateEvent(&sa,TRUE,FALSE,NULL);
RtlZeroMemory(&StartupInfo,sizeof(StartupInfo));
sprintf(CmdLine,AeDebuggerCmdLine,GetCurrentProcessId(),EventHandle);
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.lpDesktop = "Winsta0\\Default";
CsrIdentifyAlertableThread();
b = CreateProcess(
NULL,
CmdLine,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&StartupInfo,
&ProcessInformation
);
if ( b && EventHandle) {
//
// Do an alertable wait on the event
//
ntStatus = NtWaitForSingleObject(
EventHandle,
TRUE,
NULL
);
return EXCEPTION_CONTINUE_SEARCH;
} else {
LOGDEBUG(0, ("W32Exception unable to start debugger.\n"));
goto KillTask;
}
}
//
// If IGNORE is chosen and it's an EXCEPTION_DATATYPE_MISALIGNMENT,
// turn on software emulation of misaligned access and restart the
// faulting instruction. Otherwise, just fail the API and continue.
//
if (dwButtonPushed == 3) {
if (dwException == EXCEPTION_DATATYPE_MISALIGNMENT) {
SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT);
LOGDEBUG(0, ("W32Exception disabling alignment fault exceptions at user's request.\n"));
return EXCEPTION_CONTINUE_EXECUTION;
}
LOGDEBUG(0, ("W32Exception ignoring at user's request via EXCEPTION_EXECUTE_HANDLER\n"));
return EXCEPTION_EXECUTE_HANDLER;
}
//
// If user typed CLOSE or Any of the above fail,
// force just the task to die.
//
KillTask:
LOGDEBUG(0, ("W32Exception killing task via RET_FORCETASKEXIT\n"));
GETFRAMEPTR(ptd->vpStack, pFrame);
pFrame->wRetID = RET_FORCETASKEXIT;
return EXCEPTION_EXECUTE_HANDLER;
}
#ifdef DEBUG
VOID StartDebuggerForWow(VOID)
/*++
Routine Description:
This routine checks to see if there's a debugger attached to WOW. If not,
it attempts to spawn one with a command to attach to WOW. If the system
was booted with /DEBUG in boot.ini (kernel debugger enabled), we'll run
"ntsd -d" otherwise we'll run "ntsd".
Arguments:
None.
Return Value:
None.
--*/
{
BOOL fKernelDebuggerEnabled, b;
NTSTATUS Status;
SYSTEM_KERNEL_DEBUGGER_INFORMATION KernelDebuggerInformation;
ULONG ulReturnLength;
SECURITY_ATTRIBUTES sa;
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
CHAR szCmdLine[256];
HANDLE hEvent;
//
// Are we being run under a debugger ?
//
if (IsDebuggerAttached()) {
//
// No need to start one.
//
return;
}
//
// Is the kernel debugger enabled?
//
Status = NtQuerySystemInformation(
SystemKernelDebuggerInformation,
&KernelDebuggerInformation,
sizeof(KernelDebuggerInformation),
&ulReturnLength
);
if (NT_SUCCESS(Status) &&
(ulReturnLength >= sizeof(KernelDebuggerInformation))) {
fKernelDebuggerEnabled = KernelDebuggerInformation.KernelDebuggerEnabled;
} else {
fKernelDebuggerEnabled = FALSE;
LOGDEBUG(0,("StartDebuggerForWow: NtQuerySystemInformation(kdinfo) returns 0x%8.8x, return length 0x%08x.\n",
Status, ulReturnLength));
}
//
// Create an event for NTSD to signal once it has fully connected
// and is ready for the exception. We force the handle to be inherited.
//
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
hEvent = CreateEvent(&sa, TRUE, FALSE, NULL);
//
// Build debugger command line.
//
_snprintf(szCmdLine,
sizeof(szCmdLine)-1,
"ntsd %s -p %lu -e %lu -x -g -G",
fKernelDebuggerEnabled ? "-d" : "",
GetCurrentProcessId(),
hEvent
);
szCmdLine[sizeof(szCmdLine)-1] = '\0';
RtlZeroMemory(&StartupInfo,sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
b = CreateProcess(
NULL,
szCmdLine,
NULL,
NULL,
TRUE, // fInheritHandles
CREATE_DEFAULT_ERROR_MODE,
NULL,
NULL,
&StartupInfo,
&ProcessInformation
);
if (b) {
CloseHandle(ProcessInformation.hProcess);
CloseHandle(ProcessInformation.hThread);
if (hEvent) {
//
// Wait for debugger to initialize.
//
WaitForSingleObject(hEvent, INFINITE);
}
}
CloseHandle(hEvent);
return;
}
#endif // DEBUG
BOOL IsDebuggerAttached(VOID)
/*++
Routine Description:
Checks to see if there's a debugger attached to WOW. If there is,
this routine also turns on a bit in the 16-bit kernel's DS so it
can do its part to report debug events.
Arguments:
None.
Return Value:
FALSE - no debugger attached or NtQueryInformationProcess fails.
TRUE - debugger is definitely attached.
--*/
{
NTSTATUS Status;
HANDLE MyDebugPort;
LPBYTE lpDebugWOW;
static BOOL fDebuggerAttached = FALSE;
static BOOL fKernel16Notified = FALSE;
//
// Don't bother checking if we already have been told that
// there is a debugger attached, since debuggers cannot detach.
//
if (!fDebuggerAttached) {
//
// Query our ProcessDebugPort, if it is nonzero we have
// a debugger attached.
//
Status = NtQueryInformationProcess(
NtCurrentProcess(),
ProcessDebugPort,
(PVOID)&MyDebugPort,
sizeof(MyDebugPort),
NULL
);
fDebuggerAttached = NT_SUCCESS(Status) && (MyDebugPort != NULL);
}
//
// If we have a debugger attached share that information
// with the 16-bit kernel.
//
if (!fKernel16Notified && fDebuggerAttached && vpDebugWOW != 0) {
GETVDMPTR(vpDebugWOW, 1, lpDebugWOW);
*lpDebugWOW |= 1;
FREEVDMPTR(lpDebugWOW);
DBGNotifyDebugged( TRUE );
fKernel16Notified = TRUE;
}
return fDebuggerAttached;
}
void *
WOWGetVDMPointer(
VPVOID Address,
DWORD Count,
BOOL ProtectedMode
)
/*++
Routine Description:
This routine converts a 16/16 address to a linear address.
WARNING NOTE - This routine has been optimized so protect mode LDT lookup
falls stright through.
Arguments:
Address -- specifies the address in seg:offset format
Size -- specifies the size of the region to be accessed.
ProtectedMode -- true if the address is a protected mode address
Return Value:
The pointer.
--*/
{
if (ProtectedMode) {
return GetPModeVDMPointer(Address, Count);
} else {
return GetRModeVDMPointer(Address);
}
}
PVOID FASTCALL
GetPModeVDMPointerAssert(
DWORD Address
#ifdef DEBUG
, DWORD Count
#endif
)
/*++
Routine Description:
Convert a 16:16 protected mode address to the equivalent flat pointer.
Arguments:
Address -- specifies the address in selector:offset format
Return Value:
The pointer.
--*/
{
#ifdef DEBUG
void *vp;
#endif
// what to do if this assert fires?? Currently "nothing" seems to work OK.
WOW32WARNMSG((ExpLdt),("WOW::GetPModeVDMPointerAssert: ExpLdt == NULL\n"));
//
// Check to see if the descriptor is marked present
// We assume here that ExpLdt is DWORD ALIGNED to avoid a slower
// unaligned access on risc.
//
if (!((ExpLdt)[(Address >> 18) | 1] & LDT_DESC_PRESENT)) {
PARM16 Parm16;
ULONG ul;
if ((HIWORD(Address) & STD_SELECTOR_BITS) == STD_SELECTOR_BITS) {
// We've determined that the selector is valid and not
// present. So we call over to kernel16 to have it load
// the selector into a segment register. This forces a
// segment fault, and the segment should be brought in.
// Note that CallBack16 also calls this routine, so we could
// theoretically get into an infinite recursion loop here.
// This could only happen if selectors like the 16-bit stack
// were not present, which would mean we are hosed anyway.
// Such a loop should terminate with a stack fault eventually.
Parm16.WndProc.lParam = (LONG) Address;
CallBack16(RET_FORCESEGMENTFAULT, &Parm16, 0, &ul);
} else {
// We come here if the address can't be resolved. A null
// selector is special-cased to allow for a null 16:16
// pointer to be passed.
if (HIWORD(Address)) {
LOGDEBUG(LOG_ALWAYS,("WOW::GetVDMPointer: *** Invalid 16:16 address %04x:%04x\n",
HIWORD(Address), LOWORD(Address)));
// If we get here, then we are about to return a bogus
// flat pointer.
// I would prefer to eventually assert this, but it
// appears to be overactive for winfax lite.
//WOW32ASSERT(FALSE);
}
}
}
#ifdef DEBUG
if (vp = GetPModeVDMPointerMacro(Address, Count)) {
#ifdef _X86_
//
// Check the selector limit on x86 only and return NULL if
// the limit is too small.
//
if (SelectorLimit &&
(Address & 0xFFFF) + Count > SelectorLimit[Address >> 19] + 1)
{
WOW32ASSERTMSGF (FALSE, ("WOW32 limit check assertion: %04x:%04x size %x is beyond limit %x.\n",
Address >> 16,
Address & 0xFFFF,
Count,
SelectorLimit[Address >> 19]
));
return vp;
}
#endif
#if 0 // this code is a paranoid check, only useful when debugging GetPModeVDMPointer.
if (vp != Sim32GetVDMPointer(Address, Count, TRUE)) {
LOGDEBUG(LOG_ALWAYS,
("GetPModeVDMPointer: GetPModeVDMPointerMacro(%x) returns %x, Sim32 returns %x!\n",
Address, vp, Sim32GetVDMPointer(Address, Count, TRUE)));
vp = Sim32GetVDMPointer(Address, Count, TRUE);
}
#endif
return vp;
} else {
return NULL;
}
#else
return GetPModeVDMPointerMacro(Address, 0); // No limit check on free build.
#endif // DEBUG
}
ULONG FASTCALL WK32WOWGetFastAddress( PVDMFRAME pFrame )
{
return 0;
}
ULONG FASTCALL WK32WOWGetFastCbRetAddress( PVDMFRAME pFrame )
{
return( 0L );
}
ULONG FASTCALL WK32WOWGetTableOffsets( PVDMFRAME pFrame )
{
PWOWGETTABLEOFFSETS16 parg16;
PTABLEOFFSETS pto16;
GETARGPTR(pFrame, sizeof(PDWORD16), parg16);
GETVDMPTR(parg16->vpThunkTableOffsets, sizeof(TABLEOFFSETS), pto16);
RtlCopyMemory(pto16, &tableoffsets, sizeof(TABLEOFFSETS));
FLUSHVDMPTR(parg16->vpThunkTableOffsets, sizeof(TABLEOFFSETS), pto16);
FREEVDMPTR(pto16);
FREEARGPTR(parg16);
return 1;
}
ULONG FASTCALL WK32WOWGetFlatAddressArray( PVDMFRAME pFrame )
{
return (ULONG)FlatAddress;
}
#ifdef DEBUG
/*
* DoAssert - do an assertion. called after the expression has been evaluted
*
* Input:
*
*
* Note if the requested log level is not what we want we don't output
* but we always output to the circular buffer - just in case.
*
*
*/
int DoAssert(PSZ szAssert, PSZ szModule, UINT line, UINT loglevel)
{
INT savefloptions;
//
// Start a debugger for WOW if there isn't already one.
//
// Until now StartDebuggerForWow was started by
// the exception filter, which meant asserts on a
// checked build got the debugger but the user didn't see
// the assertion text on the debugger screen because
// logprintf was called before the debugger attached.
// -- DaveHart 31-Jan-95
//
StartDebuggerForWow();
savefloptions = flOptions;
flOptions |= OPT_DEBUG; // *always* print the message
//
// szAssert is NULL for bare-bones WOW32ASSERT()
//
if (szAssert == NULL) {
LOGDEBUG(loglevel, ("WOW32 assertion failure: %s line %d\n", szModule, line));
} else {
LOGDEBUG(loglevel, ("%s", szAssert));
}
flOptions = savefloptions;
if (IsDebuggerAttached()) {
DbgBreakPoint();
} else {
DWORD dw = SetErrorMode(0);
RaiseException(EXCEPTION_WOW32_ASSERTION, 0, 0, NULL);
SetErrorMode(dw);
}
return 0;
}
/*
* sprintf_gszAssert
*
* Used by WOW32ASSERTMSGF to format the assertion text into
* a global buffer, gszAssert. There is probably a better way.
*
* DaveHart 15-Jun-95.
*
*/
int _cdecl sprintf_gszAssert(PSZ pszFmt, ...)
{
va_list VarArgs;
va_start(VarArgs, pszFmt);
return vsprintf(gszAssert, pszFmt, VarArgs);
}
/*
* logprintf - format log print routine
*
* Input:
* iReqLogLevel - Requested Logging Level
*
* Note if the requested log level is not what we want we don't output
* but we always output to the circular buffer - just in case.
*
*
*/
VOID logprintf(PSZ pszFmt, ...)
{
DWORD lpBytesWritten;
int len;
char text[1024];
va_list arglist;
va_start(arglist, pszFmt);
len = vsprintf(text, pszFmt, arglist);
// fLog states (set by !wow32.logfile debugger extension):
// 0 -> no logging;
// 1 -> log to file
// 2 -> create log file
// 3 -> close log file
if(fLog > 1) {
if(fLog == 2) {
if((hfLog = CreateFile(szLogFile,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL)) != INVALID_HANDLE_VALUE) {
fLog = 1;
}
else {
hfLog = NULL;
fLog = 0;
OutputDebugString("Couldn't open log file!\n");
}
}
else {
FlushFileBuffers(hfLog);
CloseHandle(hfLog);
hfLog = NULL;
fLog = 0;
}
}
if ( len > TMP_LINE_LEN-1 ) {
text[TMP_LINE_LEN-2] = '\n';
text[TMP_LINE_LEN-1] = '\0'; /* Truncate to 128 */
}
IFLOG(iReqLogLevel) {
// write to file?
if (fLog) {
WriteFile(hfLog, text, len, &lpBytesWritten, NULL);
}
// write to terminal?
else if (flOptions & OPT_DEBUG) {
OutputDebugString(text);
}
}
// This strcpy is overflow safe because of the explicit NULL placed in the
// src string: text[TMP_LINE_LEN-1] = '\0'; above.
strcpy(&achTmp[iCircBuffer][0], text);
if (--iCircBuffer < 0 ) {
iCircBuffer = CIRC_BUFFERS-1;
}
}
/*
* checkloging - Some Functions we don't want to log
*
* Entry
* fLogFilter = Filter for Specific Modules - Kernel, User, GDI etc.
* fLogTaskFilter = Filter for specific TaskID
*
* Exit: TRUE - OK to LOG Event
* FALSE - Don't Log Event
*
*/
BOOL checkloging(register PVDMFRAME pFrame)
{
INT i;
BOOL bReturn;
INT iFun = GetFuncId(pFrame->wCallID);
PTABLEOFFSETS pto = &tableoffsets;
// Filter on Specific Call IDs
if (awfLogFunctionFilter[0] != 0xffff) {
INT nOrdinal;
nOrdinal = GetOrdinal(iFun);
bReturn = FALSE;
for (i=0; i < FILTER_FUNCTION_MAX ; i++) {
if (awfLogFunctionFilter[i] == nOrdinal) {
bReturn = TRUE;
break;
}
}
} else {
bReturn = TRUE;
}
// Do not LOG Internal Kernel Calls below level 20
if (iLogLevel < 20 ) {
if((iFun == FUN_WOWOUTPUTDEBUGSTRING) ||
((iFun < pto->user) && (iFun >= FUN_WOWINITTASK)))
bReturn = FALSE;
}
// LOG Only Specific TaskID
if (fLogTaskFilter != 0xffff) {
if (fLogTaskFilter != pFrame->wTDB) {
bReturn = FALSE;
}
}
// LOG Filter On Modules USER/GDI/Kernel etc.
switch (ModFromCallID(iFun)) {
case MOD_KERNEL:
if ((fLogFilter & FILTER_KERNEL) == 0 )
bReturn = FALSE;
break;
case MOD_USER:
if ((fLogFilter & FILTER_USER) == 0 )
bReturn = FALSE;
break;
case MOD_GDI:
if ((fLogFilter & FILTER_GDI) == 0 )
bReturn = FALSE;
break;
case MOD_KEYBOARD:
if ((fLogFilter & FILTER_KEYBOARD) == 0 )
bReturn = FALSE;
break;
case MOD_SOUND:
if ((fLogFilter & FILTER_SOUND) == 0 )
bReturn = FALSE;
break;
case MOD_MMEDIA:
if ((fLogFilter & FILTER_MMEDIA) == 0 )
bReturn = FALSE;
break;
case MOD_WINSOCK:
if ((fLogFilter & FILTER_WINSOCK) == 0 )
bReturn = FALSE;
break;
case MOD_COMMDLG:
if ((fLogFilter & FILTER_COMMDLG) == 0 ) {
bReturn = FALSE;
}
break;
#ifdef FE_IME
case MOD_WINNLS:
if ((fLogFilter & FILTER_WINNLS) == 0 )
bReturn = FALSE;
break;
#endif // FE_IME
#ifdef FE_SB
case MOD_WIFEMAN:
if ((fLogFilter & FILTER_WIFEMAN) == 0 )
bReturn = FALSE;
break;
#endif
default:
break;
}
return (bReturn);
}
/*
* Argument Logging For Tracing API Calls
*
*
*/
VOID logargs(INT iLog, register PVDMFRAME pFrame)
{
register PBYTE pbArgs;
INT iFun;
INT cbArgs;
if (checkloging(pFrame)) {
iFun = GetFuncId(pFrame->wCallID);
cbArgs = aw32WOW[iFun].cbArgs; // Get Number of Parameters
if ((fLogFilter & FILTER_VERBOSE) == 0 ) {
LOGDEBUG(iLog,("%s(", aw32WOW[iFun].lpszW32));
} else {
LOGDEBUG(iLog,("%04X %08X %04X %s:%s(",pFrame->wTDB, pFrame->vpCSIP,pFrame->wAppDS, GetModName(iFun), aw32WOW[iFun].lpszW32));
}
GETARGPTR(pFrame, cbArgs, pbArgs);
pbArgs += cbArgs;
//
// Log the function arguments a word at a time.
// The first iteration of the while loop is unrolled so
// that the main loop doesn't have to figure out whether
// or not to print a comma.
//
if (cbArgs > 0) {
pbArgs -= sizeof(WORD);
cbArgs -= sizeof(WORD);
LOGDEBUG(iLog,("%04x", *(PWORD16)pbArgs));
while (cbArgs > 0) {
pbArgs -= sizeof(WORD);
cbArgs -= sizeof(WORD);
LOGDEBUG(iLog,(",%04x", *(PWORD16)pbArgs));
}
}
FREEARGPTR(pbArgs);
LOGDEBUG(iLog,(")\n"));
if (fDebugWait != 0) {
DbgPrint("WOWSingle Step\n");
DbgBreakPoint();
}
}
}
/*
* logreturn - Log Return Values From Call
*
* Entry
*
* Exit - None
*/
VOID logreturn(INT iLog, register PVDMFRAME pFrame, ULONG ulReturn)
{
INT iFun;
if (checkloging(pFrame)) {
iFun = GetFuncId(pFrame->wCallID);
if ((fLogFilter & FILTER_VERBOSE) == 0 ) {
LOGDEBUG(iLog,("%s: %lx\n", aw32WOW[iFun].lpszW32, ulReturn));
} else {
LOGDEBUG(iLog,("%04X %08X %04X %s:%s: %lx\n", pFrame->wTDB, pFrame->vpCSIP, pFrame->wAppDS, GetModName(iFun), aw32WOW[iFun].lpszW32, ulReturn));
}
}
}
#endif // DEBUG
PVOID FASTCALL malloc_w (ULONG size)
{
PVOID pv;
pv = HeapAlloc(hWOWHeap, 0, size + TAILCHECK);
WOW32ASSERTMSG(pv, "WOW32: malloc_w failing, returning NULL\n");
#ifdef DEBUG_MEMLEAK
WOW32DebugMemLeak(pv, size, ML_MALLOC_W);
#endif
return pv;
}
DWORD FASTCALL size_w (PVOID pv)
{
DWORD dwSize;
dwSize = HeapSize(hWOWHeap, 0, pv) - TAILCHECK;
return(dwSize);
}
PVOID FASTCALL malloc_w_zero (ULONG size)
{
PVOID pv;
pv = HeapAlloc(hWOWHeap, HEAP_ZERO_MEMORY, size + TAILCHECK);
WOW32ASSERTMSG(pv, "WOW32: malloc_w_zero failing, returning NULL\n");
#ifdef DEBUG_MEMLEAK
WOW32DebugMemLeak(pv, size, ML_MALLOC_W_ZERO);
#endif
return pv;
}
VOID FASTCALL free_w (PVOID p)
{
#ifdef DEBUG_MEMLEAK
WOW32DebugFreeMem(p);
#endif
if(p) {
HeapFree(hWOWHeap, 0, (LPSTR)(p));
}
}
//
// malloc_w_or_die is for use by *initialization* code only, when we
// can't get WOW going because, for example, we can't allocate a buffer
// to hold the known DLL list.
//
// malloc_w_or_die should not be used by API or message thunks or worker
// routines called by API or message thunks.
//
PVOID FASTCALL malloc_w_or_die(ULONG size)
{
PVOID pv;
if (!(pv = malloc_w(size))) {
WOW32ASSERTMSG(pv, "WOW32: malloc_w_or_die failing, terminating.\n");
WOWStartupFailed(); // never returns.
}
return pv;
}
LPSTR malloc_w_strcpy_vp16to32(VPVOID vpstr16, BOOL bMulti, INT cMax)
{
return(ThunkStr16toStr32(NULL, vpstr16, cMax, bMulti));
}
LPSTR ThunkStr16toStr32(LPSTR pdst32, VPVOID vpsrc16, INT cChars, BOOL bMulti)
/*++
Thunks a 16-bit string to a 32-bit ANSI string.
bMulti == TRUE means we are thunking a multi-string which is a list of NULL
*separated* strings that *terminate* with a double NULL.
Notes: If the original 32-bit buffer is too small to contain the new string,
it will be free'd and a new 32-bit buffer will be allocated. If a new
32-bit buffer can't be allocated, the ptr to the original 32-bit
buffer is returned with no changes to the contents.
Returns: ptr to the original 32-bit buffer
OR ptr to a new 32-bit buffer if the original buffer was too small
OR NULL if psrc is NULL.
--*/
{
PBYTE pbuf32;
LPSTR psrc16;
INT buf16size, iLen;
INT buf32size = 0;
GETPSZPTR(vpsrc16, psrc16);
if(!psrc16) {
// the app doesn't want a buffer for this anymore
// (this is primarily for comdlg support)
if(pdst32) {
free_w(pdst32);
}
return(NULL);
}
if(bMulti) {
iLen = Multi_strlen(psrc16) + 1;
} else {
iLen = (INT)(strlen(psrc16) + 1);
}
buf16size = max(cChars, iLen);
if(pdst32) {
buf32size = (INT)size_w(pdst32);
}
// if 32-bit buffer is too small, NULL, or invalid -- alloc a bigger buffer
if((buf32size < buf16size) || (!pdst32) || (buf32size == 0xFFFFFFFF)) {
if(pbuf32 = (PBYTE)malloc_w(buf16size)) {
// now copy to the new 32-bit buffer
if(bMulti) {
Multi_strcpy(pbuf32, psrc16);
} else {
strncpy((PBYTE)pbuf32, psrc16, buf16size);
pbuf32[buf16size-1] = '\0';
}
// get rid of the old buffer
if(pdst32) {
free_w(pdst32);
}
pdst32 = pbuf32;
}
else {
WOW32ASSERTMSG(0, "WOW32: ThunkStr16toStr32: malloc_w failed!\n");
}
}
// else just use the original 32-bit buffer (99% of the time)
else if(pdst32) {
if(bMulti) {
Multi_strcpy(pdst32, psrc16);
} else {
strncpy(pdst32, psrc16, buf32size);
pdst32[buf32size-1] = '\0';
}
}
FREEPSZPTR(psrc16);
return(pdst32);
}
//
// WOWStartupFailed puts up a fatal error box and terminates WOW.
//
PVOID WOWStartupFailed(VOID)
{
char szCaption[256];
char szMsgBoxText[1024];
LoadString(hmodWOW32, iszStartupFailed, szMsgBoxText, sizeof szMsgBoxText);
LoadString(hmodWOW32, iszSystemError, szCaption, sizeof szCaption);
MessageBox(GetDesktopWindow(),
szMsgBoxText,
szCaption,
MB_SETFOREGROUND | MB_TASKMODAL | MB_ICONSTOP | MB_OK | MB_DEFBUTTON1);
ExitVDM(WOWVDM, ALL_TASKS); // Tell Win32 All Tasks are gone.
ExitProcess(EXIT_FAILURE);
return (PVOID)NULL;
}
#ifdef FIX_318197_NOW
char*
WOW32_strchr(
const char* psz,
int c
)
{
if (gbDBCSEnable) {
unsigned int cc;
for (; (cc = *psz); psz++) {
if (IsDBCSLeadByte((BYTE)cc)) {
if (*++psz == '\0') {
return NULL;
}
if ((unsigned int)c == ((cc << 8) | *psz) ) { // DBCS match
return (char*)(psz - 1);
}
}
else if ((unsigned int)c == cc) {
return (char*)psz; // SBCS match
}
}
if ((unsigned int)c == cc) { // NULL match
return (char*)psz;
}
return NULL;
}
else {
return strchr(psz, c);
}
}
char*
WOW32_strrchr(
const char* psz,
int c
)
{
if (gbDBCSEnable) {
char* r = NULL;
unsigned int cc;
do {
cc = *psz;
if (IsDBCSLeadByte((BYTE)cc)) {
if (*++psz) {
if ((unsigned int)c == ((cc << 8) | *psz) ) { // DBCS match
r = (char*)(psz - 1);
}
}
else if (!r) {
// return pointer to '\0'
r = (char*)psz;
}
}
else if ((unsigned int)c == cc) {
r = (char*)psz; // SBCS match
}
} while (*psz++);
return r;
}
else {
return strrchr(psz, c);
}
}
char*
WOW32_strstr(
const char* str1,
const char* str2
)
{
if (gbDBCSEnable) {
char *cp, *endp;
char *s1, *s2;
cp = (char*)str1;
endp = (char*)str1 + strlen(str1) - strlen(str2);
while (*cp && (cp <= endp)) {
s1 = cp;
s2 = (char*)str2;
while ( *s1 && *s2 && (*s1 == *s2) ) {
s1++;
s2++;
}
if (!(*s2)) {
return cp; // success!
}
cp = CharNext(cp);
}
return NULL;
}
else {
return strstr(str1, str2);
}
}
int
WOW32_strncmp(
const char* str1,
const char* str2,
size_t n
)
{
if (gbDBCSEnable) {
int retval;
if (n == 0) {
return 0;
}
retval = CompareStringA( GetThreadLocale(),
LOCALE_USE_CP_ACP,
str1,
n,
str2,
n );
if (retval == 0) {
//
// The caller is not expecting failure. Try the system
// default locale id.
//
retval = CompareStringA( GetSystemDefaultLCID(),
LOCALE_USE_CP_ACP,
str1,
n,
str2,
n );
}
if (retval == 0) {
if (str1 && str2) {
//
// The caller is not expecting failure. We've never had a
// failure indicator before. We'll do a best guess by calling
// the C runtimes to do a non-locale sensitive compare.
//
return strncmp(str1, str2, n);
}
else if (str1) {
return 1;
}
else if (str2) {
return -1;
}
else {
return 0;
}
}
return retval - 2;
}
else {
return strncmp(str1, str2, n);
}
}
int
WOW32_strnicmp(
const char* str1,
const char* str2,
size_t n
)
{
if (gbDBCSEnable) {
int retval;
if (n == 0) {
return 0;
}
retval = CompareStringA( GetThreadLocale(),
LOCALE_USE_CP_ACP | NORM_IGNORECASE,
str1,
n,
str2,
n );
if (retval == 0) {
//
// The caller is not expecting failure. Try the system
// default locale id.
//
retval = CompareStringA( GetSystemDefaultLCID(),
LOCALE_USE_CP_ACP | NORM_IGNORECASE,
str1,
n,
str2,
n );
}
if (retval == 0) {
if (str1 && str2) {
//
// The caller is not expecting failure. We've never had a
// failure indicator before. We'll do a best guess by calling
// the C runtimes to do a non-locale sensitive compare.
//
return _strnicmp(str1, str2, n);
}
else if (str1) {
return 1;
}
else if (str2) {
return -1;
}
else {
return 0;
}
}
return retval - 2;
}
else {
return _strnicmp(str1, str2, n);
}
}
#endif
//****************************************************************************
#ifdef DEBUG_OR_WOWPROFILE
LONGLONG GetWOWTicDiff(LONGLONG dwPrevCount) {
/*
* Returns difference between a previous Tick count & the current tick count
*
* NOTE: Tick counts are in unspecified units (PerfFreq is in Hz)
*/
LONGLONG dwDiff;
LARGE_INTEGER PerfCount, PerfFreq;
NtQueryPerformanceCounter(&PerfCount, &PerfFreq);
dwDiff = PerfCount.QuadPart - dwPrevCount;
return(dwDiff);
}
INT GetFuncId(DWORD iFun)
{
INT i;
static DWORD dwLastInput = -1;
static DWORD dwLastOutput = -1;
if (iFun == dwLastInput) {
iFun = dwLastOutput;
} else {
dwLastInput = iFun;
if (!ISFUNCID(iFun)) {
for (i = 0; i < cAPIThunks; i++) {
if (aw32WOW[i].lpfnW32 == (LPFNW32)iFun) {
iFun = i;
break;
}
}
}
dwLastOutput = iFun;
}
return iFun;
}
#endif // DEBUG_OR_WOWPROFILE
// for debugging memory leaks
#ifdef DEBUG_MEMLEAK
LPMEMLEAK lpMemLeakStart = NULL;
ULONG ulalloc_Count = 1L;
DWORD dwAllocFlags = 0;
VOID WOW32DebugMemLeak(PVOID lp, ULONG size, DWORD fHow)
{
PVOID pvCallersAddress, pvCallersCaller;
LPMEMLEAK lpml;
HGLOBAL h32 = NULL; // lp from ML_GLOBALTYPE's are really HGLOBAL's
if(lp) {
// if we are tracking this type
if(dwAllocFlags & fHow) {
// allocate a tracking node
if(lpml = GlobalAlloc(GPTR, sizeof(MEMLEAK))) {
lpml->lp = lp;
lpml->size = size;
lpml->fHow = fHow;
lpml->Count = ulalloc_Count++; // save when originally alloc'd
RtlGetCallersAddress(&pvCallersAddress, &pvCallersCaller);
lpml->CallersAddress = pvCallersCaller;
EnterCriticalSection(&csMemLeak);
lpml->lpmlNext = lpMemLeakStart;
lpMemLeakStart = lpml;
LeaveCriticalSection(&csMemLeak);
}
WOW32WARNMSG(lpml,"WOW32DebugMemLeak: can't alloc node\n");
}
// add "EnD" signature for heap tail corruption checking
if(fHow & ML_GLOBALTYPE) {
h32 = (HGLOBAL)lp;
lp = GlobalLock(h32);
}
if(lp) {
((CHAR *)(lp))[size++] = 'E';
((CHAR *)(lp))[size++] = 'n';
((CHAR *)(lp))[size++] = 'D';
((CHAR *)(lp))[size++] = '\0';
if(h32) {
GlobalUnlock(h32);
}
}
}
}
VOID WOW32DebugReMemLeak(PVOID lpNew, PVOID lpOrig, ULONG size, DWORD fHow)
{
PVOID pvCallersAddress, pvCallersCaller;
HGLOBAL h32 = NULL; // lp from ML_GLOBALTYPE's are really HGLOBAL's
LPMEMLEAK lpml = lpMemLeakStart;
if(lpNew) {
if(dwAllocFlags & fHow) {
// look for original ptr in the list
while(lpml) {
if(lpml->lp == lpOrig) {
break;
}
lpml = lpml->lpmlNext;
}
WOW32WARNMSG(lpml,
"WOW32DebugReMemLeak: can't find original node\n");
// if we found the original ptr
if(lpml) {
// update our struct with new ptr if necessary
if(lpNew != lpOrig) {
lpml->lp = lpNew;
}
lpml->size = size;
lpml->fHow |= fHow;
RtlGetCallersAddress(&pvCallersAddress, &pvCallersCaller);
lpml->CallersAddress = pvCallersCaller;
ulalloc_Count++;
}
}
// for heap tail corruption checking
if(fHow & ML_GLOBALTYPE) {
h32 = (HGLOBAL)lpNew;
lpNew = GlobalLock(h32);
}
if(lpNew) {
((CHAR *)(lpNew))[size++] = 'E';
((CHAR *)(lpNew))[size++] = 'n';
((CHAR *)(lpNew))[size++] = 'D';
((CHAR *)(lpNew))[size++] = '\0';
if(h32) {
GlobalUnlock(h32);
}
}
}
}
VOID WOW32DebugFreeMem(PVOID lp)
{
LPMEMLEAK lpmlPrev;
LPMEMLEAK lpml = lpMemLeakStart;
if(lp && dwAllocFlags) {
while(lpml) {
lpmlPrev = lpml;
if(lpml->lp == lp) {
WOW32DebugCorruptionCheck(lp, lpml->size);
EnterCriticalSection(&csMemLeak);
if(lpml == lpMemLeakStart) {
lpMemLeakStart = lpml->lpmlNext;
}
else {
lpmlPrev->lpmlNext = lpml->lpmlNext;
}
GlobalFree(lpml); // free the LPMEMLEAK node
LeaveCriticalSection(&csMemLeak);
break;
}
else {
lpml = lpml->lpmlNext;
}
}
WOW32WARNMSG((lpml), "WOW32DebugFreeMem: can't find node\n");
}
}
VOID WOW32DebugCorruptionCheck(PVOID lp, DWORD size)
{
if(lp && size) {
if(!((((CHAR *)(lp))[size++] == 'E') &&
(((CHAR *)(lp))[size++] == 'n') &&
(((CHAR *)(lp))[size++] == 'D') &&
(((CHAR *)(lp))[size++] == '\0')) ) {
WOW32ASSERTMSG(FALSE,"WOW32DebugCorruptionCheck: Corrupt tail!!\n");
}
}
}
DWORD WOW32DebugGetMemSize(PVOID lp)
{
LPMEMLEAK lpml = lpMemLeakStart;
while(lpml) {
if(lpml->lp == lp) {
return(lpml->size);
}
lpml = lpml->lpmlNext;
}
return(0);
}
// NOTE: this is called ONLY IF built with DEBUG_MEMLEAK
HGLOBAL WOW32DebugGlobalAlloc(UINT flags, DWORD dwSize)
{
HGLOBAL h32;
h32 = GlobalAlloc(flags, dwSize + TAILCHECK);
WOW32DebugMemLeak((PVOID)h32, dwSize, ML_GLOBALALLOC);
return(h32);
}
// NOTE: this is called ONLY IF built with DEBUG_MEMLEAK
HGLOBAL WOW32DebugGlobalReAlloc(HGLOBAL h32, DWORD dwSize, UINT flags)
{
HGLOBAL h32New;
PVOID lp32Orig;
// get the original pointer & check the memory for tail corruption
lp32Orig = (PVOID)GlobalLock(h32);
WOW32DebugCorruptionCheck(lp32Orig, WOW32DebugGetMemSize((PVOID)h32));
GlobalUnlock(h32);
h32New = GlobalReAlloc(h32, dwSize + TAILCHECK, flags);
// fix our memory list to account for the realloc
WOW32DebugReMemLeak((PVOID)h32New,
(PVOID)h32,
dwSize + TAILCHECK,
ML_GLOBALREALLOC);
return(h32New);
}
// NOTE: this is called ONLY IF built with DEBUG_MEMLEAK
HGLOBAL WOW32DebugGlobalFree(HGLOBAL h32)
{
WOW32DebugFreeMem((PVOID)h32);
h32 = GlobalFree(h32);
if(h32) {
LOGDEBUG(0, ("WOW32DebugFreeMem: Lock count not 0!\n"));
}
else {
if(GetLastError() != NO_ERROR) {
LOGDEBUG(0, ("WOW32DebugFreeMem: GlobalFree failed!\n"));
}
}
return(h32);
}
#endif // DEBUG_MEMLEAK