java通过c++读取 c# 动态链接库(dll)内容

1 篇文章 0 订阅

一、背景

最近要对接省集采平台,把医院的药品计划数据上传到省集采平台,收到给的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. 创建文件时,错误信息处理

  1. 第一个错误,如下图,需要对公共语言进行时进行设置,右键项目→属性→高级→公共语言进行时,设置为公共语言运行时(/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++→语言→符合模式,选择否

 

官网其他错误处理,https://docs.microsoft.com/zh-cn/cpp/error-messages/tool-errors/linker-tools-error-lnk1104?view=vs-2017

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++生成的动态链接库

  1. 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>

     

  2. 新建测试类,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));
        }
    ​
    }
    ​

     

  3. 运行测试类,输出如下参数

 

五、大功告成,项目正确引入

参考文章:

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

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值