In the CRT description on MSDN, there is the following sentence:
For binary compatibility, more than one DLL file may be specified by a single import library.
What does this mean?
Assuming I manually link msvcp140.dll
and msvcp140_1.dll
, does adding a single msvcprt.lib
to dependencies mean that both DLLs will be linked?
For this, I looked into some options related to lib.exe
(Microsoft Library Manager) and realized that it is impossible to create a single import library (lib) through multiple .def
files.
However, I confirmed that it is possible to link multiple DLLs through a single lib by merging the Import Library (lib) generated when building two DLLs using the lib.exe
command.
I'm not sure if this is the best way to do it.
How does msvcprt.lib
work so that both msvcp140.dll
and msvcp140_1.dll
can be linked?
Additionally, I found that the C++Builder solution provided by Embarcadero has a utility called implib.exe
and a method to create a single lib using multiple custom defs, but this is not a Microsoft standard (however, it is compatible with MVSC).
I'm a newbie to this area, so I'd appreciate some concrete examples, rather than just a simple answer like "look at msdn".
In the CRT description on MSDN, there is the following sentence:
For binary compatibility, more than one DLL file may be specified by a single import library.
What does this mean?
Assuming I manually link msvcp140.dll
and msvcp140_1.dll
, does adding a single msvcprt.lib
to dependencies mean that both DLLs will be linked?
For this, I looked into some options related to lib.exe
(Microsoft Library Manager) and realized that it is impossible to create a single import library (lib) through multiple .def
files.
However, I confirmed that it is possible to link multiple DLLs through a single lib by merging the Import Library (lib) generated when building two DLLs using the lib.exe
command.
I'm not sure if this is the best way to do it.
How does msvcprt.lib
work so that both msvcp140.dll
and msvcp140_1.dll
can be linked?
Additionally, I found that the C++Builder solution provided by Embarcadero has a utility called implib.exe
and a method to create a single lib using multiple custom defs, but this is not a Microsoft standard (however, it is compatible with MVSC).
I'm a newbie to this area, so I'd appreciate some concrete examples, rather than just a simple answer like "look at msdn".
Share Improve this question edited Mar 18 at 18:39 Remy Lebeau 601k36 gold badges507 silver badges850 bronze badges asked Mar 18 at 8:23 arcane22arcane22 311 silver badge1 bronze badge 1- "Assuming I manually link msvcp140.dll and msvcp140_1.dll": what does that mean? how ho you manually link them? – CristiFati Commented Mar 18 at 14:13
2 Answers
Reset to default 4Assuming I manually link msvcp140.dll and msvcp140_1.dll
That's not how it works. You link a .lib
. When that's an import lib, you get a runtime dependency on one or more DLLs. The OS will then load those dependent DLLs (typically when the EXE is loaded i.e. the process is created).
It does not matter to you exactly what Microsoft did to create that import library. They're only telling you that there can be multiple DLLs involved because you might need to redistribute those DLLs with your executable.
The "binary compatibility" part is because there are existing EXE files in the wild which make (bad) assumptions about the content of msvcp140.dll
.
an Embarcadero utility called implib
This is not a utility which you'll need as a newbie. In fact, I haven't needed it in 30 years.
The page where the quote is from ([MS.Learn]: C runtime (CRT) and C++ standard library (STL) .lib files) would have been useful.
This approach (one import library and multiple .dlls) is useful, especially since UCRT (might take a look at [SO]: How to circumvent Windows Universal CRT headers dependency on vcruntime.h (@CristiFati's answer)) arrived.
Note that it's a perpetual model: at the beginning it was (taking the simplest example) one .lib and one .dll, then they decided to add more functionality. Instead of rebuilding (and redistributing) the .dll (other existing apps using newer version might be negatively impacted), they just specified it in the .lib file(s) (and also .h file(s)), and they distribute a new .dll containing the new functionality (leaving the original one untouched).
There are several advantages to the approach:
Software is easily breakable into pieces. Related code is grouped in a separate .dlls. At runtime, only the specific .dll will be loaded, instead the huge monolithic one containing everything (less memory, less loading time)
What I talked about in the above paragraph: extending without rebuilding existing .dlls
Seamless build (link) step. There will be no change (as opposed to having one .lib file for each .dll), the big .lib file is specified (although for UCRT this is done automatically by VStudio), and the linker will pick only the .dlls containing referenced symbols
I created a MCVE that hopefully makes things clearer:
dll.h:
#pragma once #include <stdio.h> #if defined(_WIN32) # if defined(DLL_EXPORTS) # define DLL_EXPORT_API __declspec(dllexport) # else # define DLL_EXPORT_API __declspec(dllimport) # endif #else # define DLL_EXPORT_API #endif #define PRINT_MSG_1S(ARG0) printf(" [%s] (%d) - [%s]: ARG0: %s\n", __FILE__, __LINE__, __FUNCTION__, ARG0) #if defined(__cplusplus) extern "C" { #endif // dll.dll functions (initial version) DLL_EXPORT_API int dllFunc00(); // dll00.dll functions (added in a later version) DLL_EXPORT_API int dll00Func00(); // dll01.dll functions (TBR) // ... #if defined(__cplusplus) } #endif
dll.c:
#define DLL_EXPORTS #include "dll.h" int dllFunc00() { PRINT_MSG_1S("dll.dll"); return 0; }
dll00.c:
#define DLL_EXPORTS #include "dll.h" int dll00Func00() { PRINT_MSG_1S("dll00.dll"); return 1; }
main00.c:
#include <stdio.h> #include "dll.h" int main() { int res = dllFunc00(); printf("\nDone.\n\n"); return 0; }
main01.c:
#include <stdio.h> #include "dll.h" int main() { int res = dll00Func00(); printf("\nDone.\n\n"); return 0; }
Output:
[cfati@CFATI-5510-0:e:\Work\Dev\StackExchange\StackOverflow\q079516767]> sopr.bat ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [prompt]> "c:\Install\pc064\Microsoft\VisualStudioCommunity\2022\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul [prompt]> [prompt]> dir /b dll.c dll.h dll00.c main00.c main01.c [prompt]> [prompt]> :: Build initial .dll (and an executable depnding on it) [prompt]> cl /nologo /MD /DDLL dll.c /link /NOLOGO /DLL /OUT:libdll.dll /IMPLIB:libdll_.lib dll.c Creating library libdll_.lib and object libdll_.exp [prompt]> [prompt]> lib /OUT:libdll.lib libdll_.lib Microsoft (R) Library Manager Version 14.43.34809.0 Copyright (C) Microsoft Corporation. All rights reserved. [prompt]> [prompt]> cl /nologo /MD /W0 main00.c /link /NOLOGO /OUT:main00.exe libdll.lib main00.c [prompt]> [prompt]> dir /b dll.c dll.h dll.obj dll00.c libdll.dll libdll.lib libdll_.exp libdll_.lib main00.c main00.obj main01.c main_win.exe [prompt]> [prompt]> :: Add new functionality (*.dll(s)) and another executable depending on it [prompt]> cl /nologo /MD /DDLL dll00.c /link /NOLOGO /DLL /OUT:libdll00.dll dll00.c Creating library libdll00.lib and object libdll00.exp [prompt]> [prompt]> lib /OUT:libdll.lib libdll_.lib libdll00.lib Microsoft (R) Library Manager Version 14.43.34809.0 Copyright (C) Microsoft Corporation. All rights reserved. libdll00.lib(libdll00.dll) : warning LNK4006: __NULL_IMPORT_DESCRIPTOR already defined in libdll_.lib(libdll.dll); second definition ignored [prompt]> [prompt]> cl /nologo /MD /W0 main01.c /link /NOLOGO /OUT:main01.exe libdll.lib main01.c [prompt]> [prompt]> dir /b dll.c dll.h dll.obj dll00.c dll00.obj libdll.dll libdll.lib libdll00.dll libdll00.exp libdll00.lib libdll_.exp libdll_.lib main00.c main00.exe main00.obj main01.c main01.exe main01.obj [prompt]> [prompt]> :: Run the 2 executables [prompt]> main00.exe [dll.c] (7) - [dllFunc00]: ARG0: dll.dll Done. [prompt]> [prompt]> main01.exe [dll00.c] (7) - [dll00Func00]: ARG0: dll00.dll Done. [prompt]> [prompt]> :: List .exes dependencies [prompt]> dumpbin /nologo /dependents main00.exe Dump of file main00.exe File Type: EXECUTABLE IMAGE Image has the following dependencies: libdll.dll KERNEL32.dll VCRUNTIME140.dll api-ms-win-crt-stdio-l1-1-0.dll api-ms-win-crt-runtime-l1-1-0.dll api-ms-win-crt-math-l1-1-0.dll api-ms-win-crt-locale-l1-1-0.dll api-ms-win-crt-heap-l1-1-0.dll Summary 1000 .data 1000 .pdata 1000 .rdata 1000 .reloc 1000 .text [prompt]> [prompt]> dumpbin /nologo /dependents main01.exe Dump of file main01.exe File Type: EXECUTABLE IMAGE Image has the following dependencies: libdll00.dll KERNEL32.dll VCRUNTIME140.dll api-ms-win-crt-stdio-l1-1-0.dll api-ms-win-crt-runtime-l1-1-0.dll api-ms-win-crt-math-l1-1-0.dll api-ms-win-crt-locale-l1-1-0.dll api-ms-win-crt-heap-l1-1-0.dll Summary 1000 .data 1000 .pdata 1000 .rdata 1000 .reloc 1000 .text
Might also want to check:
[MS.Learn]: Overview of LIB
[MS.Learn]: Linker options
[SO]: Discover missing module using command-line ("DLL load failed" error) (@CristiFati's answer)
[SO]: LNK4006, LNK4221 warnings when using static library that includes another static library
[SO]: LNK2005 Error in CLR Windows Form (@CristiFati's answer)