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:
1. Provide installer for mvcr90.dll
2. 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: http://www.microsoft.com/express/.
Second, download Windows Driver Kit (WDK). Download it here: https://connect.microsoft.com/Downloads/DownloadDetails.aspx?SiteID=148&DownloadID=11295
We are ready to go.
1. Create a project
2. Select release configuration.
3. Set additional include directories to C:\WinDDK\<WDK_VERSION>\inc\crt. You cannot use VC++ crt directory!
4. Set additional library directories to C:\WinDDK\<WDK_VERSION>\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 !
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 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.
CRT_RTC_INITW
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.
ATL
No problems! Just use ATL from WDK ( C:\WinDDK\<WDK_VERSION>\inc\crt\atl71 ) and library from C:\WinDDK\<WDK_VERSION>\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.h>
#pragma pop_macro(“_DEBUG”)
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. (http://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:
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 ![]()
Can you give some more details on how to replace InterlockedCompareExchange ?
Comment by ac — May 4, 2008 @ 10:08 am |
Look at InterlockedCompareExchange implementation in kernel32.dll
And disassemble your executable, just replace the binary code. Instead of call to function use lock xchg inline.
Change IAT, instead of InterclockedCompareExchange replace to InterclockedIncrement for example, and replace function number.
Tomorrow I will publish the specific assembly code to change.
I do not have time for right now.
Comment by NN — May 4, 2008 @ 6:07 pm |
[Edited]
Moved to the post.
Comment by NN — May 7, 2008 @ 2:07 pm |
I assume you created a little app that does this for you as a post-build step?
Comment by fred — May 8, 2008 @ 4:36 pm |
No, I didn’t.
It was just a prove of concept to make this work.
You are welcome to write some script to do this.
It will be nice to publish it too
Comment by NN — May 8, 2008 @ 4:54 pm |
Isn’t this overkill? What are you saving overall, about 20 kB ? Why not just statically link all your dlls/exes and then use an installer that uses LZMA compression.
It was an interesting post even despite the impracticality of using this in production.
Comment by Vorenus — August 16, 2008 @ 4:47 pm |
Static link does not solve problem for using same heap between dll and exe.
Using dynamic linking you get much smaller executable than using static.
Comment by NN — August 23, 2008 @ 2:39 pm |
Have you seen Legacy Extender for Visual Studio: http://www.legacyextender.com, it has a library with the missing functions, so it is compatible with optimization too, and it also reimplements some Unicode functions which are present but non-working in Windows 95.
Comment by Mike — August 25, 2008 @ 6:32 pm |
NN could I get the code for that? I’ve been waiting for someone to do this for a while. Otherwise I will have to pay an edi expert or a programmer. And I would rather not reach that deep into my pockets.
Comment by Bob Lawblaw — September 19, 2008 @ 8:00 pm |
What do you mean code ?
There is no code at all you have to write for the release version.
Comment by NN — September 20, 2008 @ 6:05 am |
It doesn’t work on Visual 2008 and Vista64.
Comment by mitchi — March 14, 2009 @ 7:36 pm |
Very interesting what you are saying.
Can you tell what exactly doesn’t work ?
I have Windows x64 but no time now to check it.
Comment by NN — March 14, 2009 @ 7:42 pm |