1、c++ 结构体数据接收
c++
struct Person {
int id;
double salary;
// Student * student;
};
extern "C" {
Person * get_person();
void free_person(Person *p);
}
java接收数据的两种方式
第一种,通过pointer指针、构造函数接收
public class Person extends Structure {
public int id;
public double salary;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("id","salary");
}
public Person(Pointer pointer){
super(pointer);
read();
}
public Person(){
super();
}
}
public interface PersonLibary extends Library {
PersonLibary INSTANCE = (PersonLibary) Native.load("libuntitled.dll",PersonLibary.class);
Pointer get_person(); // 通过pointer接收
void free_person(Pointer ptr);
}
public class Test {
public static void main(String[] args) {
Pointer ptr = PersonLibary.INSTANCE.get_person();
if(ptr == null){
System.out.println("fail to getPerson");
}
Person person = new Person(ptr);
System.out.println(person.id + "===>" + person.salary);
}
}
第二种,通过ByReference 接收
public class Person extends Structure {
public int id;
public double salary;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("id","salary");
}
public Person(){
super();
}
public static class ByReference extends Person implements Structure.ByReference{};
}
public interface PersonLibary extends Library {
PersonLibary INSTANCE = (PersonLibary) Native.load("libuntitled.dll",PersonLibary.class);
Person.ByReference get_person();
void free_person(Pointer ptr);
}
public class Test {
public static void main(String[] args) {
Person.ByReference person = PersonLibary.INSTANCE.get_person();
if(person ==null){
System.out.println("创建结构体失败");
}
System.out.println(person.id + "===>" + person.salary);
}
}
2、c++ 嵌套复杂结构体数据接收
c++结构体
struct Student {
int sid;
char* name;
};
struct Person {
int id;
double salary;
const char* name;
Student* student;
};
extern "C" {
Person * get_person(char* name);
void free_person(Person *p);
}
java接收
public class Student extends Structure {
public int sid;
public String name;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("sid","name");
}
public Student(){
super();
}
public static class ByReference extends Student implements Structure.ByReference{};
}
public class Person extends Structure {
public int id;
public double salary;
public String name;
public Student.ByReference student; // 此处用Student.ByReference接收, 用Student接收的是指针地址,数据不对。
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("id","salary","name","student");
}
public Person(){
super();
}
public static class ByReference extends Person implements Structure.ByReference{};
}
public interface PersonLibary extends Library {
PersonLibary INSTANCE = (PersonLibary) Native.load("libuntitled.dll",PersonLibary.class);
Person.ByReference get_person(String world);
void free_person(Pointer ptr);
}
public class Test {
public static void main(String[] args) {
Person.ByReference person = PersonLibary.INSTANCE.get_person("hahaha中国人");
if (person == null) {
System.out.println("创建结构体失败");
}
System.out.println(person.id + "===>" + person.salary + "===>" + person.student.sid + "===>" + person.student.name);
}
}
常见问题:
Invalid memory access 无效内存访问
若顺序错误,会导致字段偏移错位,访问时出现Invalid memory access或数据错乱。
是否字段不匹配,不匹配导致的错误,可以通过类占用内存的大小判断
java: System.out.println("Java结构体大小: " + new ParseResult().size());
c++: printf("C结构体大小: %d\n", sizeof(struct ParseResult));
当 C++ 结构体大小为 32 字节,而 JNA 映射的Structure大小为 24 字节时,说明两者的内存布局不匹配(字段类型、顺序或对齐方式存在差异),这会导致数据读写错乱或崩溃。以下是具体的排查和解决步骤:
一、核心原因分析
大小差异通常由以下原因导致:
字段类型不匹配:C++ 与 Java 字段的字节长度不同(如 C++long为 8 字节,Java 误用int(4 字节))。
对齐方式不同:C++ 使用了特定对齐(如#pragma pack(8)),而 JNA 默认对齐方式不同。
字段数量 / 顺序错误:Java 结构体缺少字段或字段顺序与 C++ 不一致。
嵌套结构体 / 数组大小错误:嵌套结构体或数组的长度计算错误。
jna接收到的数据乱码问题
java默认为utf-8,c++默认为gbk,在接收数据时设置jna.encoding System.setProperty("jna.encoding", "GBK");
复杂结构:
c++
#pragma once
#ifdef PIPEDATADLL_EXPORTS
#define PARSER_API __declspec(dllexport)
#else
#define PARSER_API __declspec(dllimport)
#endif
#include <string>
struct ComponentItem {
const char* name;
const char* value;
};
struct CodeAnalysisItem {
const char* part;
const char* meaning;
};
struct TextItem {
const char* text;
};
struct SubComponent {
const char* name;
const char* params;
};
struct ComponentData {
const char* code;
const char* description;
const char* function;
const char* standard;
CodeAnalysisItem* codeAnalysis;
int codeAnalysisCount;
TextItem* textItems;
int textCount;
SubComponent* subComponents;
int subComponentCount;
};
struct ParseResult {
ComponentData* components;
int componentCount;
};
extern "C" {
PARSER_API void* parse_file(const char* filename);
PARSER_API ParseResult* get_parse_result(void* parserHandle);
PARSER_API void free_parser_handle(void* parserHandle);
}
java代码:
public class CodeAnalysisItem extends Structure {
public String part;
public String meaning;
public CodeAnalysisItem() {
super();
}
public CodeAnalysisItem(Pointer peer) {
super(peer);
read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("part", "meaning");
}
// 用于按引用传递(指针)
public static class ByReference extends CodeAnalysisItem implements Structure.ByReference {
public ByReference() {
super();
}
public ByReference(Pointer peer) {
super(peer);
read();
}
}
@SneakyThrows
@Override
public String toString() {
return "CodeAnalysisItem{" +
"part='" + part + '\'' +
", meaning='" + meaning + '\'' +
'}';
}
}
public class ComponentData extends Structure {
public String code;
public String description;
public String function;
public String standard;
public String gtype;
public Pointer codeAnalysis;
public int codeAnalysisCount;//分段数量 (P25.4T576A23SI解析为5段)
public Pointer textItems;
public int textCount;//数量
public Pointer subComponents;
public int subComponentCount;//数量
public ComponentData() {
super();
}
public ComponentData(Pointer peer) {
super(peer);
read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("code","description","function","standard","gtype","codeAnalysis","codeAnalysisCount","textItems","textCount","subComponents","subComponentCount");
}
// 用于按引用传递(指针)
public static class ByReference extends ComponentData implements Structure.ByReference {
public ByReference() {
super();
}
public ByReference(Pointer peer) {
super(peer);
read();
}
}
// 获取CodeAnalysisItem数组
public CodeAnalysisItem[] getCodeAnalysisItems() {
// 添加空指针和有效性检查
if (codeAnalysis == null || codeAnalysis.equals(Pointer.NULL) || codeAnalysisCount <= 0) {
System.out.println("codeAnalysis is null or NULL pointer, or textCount <= 0 (" + codeAnalysisCount + ")");
return new CodeAnalysisItem[0];
}
// 还需要知道codeAnalysis数组的长度,这里假设与某个字段相关
// 或者C++端提供了数组长度信息
try {
CodeAnalysisItem[] items = new CodeAnalysisItem[codeAnalysisCount];
for (int i = 0; i < codeAnalysisCount; i++) {
Pointer itemPointer = codeAnalysis.share(i * getCodeAnalysisItemSize());
items[i] = new CodeAnalysisItem(itemPointer);
}
return items;
} catch (Exception e) {
System.err.println("Error reading CodeAnalysisItem array: " + e.getMessage());
e.printStackTrace();
return new CodeAnalysisItem[0];
}
}
// 获取TextItem数组
public TextItem[] getTextItems() {
// 添加更严格的空指针检查
if (textItems == null || textItems.equals(Pointer.NULL) || textCount <= 0) {
System.out.println("textItems is null, NULL pointer, or textCount <= 0 (" + textCount + ")");
return new TextItem[0];
}
try {
TextItem[] items = new TextItem[textCount];
int textItemSize = getTextItemSize();
for (int i = 0; i < textCount; i++) {
// 计算每个TextItem的内存地址
Pointer itemPointer = textItems.share(i * textItemSize);
items[i] = new TextItem(itemPointer);
}
return items;
} catch (Exception e) {
System.err.println("Error reading TextItem array: " + e.getMessage());
e.printStackTrace();
return new TextItem[0];
}
}
// 获取SubComponent数组
public SubComponent[] getSubComponents() {
// 添加更严格的空指针检查
if (subComponents == null || subComponents.equals(Pointer.NULL) || subComponentCount <= 0) {
System.out.println("subComponents is null, NULL pointer, or subComponentCount <= 0 (" + subComponentCount + ")");
return new SubComponent[0];
}
try {
SubComponent[] components = new SubComponent[subComponentCount];
int subComponentSize = getSubComponentSize();
for (int i = 0; i < subComponentCount; i++) {
// 计算每个SubComponent的内存地址
Pointer componentPointer = subComponents.share(i * subComponentSize);
components[i] = new SubComponent(componentPointer);
}
return components;
} catch (Exception e) {
System.err.println("Error reading SubComponent array: " + e.getMessage());
e.printStackTrace();
return new SubComponent[0];
}
}
// 更准确地估算TextItem大小
private int getTextItemSize() {
// TextItem有一个String字段,在64位系统上指针通常是8字节
// 但需要考虑内存对齐,实际大小可能更大
return 8; // 先尝试8字节
}
// 更准确地估算SubComponent大小
private int getSubComponentSize() {
// SubComponent有两个String字段,加上可能的对齐填充
return 16; // 先尝试16字节
}
// 估算CodeAnalysisItem大小
private int getCodeAnalysisItemSize() {
// CodeAnalysisItem有两个String字段
return 16; // 先尝试16字节
}
@Override
public String toString() {
return "ComponentData{" +
"code='" + code + '\'' +
", description='" + description + '\'' +
", function='" + function + '\'' +
", standard='" + standard + '\'' +
", gtype='" + gtype + '\'' +
", codeAnalysis=" + codeAnalysis +
", textItems=" + textItems +
", textCount=" + textCount +
", subComponents=" + subComponents +
", subComponentCount=" + subComponentCount +
'}';
}
}
public interface ExampleLibrary extends Library {
// 加载动态库(Windows 加载 example.dll,Linux 加载 libexample.so)
ExampleLibrary INSTANCE = Native.load("libpipedll.dll", ExampleLibrary.class);
Pointer parseFileAndGetResult(String filename);
// 1. 映射子结构体 Point
}
public class JnaExample {
public static void main(String[] args) {
System.setProperty("jna.encoding", "GBK");
String path = "GD2016-CATALOGUE.txt";
Pointer parseResultPointer = ExampleLibrary.INSTANCE.parseFileAndGetResult(path);
ParseResult parseResult = new ParseResult(parseResultPointer);
ComponentData[] componentData = parseResult.getComponentData();
for (ComponentData data : componentData){
System.out.println(data);
}
Pointer components = parseResult.components;
ComponentData data = new ComponentData(components);
CodeAnalysisItem[] codeAnalysisItems = data.getCodeAnalysisItems();
for (CodeAnalysisItem item : codeAnalysisItems) {
System.out.println(item);
}
TextItem[] textItems = data.getTextItems();
for (TextItem item : textItems) {
System.out.println("===>" + item.text);
}
SubComponent[] subComponents = data.getSubComponents();
for (SubComponent item : subComponents) {
System.out.println(item);
}
// 注意:如果 C++ 中使用了 malloc 分配内存,需要手动释放
// Native.free(Pointer.nativeValue(strPtr));
}
}
public class ParseResult extends Structure {
public Pointer components;//标识数组(CATEGORY)
public int componentCount;//数量
public ParseResult() {
super();
}
public ParseResult(Pointer peer) {
super(peer);
read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("components","componentCount");
}
public static class ByReference extends ParseResult implements Structure.ByReference {
public ByReference() {}
public ByReference(Pointer peer) {
super(peer);
}
}
// 获取CodeAnalysisItem数组
public ComponentData[] getComponentData() {
// 添加空指针和有效性检查
if (components == null || components.equals(Pointer.NULL) || componentCount <= 0) {
System.out.println("components is null or NULL pointer, or componentCount <= 0 (" + componentCount + ")");
return new ComponentData[0];
}
// 还需要知道codeAnalysis数组的长度,这里假设与某个字段相关
// 或者C++端提供了数组长度信息
try {
ComponentData[] items = new ComponentData[componentCount];
for (int i = 0; i < componentCount; i++) {
Pointer itemPointer = components.share(i * getComponentCountSize());
items[i] = new ComponentData(itemPointer);
}
return items;
} catch (Exception e) {
System.err.println("Error reading CodeAnalysisItem array: " + e.getMessage());
e.printStackTrace();
return new ComponentData[0];
}
}
private int getComponentCountSize() {
return 88;
}
@Override
public String toString() {
return "ParseResult{" +
"components=" + components +
", componentCount=" + componentCount +
'}';
}
}
public class SubComponent extends Structure {
public String name;
public String params;
public SubComponent() {
super();
}
public SubComponent(Pointer peer) {
super(peer);
read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("name","params");
}
// 用于按引用传递(指针)
public static class ByReference extends SubComponent implements Structure.ByReference {
public ByReference() {
super();
}
public ByReference(Pointer peer) {
super(peer);
read();
}
}
@Override
public String toString() {
return "SubComponent{" +
"name='" + name + '\'' +
", params='" + params + '\'' +
'}';
}
}
JNA 本身不直接支持 C++ 的 std::map 类型(因为它是 C++ 标准库的模板类,内存布局不固定)。要在 JNA 中接收 std::map 数据,需要通过中间转换层将 std::map 转换为 JNA 可识别的结构(如结构体数组、指针数组等)。
方案一:C++ 端转换为结构体数组(推荐)
在 C++ 中将 std::map 转换为键值对结构体数组,再通过 JNA 读取数组。
- C++ 端实现(转换 std::map 为数组)
#include <map>
#include <string>
// 定义键值对结构体(供JNA映射)
struct MapEntry {
const char* key; // 键(字符串指针)
const char* value; // 值(字符串指针)
};
// 定义包含数组和长度的结构体
struct MapWrapper {
MapEntry* entries; // 键值对数组
int count; // 元素数量
};
// 转换std::map为MapWrapper
extern "C" __declspec(dllexport) MapWrapper* convertMap() {
// 示例map
std::map<std::string, std::string> cppMap = {
{"TEE", "三通"},
{"TUBE", "钢管"},
{"BEND", "弯管"}
};
// 分配包装器内存
MapWrapper* wrapper = new MapWrapper();
wrapper->count = cppMap.size();
wrapper->entries = new MapEntry[wrapper->count];
// 填充数组
int i = 0;
for (const auto& pair : cppMap) {
wrapper->entries[i].key = pair.first.c_str();
wrapper->entries[i].value = pair.second.c_str();
i++;
}
return wrapper;
}
// 释放内存的函数
extern "C" __declspec(dllexport) void freeMapWrapper(MapWrapper* wrapper) {
if (wrapper) {
delete[] wrapper->entries; // 释放数组
delete wrapper; // 释放包装器
}
}
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.nio.charset.StandardCharsets;
// 1. 映射C++的键值对结构体
public class MapEntry extends Structure {
public Pointer key; // 对应const char*
public Pointer value; // 对应const char*
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("key", "value");
}
}
// 2. 映射C++的MapWrapper结构体
public class MapWrapper extends Structure {
public MapEntry entries; // 键值对数组(首元素指针)
public int count; // 元素数量
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("entries", "count");
}
public static class ByReference extends MapWrapper implements Structure.ByReference {}
}
// 3. 映射C++导出函数
public interface MapLibrary extends Library {
MapLibrary INSTANCE = Native.load("MapLib", MapLibrary.class);
// 调用C++函数获取转换后的map数据
MapWrapper.ByReference convertMap();
// 调用C++函数释放内存
void freeMapWrapper(MapWrapper.ByReference wrapper);
}
// 4. 读取并转换为Java Map
public class StdMapHandler {
public static void main(String[] args) {
MapWrapper.ByReference wrapper = MapLibrary.INSTANCE.convertMap();
try {
// 转换为Java的HashMap
Map<String, String> javaMap = new HashMap<>();
MapEntry entry = new MapEntry();
for (int i = 0; i < wrapper.count; i++) {
// 计算数组元素地址(每个元素大小为MapEntry的size)
Pointer entryPtr = wrapper.entries.getPointer().share(i * entry.size());
entry.useMemory(entryPtr);
entry.read(); // 读取数据
// 解码字符串(根据C++实际编码调整,如GBK)
String key = entry.key.getString(0, StandardCharsets.UTF_8.name());
String value = entry.value.getString(0, StandardCharsets.UTF_8.name());
javaMap.put(key, value);
}
// 打印结果
System.out.println("Java Map: " + javaMap);
} finally {
// 释放C++分配的内存
MapLibrary.INSTANCE.freeMapWrapper(wrapper);
}
}
}
1377

被折叠的 条评论
为什么被折叠?



