VS static link and dynamic link

I recently took over a new project and encountered a series of link errors, which caused my head to be overwhelmed. Last weekend, I finally completely solved the dependency and linking problems between various projects. Taking this opportunity, I read some materials carefully and did some experiments on vs2019. I basically figured out some basic concepts of static link and dynamic link. Record it here, I hope to help myself and others.
The following is an experiment to explain how to perform static linking and dynamic linking in the vs2019 environment.

1. Create three new projects

First, we need to create a new solution named Test, and create two new projects in Test, Printer and Calc, Configuration are configured as Debug. In the Calc project, we will implement the addition function and call the printing function provided by the Printer process to print the calculation results. In the Test project, we implement the main function, and call the addition function provided by the Calc project in the main function.
Therefore, the dependency of these three projects is that the Test project depends on the Calc project, and the Calc project depends on the Printer project.

2. Add source files

  1. Printer project
//Printer.h"
#pragma once
class Printer {
public:
    static void PrintInt(int value);
};

//Printer.cpp
#include "Printer.h"
#include<iostream>
void Printer::PrintInt(int value)
{
    std::cout << value << std::endl;
}
  1. Calc project
//Calc.h
#pragma once
class Calculator {
public:
    int Add(int m, int n);
};

//Calc.cpp
#include "Calc.h"
#include "../Printer/Printer.h"
int Calculator::Add(int m, int n)
{
    Printer::PrintInt(m + n);
    return m + n;
}
  1. Test project
#include "../Calc/Calc.h"
int main()
{
    int m = 3, n = 4;
    Calculator c;
    c.Add(m, n);
    return 0;
}

Okay, now our three projects are shown below
Insert picture description here

3. Try to compile

When we compile the project, a lot of errors will be generated.

Severity	Code	Description	Project	File	Line	Suppression State
Error	LNK2019	unresolved external symbol "public: int __thiscall Calculator::Add(int,int)" (?Add@Calculator@@QAEHHH@Z) referenced in function _main	Test	C:\Users\vincent.zheng\source\repos\Test\Test\main.obj	1	
Error	LNK2019	unresolved external symbol _main referenced in function "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ)	Printer	C:\Users\vincent.zheng\source\repos\Test\Printer\MSVCRTD.lib(exe_main.obj)	1	
Error	LNK1120	1 unresolved externals	Test	C:\Users\vincent.zheng\source\repos\Test\Debug\Test.exe	1	
Error	LNK1120	1 unresolved externals	Printer	C:\Users\vincent.zheng\source\repos\Test\Debug\Printer.exe	1	
Error	LNK1120	2 unresolved externals	Calc	C:\Users\vincent.zheng\source\repos\Test\Debug\Calc.exe	1	
Error	LNK2019	unresolved external symbol _main referenced in function "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ)	Calc	C:\Users\vincent.zheng\source\repos\Test\Calc\MSVCRTD.lib(exe_main.obj)	1	
Error	LNK2019	unresolved external symbol "public: static void __cdecl Printer::PrintInt(int)" (?PrintInt@Printer@@SAXH@Z) referenced in function "public: int __thiscall Calculator::Add(int,int)" (?Add@Calculator@@QAEHHH@Z)	Calc	C:\Users\vincent.zheng\source\repos\Test\Calc\Calc.obj	1	

Look carefully at the error message, you will find that it Error LNK2019 unresolved external symbol xxx referenced in function xxxappears very frequently. This means that the external symbol was not found when linking. The specific reason is very simple, because we referenced the function of the Calc project in the test project (a function is a kind of symbol), and referenced the function of the Printer project in Calc, but these two The project did not export their symbols for use in other projects. To solve this problem, it is necessary to export the symbols of these two projects. There are generally two ways to export symbols. One is to export a static library, and the other is to export a dynamic library. Both methods have advantages and disadvantages.

4. Static Link

The static link is very simple, we static librarycan modify the Configuration Type of Printer and Calc project to (the default is Application).
Modification method:
right-click on the project name, select Configuration Properties–> General --> Configuration Type, and select from the drop-down box static library.

Compile the Printer and Calc projects separately. At this time, the Calc.liband Printer.libfiles will be generated separately in the Debug folder under the solution directory . This is the generated static link library file.

Generating lib files is not enough. We must tell the linker the path of the lib files and let the linker link to these library files. There are two ways:

  1. Add dependencies on the project. For example, the Calc project depends on the Printer project, and the Test project depends on the Calc project. Then we add dependencies to the corresponding projects.
    Insert picture description here

  2. Manually add the lib file path
    Right-click on the Test project and select Properties->Linker->input->Additional Dependencies. Add the calc.lib and printer.lib file paths
    Insert picture description here
    (the starting point of the relative path here is the path where the Test project file is located)

Theoretically speaking, after we add the lib file path, we can compile and link successfully, but in fact, we may also encounter another link error.

5. Solve link errors caused by compilation sequence

The above second method of adding file paths has a little side effect. After adding dependencies in the first method, vs can automatically determine the compilation order of each project. For example, in our example, Test depends on Calc, and Calc depends on Printer, so VS will first compile the Printer project, then compile the Calc process, and finally compile the Test project. However, the second method does not automatically deduce the compilation order, so it is possible that the Test project will be compiled first, and the Printer and Calc projects will be compiled later. At this time, the linker cannot find the lib file and will still report a link error. For example, in our example, recompiling after clean solution will report the following error:

1>------ Build started: Project: Test, Configuration: Debug Win32 ------
2>------ Build started: Project: Calc, Configuration: Debug Win32 ------
3>------ Build started: Project: Printer, Configuration: Debug Win32 ------
2>Calc.cpp
3>Printer.cpp
1>main.cpp
2>Calc.vcxproj -> C:\Users\vincent.zheng\source\repos\Test\Debug\Calc.lib
1>LINK : fatal error LNK1104: cannot open file '..\Debug\Printer.lib'
1>Done building project "Test.vcxproj" -- FAILED.
3>Printer.vcxproj -> C:\Users\vincent.zheng\source\repos\Test\Debug\Printer.lib
========== Build: 2 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

As you can see, vs. compiles the Test project first, and then compiles the Calc and Printer projects, causing the linker to report can not open ..\Debug\Printer.liban error. We can customize the compilation order to solve this problem. Right-click on the solution'Test', select Project Dependencies, set test to depend on Calc, and Calc to depend on Printer.

1>------ Build started: Project: Printer, Configuration: Debug Win32 ------
1>Printer.cpp
1>Printer.vcxproj -> C:\Users\vincent.zheng\source\repos\Test\Debug\Printer.lib
2>------ Build started: Project: Calc, Configuration: Debug Win32 ------
2>Calc.cpp
2>Calc.vcxproj -> C:\Users\vincent.zheng\source\repos\Test\Debug\Calc.lib
3>------ Build started: Project: Test, Configuration: Debug Win32 ------
3>main.cpp
3>Test.vcxproj -> C:\Users\vincent.zheng\source\repos\Test\Debug\Test.exe
========== Build: 3 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

This time, the compiler succeeded in compiling in the order we wanted, without errors.

6. Problems with static links

Static linking is relatively simple. Generate the lib file, set the link path, and our cute code can run very well. Although it is very simple to use, there are still some other problems with static linking.

  1. Waste of space. The statically linked executable file is relatively large and contains the same common code. For example, the Add function and PrintInt function code of the Test.exe file generated in the above example. If there are other programs that need to use the Add function, PrintInt function, it also needs to contain the code of these two functions. If these programs are run at the same time, then the code of these functions will be copied into the text segment of each running process, causing great waste. Memory is like a trash can in the kitchen, no matter how large it is, it is always not enough.
  2. It is inconvenient to maintain. If the static library is modified, all programs that depend on the modified static library must be recompiled and linked.

7. Dynamic Link

The dynamic link library is a modern product dedicated to solving the defects of the static library. A dynamic library is an object module, which can be loaded to any memory address when it is running or loaded and linked with a program in the memory. This process is called dynamic linking and is performed by a program called dynamic linker.
Dynamic libraries are widely used in Microsoft operating systems, and they are called DLLs.

7.1 Generate DLL

Now we use dynamic linking to call PrintInt and Add functions. Change the Configuration Type of the Calc and Printer projects to Dynamic Library. Compile the Printer, Calc, and Test projects respectively.

Project Printer build result:

1>------ Build started: Project: Printer, Configuration: Debug Win32 ------
1>Printer.cpp
1>Printer.vcxproj -> C:\Users\vincent.zheng\source\repos\Test\Debug\Printer.dll
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

Project Calc build result:

Error	LNK1120	1 unresolved externals	Calc	C:\Users\vincent.zheng\source\repos\Test\Debug\Calc.dll	1	
Error	LNK2019	unresolved external symbol "public: static void __cdecl Printer::PrintInt(int)" (?PrintInt@Printer@@SAXH@Z) referenced in function "public: int __thiscall Calculator::Add(int,int)" (?Add@Calculator@@QAEHHH@Z)	Calc	C:\Users\vincent.zheng\source\repos\Test\Calc\Calc.obj	1	

Project Test build result:

Error	LNK2019	unresolved external symbol "public: int __thiscall Calculator::Add(int,int)" (?Add@Calculator@@QAEHHH@Z) referenced in function _main	Test	C:\Users\vincent.zheng\source\repos\Test\Test\main.obj	1	
Error	LNK1120	1 unresolved externals	Test	C:\Users\vincent.zheng\source\repos\Test\Debug\Test.exe	1	
Error	LNK1120	1 unresolved externals	Calc	C:\Users\vincent.zheng\source\repos\Test\Debug\Calc.dll	1	
Error	LNK2019	unresolved external symbol "public: static void __cdecl Printer::PrintInt(int)" (?PrintInt@Printer@@SAXH@Z) referenced in function "public: int __thiscall Calculator::Add(int,int)" (?Add@Calculator@@QAEHHH@Z)	Calc	C:\Users\vincent.zheng\source\repos\Test\Calc\Calc.obj	1	

As expected, we got a lot of compilation errors again! But don't worry, let's look at them one by one. As can be seen from the above information, the Printer project has been compiled successfully and the DLL file has been generated in the Debug folder. The Calc and Test projects respectively have some errors that external symbols cannot be found. It is not surprising that the Test project cannot find the Add function, because the dll file of the Calc project has not been generated yet. But the dll file of the Printer project has been generated, but Calc still cannot find the PrintInt function?

7.2 DLL export symbols

In fact, only DLL files are not enough, we also need to export the symbols in the DLL. The export method is also very simple, just add it __declspec(dllexport)to the function declaration . In our project, we need to export the PrintInt function and the Add function. Therefore, we modify the source file as follows:

//Printer.h"
#pragma once
#define  EXPORT __declspec(dllexport)
class EXPORT Printer {
public:
     static void PrintInt(int value);
};

//Calc.h
#pragma once
#define EXPORT __declspec(dllexport)
class Calculator {
public:
    int EXPORT Add(int m, int n);
};

In this way, we exported the Printer class and the Calculator class, and no errors were reported in this compilation (remember to add dependencies). In the debug folder we found that Printer.lib, Printer.dll, Calc.lib, Calc.dll files were generated.

Guess you like

Origin blog.csdn.net/ww1473345713/article/details/108569238