/*++
 *
 *  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>
#define SHAREWOW_MAIN
#include <sharewow.h>

#include <tsappcmp.h>


/* Function Prototypes */
DWORD   W32SysErrorBoxThread2(PTDB pTDB);
VOID    StartDebuggerForWow(VOID);
BOOLEAN LoadCriticalStringResources(void);

extern DECLSPEC_IMPORT ULONG *ExpLdt;
#define LDT_DESC_PRESENT 0x8000
#define STD_SELECTOR_BITS 0x7

MODNAME(wow32.c);

#define REGISTRY_BUFFER_SIZE 512

// 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;              // # 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 hSharedTaskMemory;
DWORD  dwSharedProcessOffset;
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;

#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;
PSTR pszWindowsDirectory;
PSTR pszSystemDirectory;
BOOL gbDBCSEnable = FALSE;
#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 BOOL GdiReserveHandles(VOID);
extern CRITICAL_SECTION VdmLoadCritSec;
extern LIST_ENTRY TimerList;

extern PVOID GdiQueryTable();
extern PVOID gpGdiHandleInfo;

#if defined (_X86_)

extern PVOID WowpLockPrefixTable;

IMAGE_LOAD_CONFIG_DIRECTORY _load_config_used = {
    0,                           // Reserved
    0,                           // Reserved
    0,                           // Reserved
    0,                           // Reserved
    0,                           // GlobalFlagsClear
    0,                           // GlobalFlagsSet
    0,                           // CriticalSectionTimeout (milliseconds)
    0,                           // DeCommitFreeBlockThreshold
    0,                           // DeCommitTotalFreeThreshold
    (LONG) &WowpLockPrefixTable, // LockPrefixTable, defined in FASTWOW.ASM
    0, 0, 0, 0, 0, 0, 0          // Reserved
};

#endif

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

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;

        // 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;
        }

        //
        // setup the GDI table for handle conversion
        //

        gpGdiHandleInfo = GdiQueryTable();

        W32EWExecer();

        InitializeListHead(&TimerList);

        if (IsTerminalServer()) {
            //
            // Load tsappcmp.dll
            //
            HANDLE dllHandle = LoadLibrary (TEXT("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");
        }
        
        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);
        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))) {
                strcpy(lpData, abT);
            }

            break;

        default:
            WOW32ASSERT(FALSE);
            break;
    }
    return !!bRet;
}


/* W32Init - Initialize WOW support
 *
 * ENTRY
 *
 * EXIT
 *  TRUE if successful, FALSE if not
 */


BOOL W32Init(BOOL fMEoW)
{
    HKEY  WowKey;
#ifdef DEBUG
    CHAR WOWCmdLine[REGISTRY_BUFFER_SIZE];
    PCHAR pWOWCmdLine;
    ULONG WOWCmdLineSize = REGISTRY_BUFFER_SIZE;
#endif
    DWORD cb;
    DWORD dwType;
    PTD ptd;
    PFNWOWHANDLERSIN pfnIn;
    LPVOID lpSharedTaskMemory;
    LANGID LangID;

    UNREFERENCED_PARAMETER(fMEoW);
#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

    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;
    }

    //
    // Set up a global WindowsDirectory to be used by other WOW functions.
    //

    {
        char szBuf[ MAX_PATH ];
        int cb;

        GetSystemDirectory(szBuf, sizeof szBuf);
        GetShortPathName(szBuf, szBuf, sizeof szBuf);
        cb = strlen(szBuf) + 1;
        pszSystemDirectory = malloc_w_or_die(cb);
        RtlCopyMemory(pszSystemDirectory, szBuf, cb);

        if(!GetWindowsDirectory(szBuf, sizeof szBuf) ) {
           WOW32ASSERTMSG(FALSE, "WOW32: couldnt get windows directory, terminating.\n");
           WOWStartupFailed();  // never returns.
        }
        GetShortPathName(szBuf, szBuf, sizeof szBuf);
        cb = strlen(szBuf) + 1;
        pszWindowsDirectory = malloc_w_or_die(cb);
        RtlCopyMemory(pszWindowsDirectory, szBuf, cb);

        pszWinIniFullPath = malloc_w_or_die(cb + 8);   // "\win.ini"
        RtlCopyMemory(pszWinIniFullPath, szBuf, cb);
        pszWinIniFullPath[ cb - 1 ] = '\\';
        RtlCopyMemory(pszWinIniFullPath + cb, szWinDotIni, 8);
    }

    // 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);

    // Prepare us to be in the shared memory process list

    lpSharedTaskMemory = LOCKSHAREWOW();

    WOW32ASSERTMSG(lpSharedTaskMemory, "WOW32: Could not access shared memory object\n");

    if ( lpSharedTaskMemory ) {
        UNLOCKSHAREWOW();
    }

    CleanseSharedList();
    AddProcessSharedList();

    // 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

    }

#ifdef DEBUG

    if (RegQueryValueEx (WowKey,
             "wowcmdline",
             NULL,
             &dwType,
             (LPBYTE)&WOWCmdLine,
             &WOWCmdLineSize) != 0){
        RegCloseKey (WowKey);
        LOGDEBUG(0,("    W32INIT ERROR: WOWCMDLINE not found in registry\n"));
        return FALSE;
    }

    pWOWCmdLine = (PCHAR)((PBYTE)WOWCmdLine + WOWCmdLineSize + 1);

    WOWCmdLineSize = REGISTRY_BUFFER_SIZE - WOWCmdLineSize -1;

    if (WOWCmdLineSize < (REGISTRY_BUFFER_SIZE  / 2)){
        LOGDEBUG(0,("    W32INIT ERROR: Registry Buffer too small\n"));
        return FALSE;
    }

    if (ExpandEnvironmentStrings ((LPCSTR)WOWCmdLine, (LPSTR)pWOWCmdLine, WOWCmdLineSize) >
            WOWCmdLineSize) {
        LOGDEBUG(0,("    W32INIT ERROR: Registry Buffer too small\n"));
        return FALSE;
    }

    // Find Debug Info
    while (*pWOWCmdLine) {
        if (*pWOWCmdLine == '-' || *pWOWCmdLine == '/') {
         //   c = (char)tolower(*++pWOWCmdLine);
            switch(*++pWOWCmdLine) {
            case 'd':
            case 'D':
                flOptions |= OPT_DEBUG;
                break;
            case 'n':
            case 'N':
                flOptions |= OPT_BREAKONNEWTASK;
                break;
            case 'l':
            case 'L':
                iLogLevel = atoi(++pWOWCmdLine);
                break;
            default:
                break;
            }

        }
        pWOWCmdLine++;
    }

    if (iLogLevel > 0) {
        if (!(flOptions & OPT_DEBUG))
            if (!(OPENLOG()))
                iLogLevel = 0;
    }
    else
        iLogLevel = 0;

#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();

#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
 *
 *  see fastwow.h for instructions on how to create a new thunk 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. There are several User API's that are thunked on the 16-bit side by
 *     User32.dll code on x86 platforms.
 *  3. A FEW (only MulDiv?) GDI API's are thunked by GDI.exe in 16-bit land.
 *  4. On CHECKED x86 builds & ALL RISC builds, all API's not subject to the
 *     above exceptions are dispatched through this function.
 *  5. On x86 FREE platforms, API calls are dispatched from fastwow.asm
 *  - 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
 DWORD  dwTics;
#endif

    //
    // WARNING: DO NOT ADD ANYTHING TO THIS FUNCTION UNLESS YOU ADD THE SAME
    // STUFF TO i386/FastWOW.asm.   i386/FastWOW.ASM is used for speedy
    // thunk dispatching on retail builds.
    //

    try {

        //
        // if we get here then even if we're going to be fastbopping
        // then the faststack stuff must not be enabled yet.  that's why
        // there's no #if FASTBOPPING for this fetching of the vdmstack
        //

        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(0L);
#endif // WOWPROFILE
                ulReturn = InterpretThunk(pFrame, iFun);
                goto AfterApiCall;
            } else {
                W32PatchCodeWithLpfnw32(pFrame, (LPFNW32)iFun);
            }
        }


#ifdef WOWPROFILE // For API profiling only (debugger extension)
        dwTics = GetWOWTicDiff(0L);
#endif // WOWPROFILE

        //
        // WARNING: DO NOT ADD ANYTHING TO THIS FUNCTION UNLESS YOU ADD THE SAME
        // STUFF TO i386/FastWOW.asm.   i386/FastWOW.ASM is used for speedy
        // thunk dispatching on retail builds.
        //

        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())) {

    }
    //
    // WARNING: DO NOT ADD ANYTHING TO THIS FUNCTION UNLESS YOU ADD THE SAME
    // STUFF TO i386/FastWOW.asm.   i386/FastWOW.ASM is used for speedy
    // thunk dispatching on retail builds.
    //
}


#if NO_W32TRYCALL

INT
W32FilterException(
    INT ExceptionCode,
    PEXCEPTION_POINTERS ExceptionInformation
    )

/* W32FilterException - Filter WOW32 thread exceptions
 *
 * ENTRY
 *
 *    ExceptionCode - Indicate type of exception
 *
 *    ExceptionInformation - Supplies a pointer to ExceptionInformation
 *                           structure.
 *
 * EXIT
 *
 *    return exception disposition value.
 */

{
    extern BOOLEAN IsW32WorkerException(VOID);
    extern VOID W32SetExceptionContext(PCONTEXT);

    INT Disposition = EXCEPTION_CONTINUE_SEARCH;

    if ((ExceptionCode != EXCEPTION_WOW32_ASSERTION) &&
        IsW32WorkerException()) {

        Disposition = W32Exception(ExceptionCode, ExceptionInformation);
        if (Disposition == EXCEPTION_EXECUTE_HANDLER) {

            //
            // if this is the exception we want to handle, change its
            // context to the point where we can safely fail the api and
            // return exception disposition as continue execution.
            //

            W32SetExceptionContext(ExceptionInformation->ContextRecord);
            Disposition = EXCEPTION_CONTINUE_EXECUTION;
        }
    }
    return(Disposition);
}

#endif  // NO_W32TRYCALL


/* 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;

    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.
    //

    strcpy(szModule, CRITSTR(TheWin16Subsystem));
    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);
    }

    wsprintf(szErrorMessage,
             pszErrorFormatString,
             szTask,
             pszModuleFilePart,
             pexi->ExceptionRecord->ExceptionAddress,
             dwException
             );

    LOGDEBUG(LOG_ALWAYS, ("W32Exception:\n%s\n",szErrorMessage));

    //
    // Format dialog text into szDialogText and display.
    //

    if (AeAutoDebug) {

        dwButtonPushed = 2;

    } else {

        if (wDebugButton == SEB_CANCEL) {

            wsprintf(szDialogText,
                     "%s\n%s\n%s\n%s\n",
                     szErrorMessage,
                     CRITSTR(ChooseClose),
                     CRITSTR(ChooseCancel),
                     (dwException == EXCEPTION_DATATYPE_MISALIGNMENT)
                         ? CRITSTR(ChooseIgnoreAlignment)
                         : CRITSTR(ChooseIgnore)
                     );
        } else {

            wsprintf(szDialogText,
                     "%s\n%s\n%s\n",
                     szErrorMessage,
                     CRITSTR(ChooseClose),
                     (dwException == EXCEPTION_DATATYPE_MISALIGNMENT)
                         ? CRITSTR(ChooseIgnoreAlignment)
                         : CRITSTR(ChooseIgnore)
                     );

        }

        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 Status;
        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
            //

            Status = 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.
    //

    wsprintf(szCmdLine, "ntsd %s -p %lu -e %lu -x -g -G",
            fKernelDebuggerEnabled ? "-d" : "",
            GetCurrentProcessId(),
            hEvent
            );

    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 )
{
#if FASTBOPPING
    return (ULONG)WOWBopEntry;
#else
    return 0;
#endif
}

ULONG FASTCALL WK32WOWGetFastCbRetAddress( PVDMFRAME pFrame )
{
#if FASTBOPPING
    return (ULONG)FastWOWCallbackRet;
#else
    return( 0L );
#endif
}

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);

#if FASTBOPPING
    fKernelCSIPFixed = TRUE;
#endif

    return 1;
}

ULONG FASTCALL WK32WOWGetFlatAddressArray( PVDMFRAME pFrame )
{
#if FASTBOPPING
    return (ULONG)FlatAddress;
#else
    return 0;
#endif
}


#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);
        }
    }

    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

    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.
--*/
{
    PVOID  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 = malloc_w(buf16size)) {

            // now copy to the new 32-bit buffer
            if(bMulti) {
                Multi_strcpy(pbuf32, psrc16);
            } else {
                strcpy(pbuf32, psrc16);
            }

            // 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 {
            strcpy(pdst32, psrc16);
        }
    }

    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
DWORD GetWOWTicDiff(DWORD dwPrevCount) {
/*
 * Returns difference between a previous Tick count & the current tick count
 *
 * NOTE: Tick counts are in unspecified units  (PerfFreq is in MHz)
 */
    DWORD          dwDiff;
    LARGE_INTEGER  PerfCount, PerfFreq;

    NtQueryPerformanceCounter(&PerfCount, &PerfFreq);

    /* if ticks carried into high dword (assuming carry was only one) */
    if( dwPrevCount > PerfCount.LowPart ) {
        /* (0xFFFFFFFF - (dwPrevCount - LowPart)) + 1L caused compiler to
           optimize in an arithmetic overflow, so we do it in two steps
           to fool Mr. compiler
         */
        dwDiff = (dwPrevCount - PerfCount.LowPart) - 1L;
        dwDiff = ((DWORD)0xFFFFFFFF) - dwDiff;
    }
    else {
        dwDiff = PerfCount.LowPart - 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
