/*++ * * 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 #include #include /* 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