小记Java调用C++开发的动态链接库(DLL)

news2024/12/22 18:29:39

一、背景

五一快乐吖!死肥宅正趁着五一这段时间,努力提升自己!

最近使用Java拦截Windows系统中一些默认事件时,发现了一些瓶颈。

我用Java操作浏览器、用Java最小化其他应用窗口,但是我发现这个操作,他都是异步的。

比如,写个程序,获取当前前置窗口,给他缩小。由于它是异步的,只是给操作系统发个通知你要缩小,但是否执行完,开发者不知道。实际上由于循环过快,就成了死循环一直获取到的是当前窗口,然后一直缩小当前窗口。最后把电脑卡死了。所以用Java写系统级的功能,并不好使。

经过我在C++/C#/Java/Go中的语言选型,以下排序分先后

开发语言适用平台适用场景备注
C++跨平台底层应用、上层应用-
C#Windows底层应用、上层应用也支持跨平台,但不如直接C++
Java跨平台上层应用借助其他语言,如C/C++、C#可调用操作系统底层API
Go跨平台上层应用借助其他语言,如C/C++、C#可调用操作系统底层API。不过说替代C++,完全是扯淡,如果真要说作用,那就是为了分Java一杯羹。

二、动态链接库

2.1 概念

动态链接库(Dynamic Link Library,简称DLL)是一种Windows操作系统下常见的可执行文件格式,它包含了一些可被其他应用程序调用的函数和数据,可以用来实现模块化开发和共享代码等功能。

与静态链接库(Static Link Library,简称LIB)不同,DLL在程序运行时才会被加载和链接,而不是在编译时。这样做的好处是可以减小可执行文件的体积,并且可以在程序运行时动态加载和卸载DLL,从而实现模块化开发和动态扩展等功能。

DLL可以包含多个函数和数据,这些函数和数据都有一个独立的入口地址,可以被其他应用程序通过调用入口地址来使用。应用程序可以通过Windows API提供的一系列函数(如LoadLibrary、GetProcAddress等)来动态加载和链接DLL中的函数和数据,并在不需要时将其卸载。

使用DLL可以带来许多好处,比如:

  1. 代码复用:多个应用程序可以共享同一个DLL,从而避免重复编写相同的代码。
  2. 模块化开发:可以将大型应用程序拆分成多个DLL,每个DLL只包含一部分功能,从而降低代码复杂度,方便维护和升级。
  3. 动态加载和卸载:可以在程序运行时动态加载和卸载DLL,从而实现动态扩展和插件化开发等功能。

但是,DLL也存在一些缺点,比如:

  1. 版本兼容性问题:如果DLL的版本发生变化,调用该DLL的应用程序也需要相应地修改,否则可能会出现不兼容的问题。
  2. 安全性问题:DLL可以被其他应用程序调用,如果DLL中的代码存在漏洞或安全问题,可能会被黑客利用,造成安全风险。
  3. 性能问题:由于DLL需要动态加载和链接,所以在程序运行时可能会带来一些性能开销。

因此,在使用DLL时需要注意版本兼容性、安全性和性能等问题,合理地使用DLL可以提高代码复用和程序可维护性,提升开发效率和程序性能。

2.2 环境配置

开发IDE:Visual Studio 2022

该页面如果已安装,可以在 新建 - 安装多个工具和功能 选项中查找到

2.3 C++开发动态链接库

2.3.1 创建项目

2.3.2 编写代码

编写代码TestDLL.cpp

// TestDLL.cpp : 定义 DLL 的导出函数。
//

#include "pch.h"
#include "framework.h"
#include "TestDLL.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <string>
using namespace std;


// 这是导出变量的一个示例
TESTDLL_API int nTestDLL=0;

// 这是导出函数的一个示例。
TESTDLL_API int fnTestDLL(void)
{
    return 0;
}

TESTDLL_API int add(int a, int b) {
    int value = a + b;
   //c++打印
    cout << "C++打印: TestDLL add: " << value << endl;
    //c打印
    printf("C打印: TestDLL add: %d", value);
    // 线程休眠5秒
    std::this_thread::sleep_for(std::chrono::seconds(5));
    return value;
}


TESTDLL_API void openBrowser(char* str) {
    std::string cmd = "start " + std::string(str);
    system(cmd.c_str());
}

// 这是已导出类的构造函数。
CTestDLL::CTestDLL()
{
    return;
}

编写代码TestDLL.h头文件

// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 TESTDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// TESTDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef TESTDLL_EXPORTS
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif

// 此类是从 dll 导出的
class TESTDLL_API CTestDLL {
public:
	CTestDLL(void);
	// TODO: 在此处添加方法。
};

extern TESTDLL_API int nTestDLL;

TESTDLL_API int fnTestDLL(void);


// 声明
/*
当C++代码包含C函数或C库的头文件时,由于C++和C语言在函数名称的处理方式上有所不同,C++编译器会将这些C函数或库中的函数名称按照C++的方式进行重整,这会导致函数名称被修改并在链接时找不到函数。
通过在C++代码中使用extern "C"说明符来修饰C函数,可以告诉C++编译器对函数名进行C语言风格的链接,从而避免函数名被重整导致链接错误。

*/
extern "C" TESTDLL_API int add(int a, int b);
extern "C" TESTDLL_API void openBrowser(char* str);

2.3.3 注意事项

参考学习JNA的一天-自定义DLL以及被Java调用 - 简书

由此代码就写完了。注意几点事项

  1. 项目编码设置为UTF-8,不是UTF-8 BOM
  2. 编译系统相应版本。

1.) 设置编码

2.) 编译系统相应版本

生成-配置管理器

2.3.4 编译

三、Java调用动态链接库

3.1 JNA与JNI

JNA是基于JNI技术,实现的一个Java库!

JNI(Java Native Interface)是Java提供的一种与本地代码交互的技术。通过JNI,Java程序可以调用本地编写的C/C++等语言编写的代码,也可以让本地代码调用Java程序中的对象和方法。

JNA(Java Native Access)是一个Java库,它允许Java应用程序直接访问本地库(如动态链接库,DLL)中的功能,而无需编写任何本地代码。JNA通过Java反射和动态代理技术,将Java数据类型和本地C数据类型进行映射,实现Java和本地代码之间的互操作。

JNA(Java Native Access)和JNI(Java Native Interface)都是Java与本地代码交互的技术,但它们有一些区别。

  • 难易程度

    • JNI的使用相对较为复杂,需要编写较多的底层代码,如头文件、Java虚拟机调用等,需要掌握C/C++语言。
    • JNA的使用相对简单,只需要定义接口、使用注解、调用动态库函数即可,无需编写底层代码。
  • 性能

    • JNI在性能方面较为优秀,因为它直接操作本地内存,调用本地代码时速度较快。
    • JNA在性能方面相对较差,因为它使用Java对象来表示本地内存,需要进行频繁的对象转换和内存拷贝。
  • 平台兼容性

    • JNI需要编写本地代码,因此需要针对不同的操作系统和平台进行不同的实现。
    • JNA则是基于Java虚拟机实现的,因此可以很好地跨平台运行,无需考虑本地代码的兼容性问题。
  • 使用场景

    • JNI主要用于需要对本地代码进行直接控制的场景,如操作系统级别的编程、性能敏感的算法等。
    • JNA则主要用于简化Java与本地库交互的过程,如调用本地的API函数等。

总的来说,JNA相比JNI使用起来更加简单、方便,并且具有良好的跨平台性,但在性能方面稍逊于JNI,因此需要根据具体的需求选择合适的技术。

3.2 JNI使用

使用步骤分为三步

  1. 定义Java方法
  2. 编写底层(C/C++)方法
  3. 编译和链接:将本地代码编译为动态链接库或静态库,并将其链接到Java程序中。在Java程序中调用native方法时,动态链接库将被加载并执行本地代码。

一般JNI的方法,都会带有native关键字,如下。

public native void hello();

实际手撕JNI,使用起来较为复杂,所以一般采用JNA库来实现功能

3.3 JNA使用

3.3.1 示例代码

添加maven依赖

<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>5.12.1</version>
</dependency>
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna-platform</artifactId>
    <version>5.12.1</version>
</dependency>

以下是JNA的使用示例

public interface TestDLL extends Library {
    //加载dll,实例成对象。下面的方法,均为dll提供的方法
    TestDLL instance = Native.load("C:\\Users\\meethigher\\Desktop\\DLL\\TestDLL\\x64\\Debug\\TestDLL.dll",TestDLL.class);

    int add(int a,int b);

    void openBrowser(String str);
}

进行测试

@Slf4j
public class Main {
    public static void main(String[] args) {
        log.info("start");
        int add = TestDLL.instance.add(1, 2);
        log.info("end");
        System.out.println(add);
        for (int i = 0; i < 10; i++) {
            TestDLL.instance.openBrowser(String.format("https://meethigher.top/%s", i));
        }
    }
}

3.3.1 数据类型对应关系

表格如下

Java数据类型C/C++数据类型
booleanbool
bytechar
shortshort
intint
longlong long
floatfloat
doubledouble
charchar
Stringconst char*
byte[]char*
short[]short*
int[]int*
long[]long long*
float[]float*
double[]double*
char[]char*
char
Stringconst char*
byte[]char*
short[]short*
int[]int*
long[]long long*
float[]float*
double[]double*
char[]char*

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Unity-UGUI控件全面解析】| InputField 输入框组件详解

🎬【Unity-UGUI控件全面解析】| InputField 输入框组件详解一、组件介绍二、组件属性面板2.1 Content Type(内容类型)三、代码操作组件四、组件常用方法示例4.1 代码限制输入字符4.2 校验文本输入格式4.3 校验输入文本长度💯总结🎬 博客主页:https://xiaoy.blog.csdn.…

话说【永恒之塔sf】里面最有前途的职业:商人

如果有人问我永恒之塔里面什么职业最有前途&#xff01;那我告诉你就是商人&#xff01; 做一个NB商人比拥有一身牛b装备要更有成就感。 在老区由于进入的比较晚&#xff0c;所以最后随了大流被淹死在千万基纳中。为了证明商人在永恒之塔是钱途无量的&#xff0c;我转到了新区—…

快解析动态域名解析,实现外网访问内网数据库

今天跟大家分享一下如何借助快解析动态域名解析&#xff0c;在两种特定网络环境下&#xff0c;实现外网访问内网mysql数据库。 第1种网络环境&#xff1a;路由器分配的是动态公网IP&#xff0c;且有路由器登录管理权限。如何实现外网访问内网mysql数据库&#xff1f; 针对这种…

IDEA2022版教程上()

0、前景摘要 0.1 概览 0.2 套课程适用人群 初学Java语言&#xff0c;熟悉了记事本、EditPlus、NotePad或Sublime Text3等简易开发工具的Java初学者熟练使用其他Java集成开发环境&#xff08;IDE&#xff09;&#xff0c;需要转向IDEA工具的Java工程师们关注IDEA各方面特性的J…

Hadoop大数据分析技术(伪分布式搭建)

一.安装JDK和配置SSH免密登录 &#xff08;1&#xff09;准备软件 &#xff08;2&#xff09;解压压缩包 tar -zxvf jdk-8u221-linux-x64.tar.gz &#xff08;3&#xff09;在此处我们配置系统环境变量&#xff0c;使用命令&#xff1a; vim /etc/profile &#xff08;4&#x…

Python入门教程(高级版)

Python用了好几年了&#xff0c;但似乎一直没 “系统入门” 过&#xff08;o(╯□╰)o&#xff09;。今年&#xff08;2023年&#xff09;趁着五一假期&#xff0c;我做了一次相对完整的 “入门” ——本文是这次学习历程的详细记录。 目录 1 Python基础1.1 Python1.1.1 认识Py…

Oracle VM VirtualBox安装centos7步骤 for win10

目录 1.安装VirtualBox 2.安装vagrant 3.安装centos7 4.查看网络与百度和物理机连通情况 5.设置IP 1.安装VirtualBox 下载的链接:Downloads – Oracle VM VirtualBox 2.安装vagrant 根据自己的操作系统选择对应的版本。 Install | Vagrant | HashiCorp Developer 我的P…

asp.net+sqlserver旅游网站zjy99A2

1&#xff0e;系统登录&#xff1a;系统登录是用户访问系统的路口&#xff0c;设计了系统登录界面&#xff0c;包括用户名、密码和验证码&#xff0c;然后对登录进来的用户判断身份信息&#xff0c;判断是管理员用户还是普通用户。 2&#xff0e;系统用户管理&#xff1a;不管是…

redis使用总结

目录 redis安装与登录redis 持久化RDB(Redis DataBase)AOF(Append Only File)RDB-AOF混合持久纯缓存模式 redis 的 keyredis 的数据类型和常见应用场景StringListHashMapSet集合ZSet有序集合bitmap位图HyperLogLog基数统计GEO 地理空间Stream 流bitfiled redis 事务事务的正常执…

《基于EPNCC的脉搏信号特征识别与分类研究》阅读笔记

目录 一、论文摘要 二、论文十问 三、论文亮点与不足之处 四、与其他研究的比较 五、实际应用与影响 六、个人思考与启示 参考文献 一、论文摘要 为了快速获取脉搏信号的完整表征信息并验证脉搏信号在相关疾病临床诊断中的敏感性和有效性。在本文中&#xff0c;提出了一…

ChatGPT根据销售数据、客户反馈、财务报告,自动生成报告,并根据不同利益方的需要和偏好进行调整?

该场景对应的关键词库&#xff08;24个&#xff09;&#xff1a; 汇报对象身份&#xff08;下属、跨部门平级、领导&#xff09;、销售数据&#xff08;销售额、销售量、销售渠道&#xff09;、财务报告&#xff08;营业收入、净利润、成本费用&#xff09;、市场分析&#xf…

Vulkan实战之验证层

文章目录 验证层是什么&#xff1f;使用验证层消息回调调试实例的创建和销毁测试配置最终代码 验证层是什么&#xff1f; Vulkan API是围绕最小化驱动程序开销的想法设计的&#xff0c;该目标的表现之一是默认情况下API中的错误检查非常有限。即使是像将枚举设置为不正确的值或…

STM32 系列 DAC的介绍与使用

STM32网上资料多&#xff0c;对自己来说基本的使用也是很简单的&#xff0c; 我的STM32专栏并没有什么系统的基础教学&#xff0c;基本上是某个项目用到了&#xff0c;或者产品使用过程出过问题 才会来记录一下&#xff0c;正好用到了 DAC &#xff0c;一般产品还用得不多&…

【Unity入门】23.简单物理系统

【Unity入门】简单物理系统 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity入门系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;重力系统 &#xff08;1&#xff09;Rigidbody组件 Unity里面有提供符合我们常规认知的物理系统组件Physics&…

GDKOI 2023游记总结

不知觉就咕了1.5个月 在回忆 2021 年那个刚步入初中的懵懂孩童参加 GDOI 的惊喜中感慨初中时光的飞逝。 2021 GDOI 普及组游记 Day - ∞ \infty ∞ 去年因为疫情取消了&#xff0c;今年难得重新举办&#xff0c;珍惜每一次机会吧。 前年的地点订在深圳耀华学校&#xff0c;忘…

老外从神话原型中提取的12个品牌个性

老外从神话原型中提取的12个品牌个性 也是西方视角&#xff0c;需要本土化 参照心理学大师荣格的理论&#xff1a;心理学潜意识派 趣讲大白话&#xff1a;品牌的调调是啥 【趣讲信息科技151期】 **************************** 12种原型又归属于4种人性动机。 1、稳定&#xff0…

Python小姿势 - ## Python中的迭代器与生成器

Python中的迭代器与生成器 在Python中&#xff0c;迭代是一个非常重要的概念&#xff0c;迭代器和生成器是迭代的两种最常见的形式。那么&#xff0c;迭代器与生成器有何不同呢&#xff1f; 首先&#xff0c;我们先来了解一下迭代器。 迭代器是一种对象&#xff0c;它可以记住遍…

第二十八章 React脚手架配置代理

为了更好地理解如何在React应用程序中配置代理&#xff0c;我们需要先了解什么是代理。 代理是一种充当客户端和服务器之间中间人的服务器。当客户端向服务器发送请求时&#xff0c;代理服务器将接收请求并将其转发到服务器。服务器将响应发送回代理服务器&#xff0c;代理服务…

Spring:Bean的实例化(构造方法、静态工厂和实例化工厂)

三种方式&#xff0c;分别为构造方法、静态工厂和实例化工厂 新建Module项目&#xff0c;选择Maven&#xff0c;在pom.xml导入如下依赖&#xff1a; pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.o…

网络编程 总结二

一、TCP TCP模型 1. TCP搭建相关函数&#xff1a; 套接字Socket 1&#xff09;Socket函数&#xff1a; 2&#xff09;bind 3&#xff09;listen 4&#xff09;accept 5&#xff09;recv 注意&#xff1a; 1> TCP中的recv 可以替换成read&#xff1b; 2>TCP中的…