一、背景
最近要对接省集采平台,把医院的药品计划数据上传到省集采平台,收到给的dll动态链接库和tlb静态链接库如下
二、探索之路
对于一个java开发人员,我完全不知道这俩文件是干啥的,一脸懵逼,开始百度,中间爬过不少坑,网上大部分是通过jna或jni对dll进行解析获取方法,但是对于c#编辑的dll文件,java不能直接读取里面内容,需要通过c++读取里面内容,然后java通过jna读取c++里面方法,调用方向见下图
三、上代码
1. c#代码查看
c#编译后的dll文件是一个64位的动态链接库,所以你需要转变64位jdk环境
第三方c#代码给的是一个service类implements另一个接口类,通过ILSPY(最新下载地址:https://github.com/icsharpcode/ILSpy)软件打开dll链接库,如下图,有两个类,类里有具体方法,本文采用下面第一个方法测试
2. 创建c++空项目
因为c#编译的文件已经给我,本文就不再阐述用创建c#项目过程,直接创建c++项目
通过visutal studio 2019 preview创建c++项目,依次按照下图创建项目
创建好项目后,软件右侧会刷新出项目结构,因为项目是空的,所以文件夹下内容全部无文件
2. 创建读取c#类入口文件
创建文件入口,按如下图操作
编写代码,引入第三方dll链接库
#ifdef MYLIBAPI
#else
#define MYLIBAPI extern "C" __declspec(dllimport)
#endif
### 声明构造方法
MYLIBAPI char* GetSignature(char* userID, char* password, char* sha1);
### 通过绝对路径引入dll
#using "D:\qlyl\SaleSec.dll"
### 引入命名空间,SaleSec为dll链接库名称
using namespace SaleSec;
using namespace System;
### 定义方法,这里和c#编译的dll链接库方法名称一致
char* GetSignature(char* userID, char* password, char* sha1)
{
### 声明一个对象,通过对象调用方法
### SaleService方法对应c#中dll链接库的方法名称
SaleService^ saleservice = gcnew SaleService();
String^ userID1 = gcnew System::String(userID);
String^ password1 = gcnew System::String(password);
String^ sha11 = gcnew System::String(sha1);
String^ result = saleservice->GetSignature(userID1, password1, sha11);
char* results = (char*)(void*)System::Runtime::InteropServices::Marshal::StringToCoTaskMemAnsi(result);
return results;
}
3. 创建文件时,错误信息处理
-
第一个错误,如下图,需要对公共语言进行时进行设置,右键项目→属性→高级→公共语言进行时,设置为公共语言运行时(/clr),应用
c/c++ → 公共语言进行时,应用→确定,页面则不报错了
因为我dll类库需要64位版本,所以还需要进行如下设置
设置后页面则不报错了
2. 第二个错误,如下,这个错误一般是输入参数类型或输出参数类型不匹配导致的,我这里是因为c#是string类型的,传递了char* 类型导致错误
E1767 无法使用给定参数列表调用 函数 "SaleSec::SaleService::GetSignature" SaleSec D:\visio-workspace\SaleSec\dllmain.cpp 23
3. 第三个错误, LNK1104,找不到某个dll文件
我的这个错误是因为c++环境没有安装全导致的,重新安装下环境就好了,安装环境过程
我是把所有包含c++内容的都给添加上了
右键项目→属性→c/c++→语言→符合模式,选择否
4. 启动项目生成c++,dll动态链接库
点击上方本地windows调试器,生成dll文件,中间出了一个错误,不影响dll生成,这里不做描述
5. 第四个错误,错误信息如下,修改如下图
C2338 C++/CLI、C++/CX 或 OpenMP 不支持两阶段名称查找;请使用 /Zc:twoPhase- saleSec D:\visio-workspace\saleSec\c1xx 1
6. 第五个错误,启动报错,如下图,需要把c#编译的dll链接库也放在jdk/bin目录下
7. 其他错误官网查询解决,https://docs.microsoft.com/zh-cn/cpp/error-messages/tool-errors/linker-tools-error-lnk1104?view=vs-2017
四、java通过jna调用c++生成的动态链接库
-
pom文件引入依赖
<!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna --> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>5.5.0</version> </dependency> <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna-platform --> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna-platform</artifactId> <version>5.5.0</version> </dependency>
-
新建测试类,dll文件放在jdk安装目录 bin目录下
package oms; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.Platform; import com.sun.jna.win32.StdCallLibrary; import org.junit.Test; import java.util.UUID; /** * @author insistOn * @Title: * @Package * @Description: * @date 2020/4/279:46 */ public class DllTest { private static final String USERID= "aaa"; private static final String PASSWORD= "w23wer"; private static final String DESKEY= "235424rwerw"; private static final String SHA1= "12312"; /** * 测试电脑上msvcrt.dll文件 * * @Description: * @author: liufan * @date: 2020年5月1日 上午10:37:58 */ public interface msvcrt extends StdCallLibrary { // DLL文件默认路径为项目根目录,若DLL文件存放在项目外,请使用绝对路径 msvcrt INSTANCE = (msvcrt) Native.load((Platform.isWindows() ? "msvcrt" : "c"), msvcrt.class);// 加载动态库文件 // 声明将要调用的DLL中的方法(可以是多个方法) void printf(String format, Object... args); void printf_s(String format, Object... args); } /** * SaleSec * * @Description: 读取调用StdCall方式导出的DLL动态库方法 * @author: LinWenLi * @date: 2018年7月18日 上午10:37:58 */ public interface StdCallDll extends StdCallLibrary { // DLL文件默认路径为项目根目录,若DLL文件存放在项目外,请使用绝对路径 StdCallDll INSTANCE = (StdCallDll) Native.load((Platform.isWindows() ? "saleSec" : "c"), StdCallDll.class);// 加载动态库文件 // 声明将要调用的DLL中的方法(可以是多个方法) String GetSignature(String userId, String password, String SHA1); } /** * DLL动态库调用方法2 * * @Description: 读取调用Decl方式导出的DLL动态库方法 * @author: LinWenLi * @date: 2018年7月18日 上午10:49:02 */ public interface CLibrary extends Library { // DLL文件默认路径为项目根目录,若DLL文件存放在项目外,请使用绝对路径 CLibrary INSTANCE = (CLibrary) Native.load(Platform.isWindows() ? "saleSec" : "c", CLibrary.class); // 声明将要调用的DLL中的方法(可以是多个方法) String GetSignature(String format, String PASSWORD, String SHA1); } @Test public void test(){ System.out.println(StdCallDll.INSTANCE.GetSignature(USERID, PASSWORD, SHA1)); // System.out.println(CLibrary.INSTANCE.GetSignature(USERID, PASSWORD, SHA1)); } }
-
运行测试类,输出如下参数
五、大功告成,项目正确引入
参考文章:
https://www.cnblogs.com/wyongbo/p/jnaTest.html#
https://www.cnblogs.com/zhijian233/p/10752062.html
https://blog.csdn.net/weixin_34401479/article/details/92559052