我们将讨论:
- 如何构建DLL
- 如何在项目中使用DLL
- DLL里面有什么
- 显式(Explicit)和隐式(implicit)链接
- 当你加载DLL时发生了什么
- 如何诊断DLL加载失败
- 指定DLL输出(export)内容的各种方法
- 数据导出
- 延迟加载
- C++和DLLs
- 线程和TLS
- DLL地狱(Hell)
我们不讨论:
- 其他平台上的动态库或共享对象
- .Net 程序集或其他.Net相关主题
- 资源DLL
什么是动态库:
- …它包含代码和数据。
- …它能在程序运行时动态加载。
- …可以被多个程序共享或重用。
大多数动态库的扩展名都是 “.dll”
使用DLL有什么好处
多个程序可以共享代码和数据,而不需要每个程序都有自己的副本
- 可以节省磁盘空间的使用
- 可以节省内存的使用
可以在运行时决定是否加载某个功能
- 也许你不总是需要一些功能
- 也许你想支持开放式扩展(例如插件)
可维护性
- 通过DLL实现组件化
- 提高了可服务性(bug修复,安全补丁等)
- 提高了可维护性
使用DLL的缺点
软件发行变得更复杂
- 如果你将所有的东西放进单个EXE中,那么安装起来就很容易
增加了不兼容的可能性(DLL Hell)
不可能跨DLL边界优化代码
- 每一次跨DLL边界的调用必须是间接的
- (但是使用DLL也有性能上的好处)
创建一个小的DLL
// Hello.cpp
extern "C" char const* __cdecl GetGreeting()
{
return "Hello, C++ Programmers!";
}
使用VS构建工具进行编译和链接,我们使用/c选项告诉编译器只进行编译
使用/DLL选项告诉链接器不要构建exe(默认选项)而是构建一个dll, 使用/NOENTRY选项使得链接时去掉一些不必要的东西,简化导出的dll,使用/EXPORT选项告诉链接器“GetGreeting”是DLL中公开的接口
F:\learning\VsProject\dllproject>cl /c Hello.cpp
Hello.cpp
F:\learning\VsProject\dllproject>link Hello.obj
/DLL
/NOENTRY
/EXPORT:GetGreeting
正在创建库 Hello.lib 和对象 Hello.exp
这样我们就得到了Hello.dll,接下来我们使用这个动态库
// PrintGreeting.cpp
#include <Windows.h>
#include <stdio.h>
int main()
{
HMODULE const HelloDll = LoadLibraryExW(L"Hello.dll", nullptr, 0);
// char const* __cdecl GetGreeting();
using GetGreetingType = char const* (*__cdecl)();
GetGreetingType const GetGreeting =
reinterpret_cast<GetGreetingType>(GetProcAddress(HelloDll, "GetGreeting"));
puts(GetGreeting());
return 0;
}
编译并运行这个cpp,我们看到这个程序正常工作,并且输出了“Hello, C++ Programmers!”
F:\learning\VsProject\dllproject>cl PrintGreeting.cpp
/out:PrintGreeting.exe
PrintGreeting.obj
F:\learning\VsProject\dllproject>PrintGreeting.exe
Hello, C++ Programmers!
参考链接:
https://nullprogram.com/blog/2018/11/21/
https://www.youtube.com/watch?v=JPQWQfDhICA