This is an archive copy of the original article, with the permission of the author, NN (thanks!). You can also read the original entry. Prelude:VC++ 9.0 is a nice compiler but since VC++ 7.0 the binary does not use msvcrt.dll for dynamic build.So solutions:
The solution:We use the trick from Dynamically linking with MSVCRT.DLL using Visual C++ 2005.First, download VC++ 9.0 express (it is free and legal to use J). Download it here: https://www.microsoft.com/express/. Second, download Windows Driver Kit (WDK). Download it here: https://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx We are ready to go.
That’s all. Now your program is dependent only on msvcrt.dll! Problems:No debug?Yes there is no msvcrtd.dll for msvcrtd.lib which supplied with WDK. Maybe it is intentional, maybe not.It doesn’t say that you cannot compile debug, it just says that you must not use debug macros from CRT like ASSERT, assert and so on. You can implement these macros by yourself and this works. RunTime ChecksIt is easy, just set the compiler flags.The only problem you may notice is compiling static build with RunTime Checks. There is same function crt_debugger_hook in RunTmChk.lib from VC++ and libcmtd.lib from WDK. The solution is simple, patch RuTmChk.lib (replace the name to e.g. crt_debugger_hoox) and move it to patched\RuTmChk.lib. Then add patched directory to additional library directories. Now RunTime Checks work with static build. CRT_RTC_INITWYou may see that linker cannot find CRT_RTC_INIT function when you compile in debug.The implementation is very easy, you just need to implement CrtDbgReportW by yourself (You have the sources in Visual Studio 2008) and return this function. ATLNo problems! Just use ATL from WDK (C:\WinDDK\\inc\crt\atl71) and library from C:\WinDDK\\lib\atl\i386Notice that ATL must not be compiled in debug. Just undefine DEBUG macro before including atlbase.h: #pragma push_macro(“_DEBUG”) #undef _DEBUG #include <atlbase> #pragma pop_macro(“_DEBUG”)</atlbase> You need also to define your own implementation of ATLASSERT, otherwise it uses ASSERTE which cannot be used in our case. It is not ATL 8.0 nor ATL 9.0 but it is better than nothing J Standard C++ LibraryYou cannot use the VC++ headers because they rely on VC++ 9.0 CRT, so they are not usable.You may use other Standard C++ Library implementation like STLPort. (https://stlport.org/download.html) You need some special defines to make STLPort work correctly like: _STLP_HAS_NO_NEW_C_HEADERS _STLP_GLOBAL_NEW_HANDLER _STLP_VENDOR_TERMINATE_STD= And some more till you have your code compiled well. (Just read the readme files of STLPort) You may compile STLPort if you want to use Standard C++ streams. Windows 9x:Visual C++ 9.0 does not support Windows 9x at all. In fact the only support is for Windows 2000 and above.But what miserable Windows 9x users should do? Use old versions of VC++? No!! Investigation:When you run the compiled executable which uses only msvcrt.dll it doesn’t work because you have message box about unsupported Operating System.This happens because of linker. Linker from VC++ 9.0 sets minimum Operating System to 5.0 and SubSystem version to 5.0. There /SUBSYSTEM flag does not allow to set the number below 5.0. The simplest solution is just to patch the executable and change 5.0 to 4.0 J After that the executable runs smoothly in Windows 98, but not Windows 95. Windows 95:The only reason why Windows 95 cannot be supported is use of InterlockedCompareExchange in CRT startup code!!It can be solved very simple. You just “paste” the code of InterlockedCompareExchange instead of function call and change ImportAddressTable to not point to InterlockedCompareExchange. More detailed information about Windows 95:Compile a simple program: int main(){}Now if you disassemble your program you will see the following code: 53 push ebx ; Comperand 56 push esi ; Exchange 57 push edi ; Destination FF 15 24 20 40 00 call ds:InterlockedCompareExchange This code is created by linker, so you cannot control it. Now we just have to replace call with direct call: InterlockedCompareExchange implementation (in kernel32.dll): InterlockedCompareExchange proc near ;Destination = dword ptr 4 ;Exchange = dword ptr 8 ;Comperand = dword ptr 0Ch mov ecx, [esp+4] mov edx, [esp+8] mov eax, [esp+0Ch] lock cmpxchg [ecx], edx retn 0Ch InterlockedCompareExchange endp Now the replacement: mov ecx, [edi] mov edx, [esi] mov eax, ebx lock cmpxchg [ecx], edx It will be better to reduce the size: 8B 16 mov edx,dword ptr [esi] 8B C3 mov eax,ebx F0 0F B1 17 lock cmpxchg dword ptr [edi],edx 90 nop The next thing is to remove InterlockedCompareExchange from Import Table. I just replace it with InterlockedIncrement: Find it using your favourite hex editor (I use HxD, it is free :) ) Replace InterlockedCompareExchange with InterlockedIncrement, fill zeroes to match the same size. Now look at the number of the function, it is BA 02 (02BAh). (It is before the name) InterlockedIncrement has other number: 02C0h. Just set the number. That’s all. And of course do not forget to update exe checksum even if no one checks it :) Conclusions:You can use Visual C++ Express 2008 and run your code even in Windows 95 without too much effort.Why Microsoft doesn’t want us to be able to compile for Windows 9x? The answer is simple; it costs money to support old and buggy Operating Systems. Enjoy. P.S.I didn’t dive into technical details of every small thing.You may ask questions about the specific detail, if it is needed. Thanx to KK P.P.S.If I have written something wrong here, tell me :) |