Skip to content

Using MSVCRT.DLL with Visual Studio compiler

Konstantin Nosov edited this page May 16, 2019 · 8 revisions

Table of contents

Purpose

Visual Studio C/C++ compiler (CL), like probably any other compiler, has C runtime library (CRT) which could be linked into final executable (or DLL) statically or dynamically. Every compiler version has its own DLL with CRT, which is typically placed into Windows system directory. Visual C++ 6.0 had CRT in msvcrt.dll, every newer compiler added some numbers to DLL name.

Dynamic CRT vs static CRT

When you're building an application, you should decide if you'll use static or dynamic CRT. Static CRT will be included into the executable, growing its size. If application has several DLL which can allocate memory and return to application, they all should use the same memory manager which is not achievable with "static CRT" model, unless DLL will use special interface functions for accessing application's memory manager.

Dynamic CRT has no problems with memory management if all components of application uses that CRT. The problem is that you should either ship DLL(s) with application setup, or add a requirement to install DLL files from Microsoft's download server.

MSVCRT.dll benefits

So, why installing compiler CRT files when there's already a version of dynamic CRT library in your system? There are discussions in Web about pro's and con's of using MSVCRT.dll. In fact, Mingw compiler uses this DLL by default. Visual C++ never uses this unless you'll use some command line tweaks to compiler. Some people says: "this is a system DLL and it shouldn't be used at all". However, following this line, I can't use kernel32.dll and user32.dll, can't use Windows API?

So, let's keep it to the application developer, whether he can or can't use MSVCRT.dll in his application. I'm using it for years in UModel, with no problems at all. I know people use with with many Windows versions - probably starting with Windows XP, with 32 and 64 bit OS setups. This allows me to make application executable smaller with no adding installation requirements.

Currently msvcrt.dll is considered as a system library not linked with compiler CRT. Every modern Windows OS (at least Windows XP+, or may be even earlier) has such DLL installed. The only way to link with this dll "officially" is to use WDK (Windows Driver Kit). We're using it non-officially, borrowing LIB files for this dll from WDK and adjusting build instructions.

Difficulties

Different DLL in different OS versions

TODO

STL

STL has a separate dll (msvcp*.dll) for some non-template things like "std::cerr", iostream etc. There's no "standard STL dll", just C runtime library, so you won't be able to use msvcrt.dll and part of STL in your application. It is possible that there's some #define which allows to ignore STL dll in some way, I just didn't check that since probably year 2009.

Visual Studio 2015 and newer (UCRT)

Use of predefined FILE streams

In general, everything works, unless you'll try to use stdout/stderr/stdin variables. In UCRT Microsoft changed the way how it works (see UCRT for details, file ucrt/corecrt_wstdio.h), C compiler will poll different CRT function to get access to those variables. However it is still possible to use these streams with adding special code to your application. Below is the copy-paste from UModel's CoreWin32.cpp

#if defined(OLDCRT) && (_MSC_VER  >= 1900)

// Support OLDCRT with VC2015 or newer. VC2015 switched to another CRT model called "Universal CRT".
// It has some incompatibilities in header files.

// Access stdin/stdout/stderr.
// UCRT uses __acrt_iob_func(). Older CRT used __iob_func. Also, older CRT used "_iobuf" structure with
// alias "FILE", however "FILE" in UCRT is just a pointer to something internal structure.

// Define __iob_func locally because it is missing in UCRT
extern "C" __declspec(dllimport) FILE* __cdecl __iob_func();

// Size of FILE structure for VS2013 and older
enum { CRT_FILE_SIZE = (sizeof(char*)*3 + sizeof(int)*5) };

// Note that originally this function has dll linkage. We're removing it with _ACRTIMP_ALT="" define
// in project settings. It is supposed to work correctly as stated in include/.../ucrt/corecrt.h
// Without that define, both compiler and linker will issue warnings about inconsistent dll linkage.
// Code would work, however compiler will generate call to __acrt_iob_func via function pointer, and
// it will add this function to executable exports.

extern "C" FILE* __cdecl __acrt_iob_func(unsigned Index)
{
	return (FILE*)((char*)__iob_func() + Index * CRT_FILE_SIZE);
}

#endif // OLDCRT

This code adds a function __acrt_iob_func which is called by new CRT, and it redirects call to a __iob_func from msvcrt.dll. There's different layout of FILE structure, so code makes some size adjustments to make things correct.

When you'll add this code, linker will warn you that __acrt_iob_func is defined in header as "dll export", but it is not exported from C code. And after all, this function will appear in exports of your linked executable. To suppress this behavior, your should add some define to compiler options: _ACRTIMP_ALT.

printf() function group

UCRT has defined some "common" functions which are called by locally defined printf, sprints, vsprintf etc, so most of those functions will redirect to less number of functions. Those "common" functions are missing in msvcrt.dll, so linkage will fail. To bypass this problem, you should add define _NO_CRT_STDIO_INLINE to the compiler's command line.

Thread-safe statics (C++11)

When you have some static variable inside a function, older compiler (2013 and earlier) used code like listed below for initialization:

	if (!init_guard)
	{
		init_guard = 1;
		Call_variable_constructor();
		atexit(Variable_destructor);
	}

Everything was simple. With new C++ standard, variable initializers should be thread-safe, so compiler added new stuff which you'll immediately notice when linking the application: there will be a few unresolved externals: __Init_thread_epoch, __Init_thread_footer and __Init_thread_header. These functions are used before and after initialization block. If you don't need thread safety for static variables, you may disable this behavior with using solution described in this article. In short, you should add option /Zc:threadSafeInit- (note the minus sign after text) to the compiler arguments.

If you need thread safety, you should implement mentioned functions yourself. For reference, check UCRT source code, file crt/src/vcruntime/thread_safe_statics.cpp.

New 'operator delete'

For some reason, most of deletion of objects in VS2019 is performed with a different "operator delete":

void operator delete(void* ptr, size_t)

Make sure you define this operator in your source code.

Files

Check this page, look for "MSVCRT.zip". Actually useful files in this archive are in "lib" folder.

How to use

  • CL options: -nodefaultlib:libcmt.lib -nodefaultlib:msvcprt.lib -manifest:no
  • todo list and explain options from common.project