JNA字符串&结构体

简介: JNA字符串&结构体

1.字符串

1.1 返回字符串类型

c/c++代码

  • 全局变量
char retp[1024];
const char* getStr1(int a, int b){
memset(retp, 0, 1024);
    char outstr[256];
memset(outstr, 0, 256);
    if (outstr != 0){
sprintf_s(outstr, "汉字out DLL汉字: %d + %d==%d\n", a, b, (a + b));
    }
strcpy_s(retp, outstr);
    return retp;
}
  • malloc函数分配空间
const char* getStr2(int a, int b){
char* retp = (char*)malloc(1024);
memset(retp, 0, 1024);
char outstr[256];
memset(outstr, 0, 256);
if (outstr != 0){
sprintf_s(outstr, "汉字out DLL汉字: %d + %d==%d\n", a, b, (a + b));
//printf("%s",outstr);
    }
strcpy_s(retp, strlen(outstr) + 1, outstr);
return retp;
}

java代码

package com.jnademo;
import com.sun.jna.Library;
import com.sun.jna.Native;
public class JnaTest {
  public interface CLibrary extends Library {
    CLibrary INSTANCE = (CLibrary) Native.load("E:\\dllws\\jnaTest\\x64\\Debug\\jnaTest.dll",
        CLibrary.class);
      String getStr1(int a, int b);
      String getStr2(int a, int b);
  }
  public static void main(String[] args) {
    System.setProperty("jna.encoding","GBK");
    System.out.println(CLibrary.INSTANCE.getStr1(1, 3));
    System.out.println(CLibrary.INSTANCE.getStr2(2, 3));
  }
}

要注意添加System.setProperty("jna.encoding","GBK");否则会出现乱码。具体规则

  • c++ char* GBK编码时
System.setProperty("jna.encoding", "GBK");
  • c++ char* UTF8编码时
System.setProperty("jna.encoding", "UTF-8");

另外,其实还有个更简单的办法,JNA提供了一个宽字符字符串WString,当然c++接口参数类型要使用wchar_t*,这样WString就可以无缝转wchar_t*了,不用做任何修改,也绝对不会乱码。

1.2 C/C++接收字符串类型

C/C++代码

bool JavaStr(char* szText, int textLen) {
if (szText == NULL || textLen <= 0) {
return false;
    }
std::string strText(szText, textLen);
//OutputDebugStringA("JavaStr:");
//OutputDebugStringA(strText.c_str());
    cout << strText.c_str() << endl;
return true;
}

java代码

package com.jnademo;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Library;
import com.sun.jna.Native;
public class TestStructDemo {
  public interface TestStruct extends Library {
    TestStruct INSTANCE = (TestStruct) Native.load("E:\\dllws\\jnaTest\\x64\\Debug\\jnaTest.dll",
        TestStruct.class);
      public boolean JavaStr(String str, int strLen);   // 使用String传参数,中文会参数乱码
      public boolean JavaStr(byte[] str, int strLen);   // 使用byte[]传参数,中文正常
  }
  public static void main(String[] args) throws UnsupportedEncodingException {
//https://blog.51cto.com/softo/6009271
String utf8Str = "UTF8转成GBK";
String gbkStr = new String(utf8Str.getBytes("UTF-8"), "GBK"); // UTF8转成GBK
byte[] gbkBytes = utf8Str.getBytes("UTF-8");
        TestStruct.INSTANCE.JavaStr(gbkStr, gbkStr.length());      // 传字符串C++接收时中文是乱码
        TestStruct.INSTANCE.JavaStr(gbkBytes, gbkBytes.length);    // 传字节数组C++接收中文正常
  }
}

2. 结构体

C/C++代码

#define JNA_LIBAPI extern "C" __declspec( dllexport ) 
  //C++结构体
  struct UserStruct{
    long id;
    char* name;
    int age;
  };
  JNA_LIBAPI void sayUser(UserStruct pUserStruct);
  JNA_LIBAPI void sayUser2(UserStruct* pUserStruct);

定义一个结构体UserStruct,在定义两个方法,参数分别是UserStruct对象类型和UserStruct指针类型。

java代码

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Structure;
public class TestStructDemo {
  public interface TestStruct extends Library {
    TestStruct INSTANCE = (TestStruct) Native.load("E:\\dllws\\jnaTest\\x64\\Debug\\jnaTest.dll", TestStruct.class);
    //结构体定义
      public static class myStructure extends Structure {
          public NativeLong id;
          public String name;
          public int age;
          public static class ByReference extends myStructure implements Structure.ByReference {}
          public static class ByValue extends myStructure implements Structure.ByValue {}
          @Override
          protected List<String> getFieldOrder() {
              return Arrays.asList(new String[] {"id", "name", "age"});
          }
      }
      //申明C端调用结构体的函数
      public void sayUser(myStructure.ByValue struct);
      public void sayUser2(myStructure.ByReference struct);
  }
  public static void main(String[] args) throws UnsupportedEncodingException {
    //ByValue这个类代表结构体本身
    TestStruct.myStructure.ByValue testReference = new TestStruct.myStructure.ByValue();
        testReference.age = 20;
        testReference.id = new NativeLong(10);
        testReference.name = new String("Tony");
        TestStruct.INSTANCE.sayUser(testReference);
//ByReference代表结构体指针
        TestStruct.myStructure.ByReference testReference2 = new TestStruct.myStructure.ByReference();
        testReference2.age = 80;
        testReference2.id = new NativeLong(100);
        testReference2.name = new String("Jack");
        TestStruct.INSTANCE.sayUser2(testReference2);
  }
}

在Java中实现对C结构体的模拟,需要继承Structure类,利用这个类来模拟C语言的结构体。必须注意,Structure子类中公共字段的顺序,必须与C语言中结构体的顺序一致,否则会报错!因为Java调用动态库中的C函数,实际上是一段内存作为函数参数传递给C函数。动态库以为这个参数就是C语言传过来的参数。同时,C语言的结构体是一个严格规范,它定义了内存的次序,因此,Jna中模拟的结构体变量顺序绝不能错。

如果一个struct有两个int变量,int a 和 int b 如果Jna中的次序和C语言中次序相反,那么不会报错,但是数据将被传递到错误的字段中去。

Structure类代表了一个原生结构体。当Structure对象作为一个函数的参数或者返回值传递时,它代表结构体指针。当它被用在另一个结构体内部作为一个字段时,它代表结构体本身。

Structure类有两个内部接口Structure.ByReference和Structure.ByValue。这两个接口仅仅是标记,如果一个类实现Structure.ByReference接口,就表示这个类代表结构体指针;如果一个类实现Structure.ByValue接口,就表示这个类代表结构体本身。使用这两个接口的实现类,可以明确定义Structure实例表示的是结构体还是结构体指针

参考:
https://blog.csdn.net/redchairman/article/details/108438202
https://blog.csdn.net/houmingyang/article/details/127071298
https://zhuanlan.zhihu.com/p/466863639
相关文章
|
3月前
|
Ubuntu Java Android开发
在Ubuntu 18.04上安装与JDK 8兼容的Eclipse版本的步骤。
安装过程结束后,您就可以开始使用Eclipse来开发您的Java项目了,并且确保它与JDK 8兼容无误。这个过程涉及的是一个基本的安装流程,针对使用Java 8的用户,Eclipse的其他配置和插件安装根据个人开发环境和需求来定制。
303 0
|
10月前
|
机器学习/深度学习 人工智能 编译器
BladeDISC++:Dynamic Shape AI 编译器下的显存优化技术
本文介绍了阿里云 PAI 团队近期发布的 BladeDISC++项目,探讨在动态场景下如何优化深度学习训练任务的显存峰值,主要内容包括以下三个部分:Dynamic Shape 场景下显存优化的背景与挑战;BladeDISC++的创新解决方案;Llama2 模型的实验数据分析
|
设计模式 测试技术 持续交付
自动化测试框架的设计与实现
【9月更文挑战第25天】本文旨在探讨如何设计并实现一个高效、可扩展的自动化测试框架,以提升软件测试的效率和质量。通过分析当前流行的测试框架特点,结合最佳实践,提出一套完整的解决方案。文章不仅涵盖框架设计的理论依据,还包括具体实现步骤和示例,帮助读者深入理解自动化测试框架的搭建过程。
|
存储 Java 编译器
String能存储多少个字符?
这篇内容讨论了Java中String的长度限制。编译时,如果字符串长度大于等于65535,编译器将报错。这是由于`CONSTANT_Utf8`常量池项的长度字段是16位无符号整数,最大值为65535。而在运行时,虽然理论上String的最大长度是2^31-1,但实际长度受限于JVM内存,最大可能占用约2GB内存,超过可能导致OutOfMemoryError。JDK9以后,对于LATIN1字符的字符串,存储优化使用byte数组,节省内存。
792 1
String能存储多少个字符?
|
数据可视化 Java
《IntelliJ IDEA 插件开发》第二节:开发摸鱼看书的侧边栏窗体
一、说明 二、需求目的 三、案例开发 1. 工程结构 2. 创建 UI 窗体 3. ToolWindow 工具框 4. Configurable 配置框 5. 配置 plugin.xml 四、插件测试 五、总结 六、系列推荐
1392 0
《IntelliJ IDEA 插件开发》第二节:开发摸鱼看书的侧边栏窗体
|
存储 安全 数据安全/隐私保护
kali工具 -- setoolkit(克隆网站及利用)
kali工具 -- setoolkit(克隆网站及利用)
1147 1
|
存储 缓存 Kubernetes
在k8S中,数据持久化的方式有哪些?
在k8S中,数据持久化的方式有哪些?
|
SQL 存储 搜索推荐
SQL游标的原理与在数据库操作中的应用
SQL游标的原理与在数据库操作中的应用
|
SQL Java 数据库连接
1天搞定SpringBoot+Vue全栈开发 (3)MybatisPlus(数据库操作)
1天搞定SpringBoot+Vue全栈开发 (3)MybatisPlus(数据库操作)
|
算法 安全 Java
浅析五种最常用的Java加密算法,以后可以直接拿来用了
信息加密是现在几乎所有项目都需要用到的技术,身份认证、单点登陆、信息通讯、支付交易等场景中经常会需要用到加密算法,所谓加密算法,就是将原本的明文通过一系列算法操作变成密文。接下来就介绍一下目前比较常用的一些加密算法,本期不涉及算法底层,以应用介绍和代码展示为主。 如果只想了解原理,可跳过代码部分,代码可直接拿来使用。
3079 0