Developers‎ > ‎

NN's article

This is an archive copy of the original article, with the permission of the author, NN (thanks!). You can also read the original entry.


VC++ 9.0 is a nice compiler but since VC++ 7.0 the binary does not use msvcrt.dll for dynamic build.

So solutions:
  • Provide installer for mvcr90.dll
  • Compile static build
Both solutions are not reasonable sometimes. The solution 1 cannot be used for ActiveX controls because there is undoubtedly no VC++ 9.0 runtime in all computers. The solution 2 is not usable when you want the same heap in executable and in your dll or in two dlls.

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:

Second, download Windows Driver Kit (WDK). Download it here:

We are ready to go.
  1. Create a project
  2. Select release configuration.
  3. Set additional include directories to C:\WinDDK\\inc\crt. You cannot use VC++ crt directory!
  4. Set additional library directories to C:\WinDDK\\lib\w2k\i386
  5. Add to additional libraries msvcrt_win2000.obj
  6. Compile.

That’s all. Now your program is dependent only on msvcrt.dll!


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 Checks

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


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


No problems! Just use ATL from WDK (C:\WinDDK\\inc\crt\atl71) and library from C:\WinDDK\\lib\atl\i386

Notice 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++ Library

You 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. (

You need some special defines to make STLPort work correctly like:




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


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


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.



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


If I have written something wrong here, tell me :)

Terms  |  Report Abuse  |  Powered by Google Sites