动态链接库(Dynamic Link Library)
关于动态链接库
链接
大多数高级语言都支持分别编译(compiling),程序员可以显式地把程序划分为独立的模块或文件,然后由编译器(compiler)对每个独立部分分别进行编译。在编译之后,由链接器(Linker)把这些独立编译单元链接(Linking)到一起
静态链接
在程序开发中,将各种目标模块(.OBJ)文件、运行时库(.LIB)文件,以及已编译的资源(.RES)文件链接在一起,以便创建Windows的.EXE文件,这种方式是静态链接
动态链接
在程序运行时,Windows把一个模块中的函数调用链接到库模块中的实际函数上的过程
二者区别
- 静态链接会将相关的程序全部打包到最终的可执行文件中
- 静态链接不会将程序打包到可执行文件中,而是在.exe执行时调用独立的.dll文件
dll地狱
- dll地狱指因为系统文件被覆盖而让整个系统崩溃的现象
- 例如多个系统程序依赖某个dll模块,而又有一些程序依赖该dll模块的新版本,而这个dll模块的新版本又不向下兼容,那么当你更新该dll模块后,则多个系统程序将无法工作
- .Net平台中允许一个dll的多个版本同时在同一台机器上工作,从而解决了这一问题,该技术成为
Side-by-Side
[1]
Windows中主要的dll
名称 |
功能 |
KERNEL32.DLL |
低级内核函数,用于内存管理、任务管理、资源控制等 |
USER32.DLL |
windows管理有关的函数,消息、菜单、光标、计时器、通信,钩子等 |
GDI32.DLL |
图形设备接口库 |
ODBC32.DLL |
ODBC功能 |
Ws2_32.dll |
socket通信功能 |
dll中函数输入/输出参数(c#)
其中,ref和out类似,但是ref需要自己赋初值,而out由dll负责赋值
dll的引用计数
- DLL在内存中只有一个实例,系统为每个DLL维护一个线程级的引用计数,一旦一个线程载入了该DLL,引用计数将会加1。而程序终止或者引用计数变为0(仅指运行时动态链接库),DLL就会释放占用程序的虚地址空间。
Windows的虚地址映射
- Windows将提供内部的地址映的工作,例如一个DLL文件被加载后在物理内存中只占一个固定区域,有多个进程使用同一个DLL文件,Windows将这个DLL的内存地址空间通过地址映射后提供给各个进程,进程代码地址与DLL映射后地址构成的是进程的虑地址空间,进程在自己的虚地址空间中好像是自己独自在使用这个DLL文件,使用DLL中的函数与程序自身的函数没有区别。
C#创建DLL类库(托管的动态链接库)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| namespace csTestDLL { public class csDLLClass { double Pie = Math.PI;
public double PIE { get => Pie;}
public string say_hello() { return "Hello! This is csTestDLL."; }
private double calc_power_two(double r) { return r * r; }
public double calculate_circle_area(double r) { return Math.PI * calc_power_two(r); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| Assembly csdll = Assembly.LoadFrom("csTestDLL.dll");
public void say_hello_cs() { foreach (Type t in csdll.GetTypes()) { if (t.IsClass && !t.IsAbstract) { ConstructorInfo[] ci = t.GetConstructors(); MethodInfo[] mis = t.GetMethods(); callback("构造函数:" + ci[0].ToString(), listname); foreach (var item in mis) { callback("pulic函数:" + item.ToString(), listname); } mis = t.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance); foreach (var item in mis) { callback("private函数:" + item.ToString(), listname); } MethodInfo mi = t.GetMethod("say_hello"); object obj = Activator.CreateInstance(t); string msg = mi.Invoke(obj, null) as String; callback("welcome:" + msg, listname); callback(split_line, listname); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void calc_circle_area(double r = 1) { foreach (Type t in csdll.GetTypes()) { if (t.IsClass && !t.IsAbstract) { Type[] p = new Type[0]; ConstructorInfo ci = t.GetConstructor(p); object o = ci.Invoke(null); MethodInfo mi = t.GetMethod("calculate_circle_area"); object[] para = new object[1] { r }; string msg = mi.Invoke(o, para).ToString(); callback("圆面积:" + msg, listname); callback(split_line, listname); } } }
|
C++非托管动态链接库
头文件类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include "myHead.h" #include "pch.h"
int __stdcall factorial(int n) { int ans = 1; while (n > 1) { ans *= n; n--; } return ans; }
|
- 关于__stdcall[2]
- __stdcall是微软特有的修饰符
- 它表示对这个程序要按照约定的方式调用,会将所有函数当做winapi调用
1 2 3 4 5 6 7 8
| #pragma once
extern "C" __declspec(dllexport) int __stdcall factorial(int n);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
[DllImport("../../../debug/cppTestDLL.dll")] private static extern Int32 factorial(int n);
public void calc_factoria(int num) { callback("cppDLL开始调用", listname); Int32 ans = factorial(num); callback(num + "! = " + ans.ToString(), listname); callback(split_line, listname); }
|
使用def
1 2 3 4 5
| LIBRARY library
EXPORTS factorial
|
注意
1 2 3 4 5
| [ DllImport( "kernel32.dll",EntryPoint="GetVersionEx" )]
|