3Dayss 2024-07-24 09:02 采纳率: 33.3%
浏览 13

一个有关于Java通过JNA调用dll程序,一个带数组结构体的接口无法调用的问题。

一个有关于Java通过JNA调用dll程序,一个带数组结构体的接口无法调用的问题。
在尝试调用一个创芯科技公司的CAN通讯的dll程序,有一个接口方法需要放数组,但是这个数组的创建出了问题。


情况1.首先通过正常数组实例化的方式执行,会报异常“java.lang.IllegalArgumentException: Structure array elements must use contiguous memory (bad backing address at Structure array index 1)”,大概是说结构体数组的内存地址不连续。

img


情况2.后通过网上查阅资料得知要想获得内存地址连续的结构体数组,需要用到JNA包下Structure类的toArray()方法。但是在实际使用中又会报错java.lang.IllegalStateException: Array fields must be initialized,大概是说数组字段必须要实例化。

img


问题点:怎样才可以解决上面两种情况,或者有第三钟方法可以使之正常调用这个接口方法。


有关于dll文件和使用指引来源
https://www.zhcxgd.com/1.html


以下是当前调试的代码

public class CanUtil {
    public interface testDlls extends Library {
//        testDlls INSTANCE = (testDlls) Native.loadLibrary("ControlCAN", testDlls.class);  // ControlCAN.dll
        
        Integer VCI_OpenDevice(Integer DevType, Integer DevIndex, Integer Reserved);
        Integer VCI_CloseDevice(Integer DevType, Integer DevIndex);
        Integer VCI_Transmit(Integer DeviceType, Integer DeviceInd, Integer CANInd, VCI_CAN_OBJ[] pSend, Integer Length);
    }
    
    public static void main(String[] args) {
        try {
            testDlls dll = (testDlls) Native.loadLibrary("ControlCAN", testDlls.class);  // ControlCAN.dll
            System.out.println("2222");
//            
//            int result = dll.VCI_OpenDevice(4, 0, 0);
//            System.out.println("result: " + result);
//            
//            result = dll.VCI_CloseDevice(4, 0);
//            System.out.println("result: " + result);
            
            // 数组容量
            int cycle = 2;// 当数组容量为1 且 数组由正常数组实例化构成(method=false)时, 确认可执行通过, 下方result结果为0.
            
            // 表示选择哪种方法
            boolean method = true;
            
            VCI_CAN_OBJ[] vco;
            if (method) {
                // 通过实体类继承的Structure类中的toArray(arraySize)方法获得连续存储地址的数组, 但是会报错说没有被实例化
                vco = (VCI_CAN_OBJ[]) new VCI_CAN_OBJ().toArray(cycle);// <<---- 就是在这里发生报错: java.lang.IllegalStateException: Array fields must be initialized
            } else {
                // 正常的数组实例, 但是会报错说数组内存地址不是连续的
                vco = new VCI_CAN_OBJ[cycle];// <<---- 会在下面调用接口方法的时候发生报错: java.lang.IllegalArgumentException: Structure array elements must use contiguous memory (bad backing address at Structure array index 1)
            }
            
            // 通过遍历数组进行数据保存
            for (int i = 0; i < cycle; i++) {
                vco[i] = new VCI_CAN_OBJ();
                vco[i].ID = (byte)i;
                vco[i].RemoteFlag = (byte)0;
                vco[i].ExternFlag = (byte)0;
                vco[i].DataLen = (byte)1;
                byte[] data = new byte[1];
                data[0] = 0x66;
                vco[i].Data = data;
            }
            
            Integer result = -2;
            result = dll.VCI_Transmit(4, 0, 0, vco, cycle);
            System.out.println("result: " + result);
            
        } catch (Exception e) {
            // TODO: handle exception
            System.out.println(e.toString());
            e.printStackTrace();
        }
    }
}

以下是结构体代码:

public class VCI_CAN_OBJ extends Structure {
    public byte ID;
    
    public byte RemoteFlag;
    
    public byte ExternFlag;
    
    public byte DataLen;
    
    public byte[] Data;
    
    public VCI_CAN_OBJ() {};
    
    public static class ByReference extends VCI_CAN_OBJ implements Structure.ByReference {}
    public static class ByValue extends VCI_CAN_OBJ implements Structure.ByValue {}
    
    //定义取值次序,需要与C/C++中对齐,不然会出现NoSuchFieldError
    @Override
    protected List getFieldOrder() {
        return Arrays.asList(new String[]{"ID", "RemoteFlag", "ExternFlag", "DataLen", "Data"});
    }
    
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("ID: " + this.ID + ", ");
        sb.append("RemoteFlag: " + this.RemoteFlag + ", ");
        sb.append("ExternFlag: " + this.ExternFlag + ", ");
        sb.append("DataLen: " + this.DataLen + ", ");
        if (this.Data != null) {
            sb.append(" { ");
            for(byte unit : Data) {
                sb.append(unit + ", ");
            }
            sb.replace(sb.length()-2, sb.length(), " } ");
        }
        return sb.toString();
    }
}

  • 写回答

3条回答 默认 最新

  • 阿里嘎多学长 2024-07-24 09:02
    关注

    以下内容由AIGC及阿里嘎多学长共同生成、有用望采纳:


    根据您提供的信息和代码,您在使用Java和JNA库调用DLL时遇到了两个主要问题:

    1. 正常数组实例化时内存地址不连续:这是因为Java中的对象数组在内存中不是连续存储的,每个对象都是一个引用,指向堆内存中的不同位置。
    2. 使用Structure.toArray()方法时数组字段未实例化:这是因为toArray()方法要求传入的数组元素已经被实例化,否则会抛出IllegalStateException

    为了解决这些问题,我们可以尝试以下步骤:

    解决方案:

    1. 使用Structure数组:由于JNA需要结构体数组的内存地址连续,我们可以使用Structure数组来代替普通的Java对象数组。Structure数组可以保证内存地址的连续性。

    2. 正确实例化Structure数组元素:在使用toArray()方法之前,确保每个Structure数组元素已经被实例化。

    修改后的代码示例:

    public class CanUtil {
        // ... 其他代码保持不变 ...
    
        public static void main(String[] args) {
            // ... 其他代码保持不变 ...
    
            int cycle = 2; // 数组容量
            boolean method = true; // 表示选择哪种方法
    
            VCI_CAN_OBJ[] vco;
    
            if (method) {
                // 使用toArray方法创建连续内存地址的Structure数组
                Structure[] tempArray = new Structure[cycle];
                for (int i = 0; i < cycle; i++) {
                    tempArray[i] = new VCI_CAN_OBJ();
                }
                // 将Structure数组转换为VCI_CAN_OBJ数组
                vco = new VCI_CAN_OBJ[tempArray.length];
                for (int i = 0; i < tempArray.length; i++) {
                    vco[i] = (VCI_CAN_OBJ) tempArray[i].toArray(new VCI_CAN_OBJ[1])[0];
                }
            } else {
                // 这里不推荐使用,因为会导致内存地址不连续的问题
                vco = new VCI_CAN_OBJ[cycle];
            }
    
            // 初始化数组元素
            for (int i = 0; i < cycle; i++) {
                vco[i].ID = (byte) i;
                vco[i].RemoteFlag = (byte) 0;
                vco[i].ExternFlag = (byte) 0;
                vco[i].DataLen = (byte) 1;
                byte[] data = new byte[]{0x66};
                vco[i].Data = data;
            }
    
            // ... 其他代码保持不变 ...
        }
    }
    

    注意事项:

    • 确保VCI_CAN_OBJ类正确继承自Structure,并且getFieldOrder()方法返回正确的字段顺序。
    • 使用toArray()方法时,需要先创建一个Structure数组,并确保每个元素都被实例化。
    • 代码示例中假设VCI_CAN_OBJ类已经正确地继承自Structure

    运行环境:

    • Java版本:建议使用Java 1.6或更高版本。
    • JNA库版本:建议更新到最新版本以获得更好的兼容性和性能。

    通过上述修改,应该能够解决您遇到的问题,并成功调用DLL中的接口方法。如果还有问题,请检查DLL的文档和JNA的使用文档,确保所有调用都符合要求。

    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 7月24日

悬赏问题

  • ¥15 ssh登录页面的问题
  • ¥60 渗透一个指定银行app,拿到客户信息,需要什么级别
  • ¥50 关于在matlab上对曲柄摇杆机构上一点的运动学仿真
  • ¥15 jetson nano
  • ¥15 :app:debugCompileClasspath'.
  • ¥15 windows c++内嵌qt出现数据转换问题。
  • ¥15 stm32 串口通讯过程中的问题
  • ¥20 公众号如何实现点击超链接后自动发送文字
  • ¥15 用php隐藏类名和增加类名
  • ¥15 算法设计与分析课程的提问