dll动态库生成与调用(1):生成dll动态库、C程序调用动态库
dll动态库生成与调用(2):Java程序利用JNI、JNA调用动态库
文章简介
本文介绍了使用C语言去生成一个动态库,并使用C程序去调用这个动态库。
当然,你可能想用C++来创建自己的调用库,按照文中的步骤直接调用肯定是会出现问题的,这是因为在C++中,为了面向对象中的重载等特性,会在编译中将函数的名字做相应修改,你可以将要调用的内容采用extern "C"
进行修饰。
extern "C"
有两层含义:
第一,被它限定的函数或变量是extern类型的。
第二,被它修饰的变量和函数是按照C语言方式编译和连接的。
开发环境
IDE: Visual Studio 2015、IDEA 2019.1
JNA(Java库): 3.5.2
JDK: 1.8
文件目录结构
C:
JNI_test项目中的dll.h、dll.c
用于生成dll动态库文件,JNI_test_call项目中的call.c
用于调用dll库。
注: 如果是VS新手,建议这两个项目不要放在同一个解决方案中,为这两个项目各创建一个解决方案,能避开一些VS使用上的问题。
一、动态库的生成
注: 在项目JNI_test中进行操作。
1.新建项目,用于生成dll库
在VS中新建项目:Win32控制台应用程序->控制台应用程序/DLL->空项目
我一般是在控制台应用程序中进行函数调试,再调VS选项进行DLL生成,这样比较方便,如果你直接选择DLL,就不需要调设置了。
设置在 项目属性页 的 常规->配置类型 中,将配置类型从 “.exe” 改为 “.dll” 即可,如下所示:
不一定需要空项目,如果选择创建的不是空项目,会默认带几个文件,没影响。
2.编写动态库的.h头文件、.c源文件
dll.h
// 宏定义
#define C_API _declspec(dllexport)
// 在DLL中,将被调用的函数
C_API int start(char *);
第2行定义一个宏,用C_API
替代_declspec(dllexport)
。当然,可以不定义宏,直接在函数前面写_declspec(dllexport)
也是没问题的。
第4行声明一个函数,将在dll.c中进行实现。
dll.c
#include<stdio.h>
#include"dll.h"
C_API int start(char *string) {
printf("\n\n");
printf("=========DLL out start=========\n");
printf("call print: %s\n",string);// 打印传过来的字符传
printf("=========DLL out end=========\n\n");
return 0;
}
第2行,别忘了包含头文件。
3.生成动态库文件
在VS的“生成”菜单中进行生成,会产生和项目名同名的.dll
和.lib
文件,库的生成就完成了。
二、在C语言程序中调用dll动态库
在项目JNI_test_call中进行操作。本文写了2种调用dll的方法。
推荐第二种调用方法。
1.第一种调用方法:仅需要.dll文件(“显式链接”)
注意: 本调用方法采用的是动态库的显式链接。(可以看文章末尾 补充阅读第5项)主要需要2个Windows的API:LoadLibrary
和GetProcAddress
。
1.C语言库代码:
call.c
#include<stdio.h>
#include<windows.h> // 用于库调用函数
#include<tchar.h> // 用于_T等编码转换宏
int main() {
HINSTANCE hDll;
int(*startCall)(char *);// 函数指针,用于存放dll中要调用的函数
LPCWSTR szString = _T("JNI_test.dll");// 将char字符串转换为LPCWSTR格式,不然无法找到dll文件
// 加载库(有2种加载方式)
//hDll = LoadLibraryEx("E:/JNI_test/JNI_test.dll", NULL, LOAD_WITH_ALTERED_SEARCH_PATH);// 库的绝对路径
hDll = LoadLibrary(szString);// 将dll移到当前程序目录下,可以直接用dll名替代
if (hDll == NULL) { // 库加载失败
printf("%s", "failed to load dll!\n");
}
else { // 库加载成功
// 获取start函数的内存地址
startCall = GetProcAddress(hDll,"start");
// 利用函数指针调用函数
startCall("hello, I'm JNI_test_call!");
}
FreeLibrary(hDll);
return 0;
}
_T
宏 用于将char字符串转换成LPCWSTR,详细说明可以看 本文末尾的 错误记录第1.2和1.3 或者 参考文章第8项。
2.dll文件的调用路径:
在调用程序生成目录下(产生.exe
的目录),放入我们要调用的.dll
文件,并不需要.lib
文件。
2.第二种调用方法:需要.h,.lib,.dll文件(推荐此方法)
1.C语言库的调用程序代码:
call.c
#include<stdio.h>
#include"dll.h"
//#pragma comment(lib, "JNI_test.lib")
int main() {
// 当做普通行数调用即可
start("hello,I'm JNI_test_call!");
return 0;
}
2.配置.h
头文件:
再在call.c
中添加对动态库头文件的引用:
#include"dll.h"
3.配置.lib
、.dll
文件:
配置lib和dll文件所在目录:
配置需要调用的lib文件:
注意: “附加依赖项“可以在call.c代码中添加#pragma comment(lib, "JNI_test.lib")
来进行代替。
4.运行项目:
最后在VS中运行本项目即可,结果如下:
三、附加内容
1. 错误记录
1.1 LNK2019 LNK1120 无法解析的外部符号 “xxxx1”" ,该符号在函数 xxxx2 中被引用
问题的原因在于,找不到被我们引用的函数的声明。
xxxx1
是我们要引用的函数,函数的声明和实现在其它的文件中。
xxxx2
是使用xxxx1
的函数。举个例子:如果我们在main()
中使用start()
,如果出现此错误,那么会报无法解析的外部符号 “start”" ,该符号在函数main中被引用
解决方法:
-
检查函数名是否书写正确,确认一下你包含的头文件中是否有函数的声明
-
检查头文件配置是否正确
1.2 warning C4133: “函数”: 从“char [xx]”到“LPCWSTR”的类型不兼容
第一种解决方法:(不推荐)
在项目的属性页中找到 常规->字符集:把 “使用 Unicode 字符集” 改为 “使用多字节字符集”,如下所示:
第二种解决方法:
使用_T
宏,对char字符串进行转换,关键代码如下:
需要包含头文件:#include<tchar.h>
LPCWSTR szString = _T("JNI_test.dll");// 字符串转换
hDll = LoadLibrary(szString);// 加载库
用这种方法,就不需要改变项目所使用的字符集。
1.3 无法加载库
主要的原因&解决方法:
-
库的路径错误,查看库是否存在以及库的路径是否在我们配置的地方。
-
显式加载库中的字符串编码格式不符合调用要求,可以参考本文中错误记录的第1.2项,进行处理。
1.4 调整VS项目属性页,但却没有效果
这个问题在以前的文章中有提到过,VS设置如果没有效果,那么应该先检查 VS项目配置页 是否和 当前项目生成的配置 相符,如下所示:
如果并不是上面的设置的问题,那么你可能是你改动的设置对你要解决的问题没有效果。对此的建议是,将VS的错误提示再多在网上进行查询,找到问题的,如果问题查询不到,你可以将提示中的关键词抽取出来,列在搜索框中进行查询。另外,项目中的警告,也应当注意,如果有心有余力的话,不妨将警告也解决掉吧。
2. 参考文章
- VS 编写c++dll库文件(推荐阅读,有讲到本文章中的一些基础知识)
- c语言调用dll文件
- c语言怎么调用dll文件?
- LoadLibrary加载动态库失败的思考
- loadlibrary()失败的问题
- warning C4133: “函数”: 从“char [5]”到“LPCWSTR”的类型不兼容
- C语言创建动态dll和调用dll(visual studio 2013环境下)
- 在vs中char类型的实参与LPCWSTR类型的形参类型不兼容怎么解决?(推荐阅读,内容提及:UNICODE和ASCII码之间的关系、LPCTSTR是什么)
3. 补充阅读
- #pragma_百度百科
- #ifndef的用法(可使C++中的.h头文件只被包含一次)
- extern和extern “C”(如果使用C++,则需要了解)
- dllMain函数的作用(这个函数可以不写,如果没有,会自动调用缺省的dllMain)
- 使用LoadLibrary调用DLL(文中有介绍“显式链接”和“隐式链接”)
- 关于“Error: “const char *” 类型的实参与 "LPCWSTR"类型的形参不兼容”错误的解决方案(推荐阅读,内容提及:字符串编码格式的介绍)