环境要求
新建一个空白项目,可以是exe的,也可以直接是dll的,也可以是啥都没有的空项目,推荐创建空项目,项目创建好以后进行配置,共两步
第一步,打开项目属性
第二步,设置配置类型为动态库dll,并确定
编写dll库函数
test1.c
_declspec(dllexport) int test(int a, int b)
{return a + b + b;
}
test1.h
#pragma once
_declspec(dllexport) int test(int a, int b);
test2.c
_declspec(dllexport) int minus(int a, int b)
{return a - b;
}
test2.h
#pragma once
_declspec(dllexport) int minus(int a, int b);
常见问题和错误说明
声明,下述结论以测试工程为例: 1. 编写dll库不需要main函数,直接写函数即可,参考工程见下图1;2. 生成的dll文件保存路径如下:H:\Test\x64\Debug;3. 生成成功的标志(图2),只要输出窗口中显示1成功即可,不用理会白色的报错;4. 如果输出窗口中没有字(图3),那么前往 H:\Test\x64\Debug;把里面所有文件删了再重新编译运行即OK;5. 如果上述都不行,那么点击 生成 --> 重新生成解决方案;6. 编写函数时,一定要加上 _declspec(dllexport) ,不然dll文件可以正常生成,但是Python读取不了;7. 只写.c文件,不写.h文件,生成的dll文件python一样可以调用,不建议这么干;8. lib和dll文件详细说明后附;9. 加上 _declspec(dllexport) ,会生成 lib 文件和 dll 文件,不加只会生成dll文件;10. 如果工程下存在多个.c和.h文件,里面都存放了函数,随便点哪里编译运行即可,生成的dll文件会包含所有.c的函数;11. 无法看到dll文件中有哪些函数,想看的话可以通过小工具 dll export viewer;12. 有时候头文件中会通过 extern "C" _declspec(dllexport) int minus(int a, int b); 来创建函数,是因为如果按照C的方式来创建,在dll经过编译器后函数名不会变,否则会被编译器修饰,例如C++调用dll时。
Python调用dll文件
Python调用dll时,只需要dll文件
import ctypesa = ctypes.CDLL('Test.dll')
ret = a.test(1,2)
ret1 = a.miuns(1,2)print(ret, ret1)
C语言调用dll文件
-
静态调用
1. 静态调用需要lib文件和dll文件,在程序一开始执行时就加载这个文件。2. 在调用前,先把lib和dll拷贝到当前工程文件夹,不然提示找不到dll,见下图。3. 在下面示例中,我没有声明 test 函数,但是它依然可以正常调用,是因为C会假设它存在,并执行相应操作,但是这种方式不可以取,在调用时,还是显式声明一下。
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>#pragma comment(lib, "H://Test//x64//Debug//Test.lib") // 通过关键字导入静态文件路径
_declspec(dllimport) int minus(int a, int b); // 声明导入函数,注意这里是import
int main() {int a = minus(5, 6);int b = test(5, 6);printf("%d", a);printf("%d", b);
}
-
动态调用
1. 动态调用是比较常用的一种方法。2. dll文件可以通过直接指定路径的方式来添加。3. 动态调用是什么时候用dll,什么时候再加载dll。4. 流程: 实例一个句柄 --> 创建指向函数地址的指针 --> 获取函数入口地址 --> 使用函数
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>int main()
{HINSTANCE h = LoadLibrary(L"H://Test//x64//Debug//Test.dll"); // 实例化句柄 typedef int (*p_func)(int a, int b); // 创建一个指向函数地址的指针p_func testfunc = (p_func)GetProcAddress(h, "test"); // 获取函数入口地址int a = testfunc(5, 6);printf("%d", a);return 0;
}
dll和lib的详细介绍
lib 分为两种
- 第一种包含了函数所在的dll文件和文件中函数入口,具体的代码由运行时dll来提供。
- 第二种时lib包含了代码本身,在编译时直接把代码加入到程序中。
区别
- lib是编译时才用,dll是运行时才用。
- 完成源代码编译只需lib,程序要跑起来需要用到dll。
- 如果存在dll文件,那么lib中一般是一些索引信息,记录函数的入口和位置,dll中是具体内容。
- 如果只有lib,那么这个lib是静态编译出来的,索引和代码都在里面。
- 如果使用静态编译的lib,那么编译时程序会全部加载到项目中,会使应用程序超级大,并且失去了动态库的灵活性,发布新版本需要发布新的应用程序。
- 如果不想使用lib文件,可以同win32 API提供的Loadbriary,GetProcAddress来直接调用dll。
注意事项
- 头文件中包含lib中说明输出的符号和数据结构,应用程序调用lib时,需要将头文件包含进去。
- 包含了dll中说明输出的符号和数据结构的.h文件,应用程序调用dll时,应该将.h文件引入应用程序的源文件中。
开发成功后的应用程序在发布时,只需要.exe和.dll文件,并不需要.lib和.h文件。