Jna调用c语言

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 读取数组。

  1. 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);
        }
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值