Louis Solomon / SteelBytes
Written 22/Apr/08
Revised 23/Apr/08 typo's and comment about link.exe/editbin.exe, and the 'quick notes'
Added Link 25/Aug/08 My command line tool that patches compiled code ExeVersion
Added Link 27/Aug/08 other related work LegacyExtender
Here is the result of my exploring this problem ... YMMV :-)
Part 1 (NT4 and Win9x): the required OS version flags in the .exe header are set to 5.0. Can you use Link.exe's or EditBin.exe's /SUBSYSTEM switch to fix this? No, as they don't let you set a value less then 5.0 on both the OS and SubSystem fields (using older version of EditBin from some older version of VS/VC/SDK apparently works). Solution is to patch 'em back to 4.0, here is some code that will do that
HANDLE hfile = CreateFile(argv[1],GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,NULL); if (hfile!=INVALID_HANDLE_VALUE) { HANDLE hmapping = CreateFileMapping(hfile,0,PAGE_READWRITE,0,0,0); if (hmapping) { BYTE *file_base = (BYTE *)MapViewOfFileEx(hmapping,FILE_MAP_ALL_ACCESS,0,0,0,0); if (file_base) { IMAGE_OPTIONAL_HEADER *ioh = (IMAGE_OPTIONAL_HEADER *)(file_base + ((IMAGE_DOS_HEADER *)file_base)->e_lfanew + 4 + sizeof(IMAGE_FILE_HEADER)); ioh->MajorOperatingSystemVersion = 4; ioh->MinorOperatingSystemVersion = 0; ioh->MajorSubsystemVersion = 4; ioh->MinorSubsystemVersion = 0; UnmapViewOfFile(file_base); } CloseHandle(hmapping); } CloseHandle(hfile); }
Part 2 (Win9x): the CRT uses the Unicode / Widechar version of GetModuleHandle() in it's internal _crt_waiting_on_module_handle(). Here is a replacement that doesn't
extern "C" __declspec(noinline) HMODULE __cdecl _crt_waiting_on_module_handle(LPCWSTR szModuleName) { #define INCR_WAIT 1000 #define _MAX_WAIT_MALLOC_CRT 60000 char szModuleNameA[MAX_PATH]; WideCharToMultiByte(CP_ACP,0,szModuleName,-1,szModuleNameA,_countof(szModuleNameA),NULL,NULL); unsigned long nWaitTime = INCR_WAIT; HMODULE hMod = GetModuleHandleA(szModuleNameA); while(hMod == NULL) { Sleep(nWaitTime); hMod = GetModuleHandleA(szModuleNameA); nWaitTime += INCR_WAIT; if(nWaitTime > _MAX_WAIT_MALLOC_CRT) { break; } } return hMod; #undef INCR_WAIT #undef _MAX_WAIT_MALLOC_CRT }
Part 3 (Win9x): the CRT is incorrectly checking the return code of InitializeCriticalSectionAndSpinCount() on Win9x, and here is the fixed version of that function
extern "C" __declspec(noinline) int __cdecl __crtInitCritSecAndSpinCount (PCRITICAL_SECTION lpCriticalSection,DWORD dwSpinCount) { int ret; __try { /* * Call the real InitializeCriticalSectionAndSpinCount */ ret = InitializeCriticalSectionAndSpinCount(lpCriticalSection, dwSpinCount); if (GetVersion()&0x80000000) ret = TRUE; // when win9x assume true (else we would have thrown an exception - read the MSDN :-) } __except (_exception_code()== STATUS_NO_MEMORY ? EXCEPTION_EXECUTE_HANDLER:EXCEPTION_CONTINUE_SEARCH) { /* * Initialization failed by raising an exception, which is probably * STATUS_NO_MEMORY. It is not safe to set the CRT errno to ENOMEM, * since the per-thread data may not yet exist. Instead, set the Win32 * error which can be mapped to ENOMEM later. */ if (GetExceptionCode() == STATUS_NO_MEMORY) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); } ret = FALSE; } return ret; }
Quick set of notes on making use of this info in vs2008