2012-06-03: Published on my blog at berlios.de.
2017-12-22: I discovered a copy of this text on my laptop. Fixed some URLs.
About 14 years ago, when I began to code for Windows (NT 4.0 at that time), one of the first things I had to learn was to choose the right C-Runtime Library. At that time there were several options: static library or DLL, single-threaded or multi-threaded, debug or release version. Fortunately, the right settings were obvious: DLL, multi-threaded, release: msvcrt.dll. Of course you had look after the version of that library, but that wasn't to difficult.
During the years Microsoft improved Visual Studio and new versions of the C-Runtime became available: msvcr70.dll ... msvcr100.dll. However theses libraries belong to the compiler, not to the operation system. The C-runtime provided by the operation system is still msvcrt.dll. That's a very unfamiliar situation for an developer coming from a Unix background, where libc is provided as part of the operating system and is the most basic library at all. Of course the Windows situation is conceptually clean: the OS provides the Win32 API and this API is independent of any programming language. If you coding C, your compiler provides the required C-runtime library. And if you happen to use different compilers you end up with different C-runtime libraries. And now you are in trouble: there is not only a performance penalty if you program links more libraries, you must care not to reserve a resource (like a FILE pointer) with one C-runtime library and release it using a second C-runtime library. The recommended solution is to recompile and relink everything using the latest version of Visual Studio. That's fine, if you have access to the source, budget and time to port all your projects. Unfortunately, in most non trivial projects you have neither access to all sources nor the time and the budget to port all sub-projects to a newer compiler. And even, if you manage it and build everything using a single version of Visual Studio, the same problem will raise its head again, when Microsoft publishes the next version of the compiler.
What else can be done? Of course there are a few options: ignoring the problem (hey, that's a real option!), not using C or C++ at all or - last but not least - linking against msvcrt.dll. This last option has a few advantages: the library is part of the operating system and therefore always available and up to date. There is also a major disadvantage: it's not a trivial task to link against this library. Currently the WDK compiler and its ugly build system provide the only supported way to link against msvcrt.dll. Other options are the ancient Visual Studio 6 (you still have a licensed copy, don't you?) or the MinGW port of GCC. That's all not convenient enough for a mainstream project. Therefore I'll demonstrate how to compile and link against msvcr90.dll using Visual Studio 2008.
If you already know and use Project Property Sheets, you may want to skip this paragraph. Otherwise read this definition from Microsoft:
A project property sheet is an .xml file with the extension .vsprops. It enables you to specify switches for build tools such as the compiler or linker and create user-defined macros. You can use property sheets to create project configurations that can be applied to multiple projects since project settings that are defined in .vsprops files are inheritable, unlike project settings defined in Project Files (.vcproj files). Therefore, a project configuration defined in a .vcproj file can inherit project settings from one or more property sheets (.vsprops files).
Now think of a Project Property Sheet, that changes the compiler and linker switches of an arbitrary project to compile and link against msvcrt.dll instead of msvcr90.dll. If you had such a property sheet, switching to msvcrt.dll becomes very simple and affordable even for a large solution, that contains many build targets. Impossible? Not at all, just look at my project https://github.com/scVENUS/vs90-msvcrt. It provides the property sheet vs90_msvcrt.vsprops. We will look at it later.
Simply clone https://github.com/scVENUS/vs90-msvcrt and get a copy of the WDK (Windows Driver Kit) version 7.1.0. (It is available from Microsoft's download center http://www.microsoft.com/en-us/download/details.aspx?id=11800). Then follow the instructions in readme.txt.
<?xml version="1.0" encoding="Windows-1252"?> <VisualStudioPropertySheet ProjectType="Visual C++" Version="8.00" Name="vs90_msvcrt" > <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories=""$(WdkDir)\inc\crt"" AdditionalUsingDirectories=""$(WdkDir)\inc\crt"" PreprocessorDefinitions="_USE_MSVCRT;_NO_DEPRECATED__TOLOWER__TOUPPER" RuntimeLibrary="2" /> <Tool Name="VCLinkerTool" AdditionalDependencies="msvcrt_winxp.obj msvcrt_supplement.lib" AdditionalLibraryDirectories=""$(WdkDir)\lib\Crt\i386";"$(WdkDir)\lib\wxp\i386"" /> <Tool Name="VCManifestTool" EmbedManifest="false" /> <UserMacro Name="WdkDir" Value="D:\fgng\wdk" PerformEnvironmentSet="true" /> </VisualStudioPropertySheet>
This property sheet
By simply including this property sheet into your Visual Studio solution, your solution uses the MSVCRT.DLL.
Microsoft uses almost the same C++-compiler CL.EXE for the WDK and for Visual Studio. If you build a program using the WDK, the additional object file msvcrt_winxp.obj provides all the definitions and functions missing from msvcrt.dll compared with msvcr90.dll. In theory you only need to link msvcrt_winxp.obj and MSVCRT.LIB. (See Koby Kahane, Dynamically linking with MSVCRT.DLL using Visual C++ 2005 for an excellent discussion of the WDK details.) Unfortunately MSVCRT.LIB lacks several commonly used symbols, which causes linker errors. This is unproblematic as long as you write new source code, because you can substitute the missing functions by similar functions. But if you just want to rebuild an existing project, the missing symbols must be somehow provided. There are many ways to provide a symbol. I'll demonstrate a few.
An import library is a file, that informs the compile time linker, about the content of a particular dynamic link library (DLL). The file extension of import libraries as well as static libraries is usually '.lib' and the file name of an import library usually matches the file name of its DLL, but there are exceptions. For instance, Visual Studio provides the import library named MSVCRT.LIB for the DLL MSVCR90.DLL. A less known fact is that you can combine a static library and a link library into a single file. The library "msvcrt_supplement.lib" is an example for such a combination.
One symbol missing from the WDK-provided MSVCRT.LIB is the global variable "_environ". It's usage has been deprecated by Microsoft. However it is provided by the MSVCRT.DLL and many programs origin from Posix require this variable. To overcome this problem we add an additional link library for MSVCRT.DLL, that declares the global variable "_environ". Another missing symbol is the function getpid(). Microsoft renamed this function to _getpid() (which is missing from MSVCRT.LIB too). Fortunately a link library can even rename a symbol from a DLL. To create the link library, we need to call LIB.EXE with the following options:
/def /machine:X86 /name:msvcrt.dll /export:_environ,DATA /export:_getpid /export:getpid:_getpid
Here we tell the linker to export "_environ" as global DATA and to resolve the symbol "getpid" using the symbol "_getpid" from MSVCRT.DLL. The output is a link library, that can be linked into a static library.
Unfortunately Visual Studio does not support multiple invocations of LIB.EXE within a single project, but there's a trick: As an alternative to placing all the options on the command line, the LIB.EXE accepts response files that contain switches and arguments. The response file "create_importlib.args" contains the LIB.EXE arguments. I added this file as an additional source the the Visual Studio project and defined an custom build step for the response file. Within the custom build step Visual Studio invokes LIB.EXE which creates the link library "msvcrt_supplement_importlib.lib". Later Visual Studio links this library into "msvcrt_supplement.lib".
UNIX and the C programming language often represent time as number of seconds since Jan 1st, 1970. The C standard defines the specialised integer type "time_t" to store this number. Historically time_t was a 32bit value, but today "time_t" is usually 64bit wide, because of the overflow of 32bit time_t variables 2038. Microsoft also used a 32bit time_t for MSVCRT.DLL and switched to 64bit with VC8 (Visual Studio 2005). A similar issue exists with file sizes: todays big hard disks require 64 bit for file sizes. Surely the most affected function is "stat", because it returns both "time_t" values and a file size. There are 4 possible size combinations for time and size values and current Microsoft runtime libraries provide them all. The aged MSVCRT.DLL does not. I implemented the missing functions in msvcrt_missing.c.
During the preparation of this project, you had to patch some WDK header files. Besides changes related to the time_t and file size issues, there are two additional modifications: