Java多进程调用dll程序和exe程序

🎯导读:本文介绍了使用Java调用本地DLL及EXE程序的方法。针对DLL调用,文章提供了基于Java Native Access (JNA) 库的具体实现方案,包括定义Java接口以映射DLL中的函数,并展示了如何加载DLL及调用其中的方法。对于EXE程序的调用,则提出了一种通过批处理文件(BAT)启动外部可执行文件的方式,并通过轮询检查结果文件的存在来判断计算是否完成。此外,还探讨了使用ProcessBuilder启动独立进程来运行DLL调用程序DllRunner.jar,以及如何处理子进程的输入输出流以避免阻塞。文中还提到了在不同JDK版本间编译与运行时可能遇到的兼容性问题及其解决方案。

文章目录

  • Java调用DLL程序
    • jna介绍
    • 依赖
    • 编写接口
    • 使用
  • DLL错误导致java进程退出
    • 处理方式
      • **在C++代码中加强错误处理**
      • **使用守护进程(Watchdog)**
      • Java**使用分离的进程调用DLL**
        • java调用dll的程序打包成`DllRunner.jar`
        • 使用Process新开进程调用`DllRunner.jar`
  • Java调用exe

Java调用DLL程序

jna介绍

Java Native Access (JNA) 是一个Java开发库,它允许Java程序直接调用本地操作系统API和C语言库。JNA库通过JNI (Java Native Interface) 进行工作,但是它不需要编写任何C代码或者创建本机库。

依赖

<dependency><groupId>net.java.dev.jna</groupId><artifactId>jna</artifactId><version>5.14.0</version>
</dependency>

编写接口

在这里插入图片描述

编写接口的目的是对应dll的方法,dll的方法如下

在这里插入图片描述

import com.sun.jna.Library;
import com.sun.jna.Native;import java.io.File;/*** @Author dam* @create 2024/7/2 15:24*/
public interface AlgorithmDll extends Library {String dllPath = System.getProperty("user.dir") + File.separator + "algorithmDll" + File.separator + "GDUT_PACK.dll";/*** 对接C++程序的方法* @param input 输入* @return 输出*/String RunDLL(String input);public static AlgorithmDll getInstance() {return Native.loadLibrary(dllPath, AlgorithmDll.class);}
}

使用

调用dll的方法

output = AlgorithmDll.getInstance().RunDLL(input);

下面方法的作用是接收一个计算任务,然后调用dll程序进行计算,最后接收dll程序的输出进行返回

public static CIMSTask calculate(CIMSTask cimsTask) {System.out.println(cimsTask.getNestTaskCode() + "开始计算");String input = JSON.toJSONString(cimsTask);String output;try {TxtUtil.write(new File(inputPath + "input" + cimsTask.getNestTaskID() + ".json"), input, "utf-8");output = AlgorithmDll.getInstance().RunDLL(input);if ("".equals(output)) {throw new RuntimeException(cimsTask.getNestTaskCode() + "计算失败");}System.out.println(cimsTask.getNestTaskCode() + "计算完成");try {TxtUtil.write(new File(outputPath + "output" + cimsTask.getNestTaskID() + ".json"), output, "utf-8");} catch (Exception e) {throw new RuntimeException(e);}} catch (Exception e) {throw new RuntimeException(e);}return JSON.parseObject(output, CIMSTask.class);
}

上述代码就可以实现DLL的简单调用,但是如果要多线程调用该方法同时计算多个任务的话,会有风险,请继续阅读下一小节

DLL错误导致java进程退出

当C++的DLL在Java中调用时,如果DLL发生了严重错误导致进程退出(例如PROCESS EXIT(-1)),Java虚拟机(JVM)也会随之中断。这种情况通常是由于未捕获的异常或访问无效内存等导致的C++崩溃。由于JVM和C++共享同一个进程空间,如果C++导致整个进程崩溃,Java程序也会被迫退出。

如果多个任务在同时计算,一旦一个任务发生了错误中断,会导致所有任务都被迫中断。

处理方式

在C++代码中加强错误处理

首先,确保C++代码中所有可能引发异常或导致崩溃的地方都进行了适当的错误处理。例如,使用try-catch块捕获所有异常并妥善处理,避免异常传递到Java层导致进程崩溃。

使用守护进程(Watchdog)

编写一个守护进程监控Java应用程序,如果发现Java程序因C++崩溃而终止,可以自动重新启动它。虽然这并不能防止崩溃,但可以提供一种恢复机制。

Java使用分离的进程调用DLL

通过将DLL调用放在一个单独的进程中运行,主Java程序通过IPC(进程间通信)与这个进程进行通信。如果DLL进程崩溃,主Java程序不会受到影响。

java调用dll的程序打包成DllRunner.jar

调用dll程序如下:

import com.sun.jna.Native;
import com.sun.jna.Library;import java.io.*;/*** @Author dam* @create 2024/8/26 10:17*/
public class DllRunner {public interface AlgorithmDll extends Library {String dllPath = System.getProperty("user.dir") + File.separator + "algorithmDll" + File.separator + "GDUT_PACK.dll";public static AlgorithmDll getInstance() {return Native.loadLibrary(dllPath, AlgorithmDll.class);}String RunDLL(String input);}public static void main(String[] args) throws UnsupportedEncodingException {BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));PrintWriter writer = new PrintWriter(new OutputStreamWriter(System.out, "UTF-8"), true);try {// 从标准输入读取输入数据String input = reader.readLine();// 调用DLL函数String output = AlgorithmDll.INSTANCE.RunDLL(input);// 将结果输出到标准输出writer.println("RESULT:" +output);} catch (Exception e) {// 处理异常并输出到标准错误System.err.println("Error: " + e.getMessage());e.printStackTrace(System.err);System.exit(-1);}}
}

jna-5.14.0.jar和DllRunner程序放在一个包下面

在这里插入图片描述

执行如下命令,将程序打包

javac -classpath jna-5.14.0.jar -d . DllRunner.javajar cf DllRunner.jar -C . com/

执行报错,因为有的字符没有响应的编码,直接把注释改成英文就行,或者直接删除注释

在这里插入图片描述

打包成功之后的目录如下:

在这里插入图片描述

打包之后,可以使用命令jar tf DllRunner.jar来查看jar包里面的结构

在这里插入图片描述

如果运行子进程之后报下面这种错误,意思是使用了更高版本的JDK来编译了程序,但是运行程序的时候使用的JDK版本较低,会出现版本不兼容问题。我的电脑安装了多个JDK,默认使用JDK17来编译了代码,但是后面运行程序使用的是JDK8,所以出现了如下报错

在这里插入图片描述

解决方法很简单,在编译代码的时候,使用--release 版本号来指定就行

javac --release 8 -classpath jna-5.14.0.jar -d . DllRunner.java
jar cf DllRunner.jar -C . com/

关于javac的命令,可以在命令行用javac查看

在这里插入图片描述

使用Process新开进程调用DllRunner.jar

我们在主线程中启动了一个新的线程来处理子进程的输出流。由于 ProcessBuilder 中的流是阻塞的,会使用一个缓冲区来存储这些输出,如果子进程的输出数据量占满了缓冲区,可能会导致线程挂起或阻塞,需要使用下面的代码,将dll的输出流合并到java中

// 合并标准输出和错误输出
builder.redirectErrorStream(true); 

最终的调用DLL的方法如下

public static CIMSTask calculateWithDll(CIMSTask cimsTask) {System.out.println(cimsTask.getNestTaskCode() + " 开始计算");String input = JSON.toJSONString(cimsTask);AtomicReference<String> output = new AtomicReference<>();try {// 将输入数据写入文件
//            TxtUtil.write(new File(inputPath + "input" + cimsTask.getNestTaskID() + ".json"), input, "utf-8");// 使用ProcessBuilder启动DllRunnerString jarPath = System.getProperty("user.dir") + File.separator + "process" + File.separator + "DllRunner.jar;" +System.getProperty("user.dir") + File.separator + "process" + File.separator + "jna-5.14.0.jar";System.out.println("jarPath:" + jarPath);List<String> command = Arrays.asList("java","-cp",jarPath,"com.dam.algorithm.DllRunner");ProcessBuilder builder = new ProcessBuilder(command);// 合并标准输出和错误输出builder.redirectErrorStream(true);Process process = builder.start();// 向进程的标准输入写入任务数据OutputStream outputStream = process.getOutputStream();PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream, "UTF-8"), true);writer.println(input);// 确保所有数据都被写入writer.flush();// 处理子进程输出和错误流new Thread(() -> {try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {String line;while ((line = br.readLine()) != null) {// 将子进程的输出打印到主进程运行窗口if (line.contains("RESULT:")) {output.set(line.split("RESULT:")[1]);} else {System.out.println("任务:" + cimsTask.getNestTaskCode() + "的子进程输出:" + line);}}} catch (IOException e) {e.printStackTrace();}}).start();// 等待进程结束int exitVal = process.waitFor();if (exitVal == 0) {System.out.println("子进程运行成功");} else {System.err.println("子进程运行出错,错误码: " + exitVal);}if (output.get() == null || output.get().isEmpty()) {throw new RuntimeException(cimsTask.getNestTaskCode() + " 计算失败");}System.out.println(cimsTask.getNestTaskCode() + " 计算完成");} catch (Exception e) {throw new RuntimeException(e);}CIMSTask cimsTaskRes = JSON.parseObject(output.get(), CIMSTask.class);cimsTaskRes.setNestTaskCode(cimsTask.getNestTaskCode());return cimsTaskRes;
}

Java调用exe

上面的调用方法比较麻烦,需要先打包才能调用,也可以通过调用exe来执行c++程序。调用逻辑是先生成一个bat文件,在bat文件中定位到exe程序所在位置,然后执行exe。"> " + cimsTask.getNestTaskCode() + "_output.log 2>&1"的作用是把exe的输出转移到日志文件中,防止exe进程陷入阻塞状态

/*** 调用exe来求解* 通过自旋来判断任务是否计算完成** @param cimsTask* @return*/
public static CIMSTask calculateWithExe(CIMSTask cimsTask) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println(cimsTask.getNestTaskCode() + " 开始计算,时间:" + sdf.format(new Date()));String input = JSON.toJSONString(cimsTask);try {// 将输入数据写入文件String taskInputPath = inputPath + cimsTask.getNestTaskCode() + ".json";TxtUtil.write(new File(taskInputPath), input, "utf-8");String exeStr =exePath.substring(0, 1) + ":\n" +"cd " + exePath + "\n" +"GDUT_PACK.exe " + cimsTask.getNestTaskCode() + ".json> " + cimsTask.getNestTaskCode() + "_output.log 2>&1";String batPath = exePath + File.separator + "bat" + File.separator + cimsTask.getNestTaskCode() + ".bat";TxtUtil.write(new File(batPath), exeStr, "utf-8");System.out.println("batPath:" + batPath);// 执行bat文件,开始计算ProcessBuilder processBuilder = new ProcessBuilder(batPath);Process process = processBuilder.start();// 扫描获取结果String resultPath = outputPath + cimsTask.getNestTaskCode() + ".json";File resFile;while (true) {resFile = new File(resultPath);if (resFile.exists()) {System.out.println(cimsTask.getNestTaskCode() + " 计算完成,时间:" + sdf.format(new Date()));String resultStr = TxtUtil.read(new File(outputPath + cimsTask.getNestTaskCode() + ".json"), "utf-8");CIMSTask resCimsTask = JSON.parseObject(resultStr, CIMSTask.class);resCimsTask.setNestTaskCode(cimsTask.getNestTaskCode());// 删除结果文件resFile.delete();// 删除输入文件File taskInputFile = new File(taskInputPath);if (taskInputFile.exists()) {taskInputFile.delete();}// 删除bat文件File batFile = new File(batPath);if (batFile.exists()) {batFile.delete();}// 删除输出日志File logFile = new File(exePath + File.separator + cimsTask.getNestTaskCode() + "_output.log");if (logFile.exists()) {logFile.delete();}// 停止进程process.destroy();return resCimsTask;}Thread.sleep(1000);}} catch (Exception e) {throw new RuntimeException(e);}
}

这种方法实现比较方便,但是有几个缺点:

  • 主进程无法得知子进程在什么时候执行完毕,只能通过自旋的方式来判断结果文件是否生成,文件成功生成则说明exe运行结束
  • 需要将exe的输出转移到log文件中,如果进程出错进入死循环,疯狂输出,可能导致日志文件非常大。当然可以定时清空日志里面的内容来解决该问题
  • 当java程序判断出exe程序存在问题时,无法直接通过java代码中断exe程序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/413741.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

opencv之几何变换

文章目录 1 前言2 线性几何变换的主要类型2.1 平移 (Translation)&#xff1a;2.1.1 定义2.1.2代码 2.2 缩放 (Scaling)&#xff1a;2.2.1 定义2.2.2 代码 2.3 旋转 (Rotation)&#xff1a;2.3.1 定义2.3.2 代码 2.4 仿射变换 (Affine Transformation)&#xff1a;2.4.1 定义2.…

数据源10min自动断开连接导致查询抛异常(未获取可用连接)

由于个人能力有限&#xff0c;本文章仅仅代表本人想法&#xff0c;若有不对请即时指出&#xff0c;若有侵权&#xff0c;请联系本人。 1 背景 工作中引入druid来管理数据源连接&#xff0c;由于数据源每隔10分钟强制管理空闲超过10分钟的连接&#xff0c;导致每隔10分钟出现1…

3D打印透气钢与传统透气钢的差异

透气钢作为一种集金属强度与透气性能于一体的特殊材料&#xff0c;在注塑模具领域扮演着关键角色&#xff0c;通过有效排除模具内困气&#xff0c;显著提升制品成型质量与生产效率。当前&#xff0c;市场上主流的透气钢产品多源自日本、美国&#xff0c;其高昂成本与技术壁垒限…

Golang | Leetcode Golang题解之第388题文件的最长绝对路径

题目&#xff1a; 题解&#xff1a; func lengthLongestPath(input string) (ans int) {n : len(input)level : make([]int, n1)for i : 0; i < n; {// 检测当前文件的深度depth : 1for ; i < n && input[i] \t; i {depth}// 统计当前文件名的长度length, isFi…

生成艺术,作品鉴赏:物似主人形

2001年&#xff0c;当21岁的我&#xff0c;还在恒基伟业当高级工程师时。我有一个女同事&#xff0c;她有个特别大的杯子用来喝水&#xff0c;不夸张的说&#xff0c;是那种我从来没见过的大杯子&#xff0c;由于她是很大只的那种&#xff0c;她便自嘲说&#xff1a;「物似主人…

【Kubernetes部署篇】二进制搭建K8s高可用集群1.26.15版本(超详细,可跟做)

文章目录 一、服务器环境信息及部署规划1、K8S服务器信息及网段规划2、服务器部署架构规划3、组件版本信息4、实验架构图 二、初始化环境操作1、关闭防火墙2、配置本地域名解析3、配置服务器时间保持一致4、禁用swap交换分区(K8S强制要求禁用)5、配置主机之间无密码登录6、修改…

JVM2-JVM组成、字节码文件、类的生命周期、类加载器

Java虚拟机的组成 Java虚拟机主要分为以下几个组成部分&#xff1a; 类加载子系统&#xff1a;核心组件类加载器&#xff0c;负责将字节码文件中的内容加载到内存中运行时数据区&#xff1a;JVM管理的内存&#xff0c;创建出来的对象、类的信息等内容都会放在这块区域中执行引…

RelativeLayout相对布局

activity_relative_layout.xml <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"150dp…

QT QGraphicsView实现预览图片显示缩略图功能

QT QGraphicsView实现预览图片显示缩略图功能QT creator Qt5.15.2 头文件&#xff1a; #ifndef TGRAPHICSVIEW_H #define TGRAPHICSVIEW_H#include <QGraphicsView> #include <QMainWindow> #include <QObject> #include <QWidget>class TGraphicsVie…

Oracle 客户端 PL/SQL Developer 15.0.4 安装与使用

目录 官网下载与安装 切换中文与注册 连接Oracle数据库 tnsnames.ora 文件使用 Oracle 客户端 PL/SQL Developer 12.0.7 安装、数据导出、Oracle 执行/解释计划、for update。 官网下载与安装 1、官网&#xff1a;https://www.allroundautomations.com/products/pl-sql-d…

P01-何谓Java方法

P01-何谓Java方法 一、System.out.println()分析 二、剖析方法 谈到方法&#xff0c;我就突然想到了c函数&#xff1a; 其实&#xff1a;Java 方法和 C 函数在许多方面确实有类似之处&#xff0c;但它们也存在一些显著的差异。下面是它们的一些共同点和不同点&#xff1a; 共同…

DORIS - DORIS简介

前言 本博文基于DORIS的2.1.5版本。apache-doris-2.1.5-bin-x64.tar.gz 是什么&#xff1f; DORIS官网 Apache Doris 是一款基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以高效、简单、统一的特点被人们所熟知&#xff0c;仅需亚秒级响应时间即可返回海量数据下的…

【第0004页 · 递归】生成括号对

【前言】本文以及之后的一些题解都会陆续整理到目录中&#xff0c;若想了解全部题解整理&#xff0c;请看这里&#xff1a; 第0004页 生成括号对 今天这题有点难绷&#xff0c;从某种程度上来说应该是第二次写这个问题了&#xff0c;但还是卡住了&#xff0c;现在我们来看一下…

安防视频汇聚平台EasyCVR启动后无法访问登录页面是什么原因?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台基于云边端一体化架构&#xff0c;兼容性强、支持多协议接入&#xff0c;包括国标GB/T28181协议、部标JT808、GA/T1400协议、RTMP、RTSP/Onvif协议、海康Ehome、海康SDK、大华SDK、华为SDK、宇视SDK、乐橙SDK、萤石云SDK等…

设计模式之适配器模式:软件世界的桥梁建筑师

一、什么是适配器模式 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff08;Structural Pattern&#xff09;&#xff0c;通过将类的接口转换为客户期望的另一个接口&#xff0c;适配器可以让不兼容的两个类一起协同工作。其核心思想是通过一个…

SQL 语言简明入门:从历史到实践

SQL&#xff08;Structured Query Language&#xff09;是数据库领域的核心语言。自20世纪70年代中期由IBM公司开发以来&#xff0c;SQL已经成为全球最广泛使用的数据库管理语言。 本文将以简洁明了的方式为您介绍SQL的历史、基本结构、核心语言组成以及其独特的特点和书写规则…

Cookie对象的缺陷与应对策略

Cookie对象的缺陷与应对策略 1. 安全性问题&#xff1a;Cookie是明文的2. 存储限制&#xff1a;浏览器对Cookie数量和大小有限制3. 性能影响&#xff1a;Cookie携带过多增加网络流量4. 数据类型限制&#xff1a;Cookie的value值只能是字符串 &#x1f496;The Begin&#x1f4…

华为2024 届秋招招聘——硬件技术工程师-电源方向-机试题(四套)(每套四十题)

华为 2024 届秋招——硬件-电源机试题&#xff08;四套&#xff09;&#xff08;每套四十题&#xff09; 岗位——硬件技术工程师 岗位意向——电源 真题题目分享&#xff0c;完整版带答案(有答案和解析&#xff0c;答案非官方&#xff0c;未仔细校正&#xff0c;仅供参考&am…

bbr 和 inflight 守恒的收敛原理

先看 bbr&#xff0c;以 2 条流 bw 收敛为例&#xff0c;微分方程组如下&#xff1a; { d x d t C ⋅ g ⋅ x g ⋅ x y − x d y d t C ⋅ g ⋅ y g ⋅ y x − y \begin{cases} \dfrac{dx}{dt}C\cdot\dfrac{g\cdot x}{g\cdot xy}-x\\\ \dfrac{dy}{dt}C\cdot\dfrac{g\cdot y…

Python酷库之旅-第三方库Pandas(113)

目录 一、用法精讲 496、pandas.DataFrame.kurtosis方法 496-1、语法 496-2、参数 496-3、功能 496-4、返回值 496-5、说明 496-6、用法 496-6-1、数据准备 496-6-2、代码示例 496-6-3、结果输出 497、pandas.DataFrame.max方法 497-1、语法 497-2、参数 497-3、…