无效的内存访问,其中JNA从结构内部的结构数组中读取

问题描述 投票:1回答:1

我的任务是将C#应用程序迁移到Java应用程序。 C#应用程序使用几个DLL来完成其工作,并与外围设备进行通信。

DLL的头在C#中看起来像这样

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct InnerStructure
    {
        /// COM port used by the device
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = PORT_SIZE)] //7
        public string szPort;            

        /// Specifies whether the device is activated.
        public bool fActivated;         

        /// Name of the device
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_NAME_SIZE)]  //248
        public string szName;             //COM
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct ParamStructure
    {
        /// COM port used by the devices
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
        public InnerStructure[] USB;
    }

    [DllImport("PCLService.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern bool startPclService();

    [DllImport("PCLService.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern bool stopPclService();

    [DllImport("PclUtilities.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
    public static extern int getUSBDevices(ref ParamStructure pStructure, ref int pdwSize, ref int pdwEntries);

我最初尝试使用JNI,但是我无法设法加载DLL(它们使用.NET,并且很难通过DependenciesWalker查找依赖项,所以我切换到JNA。

这是我的Java代码

public class DllDemo {

    @Structure.FieldOrder({ "szPort", "fActivated", "szName" })
    public static class InnerStructure extends Structure {
        public PointerByReference szPort;
        public IntByReference fActivated;
        public PointerByReference szName;

        public InnerStructure() {};
        public InnerStructure(Pointer p) {
            super(p);
            read();
        };
    }

    @Structure.FieldOrder({ "USB" })
    public static class ParamStructure extends Structure implements Structure.ByReference {
        // In the C# code, the array is size 10
        public InnerStructure[] USB = (InnerStructure[])new InnerStructure().toArray(10);

        public ParamStructure() {};
        public ParamStructure(Pointer p) {
            super(p);
            read();
        };
    }

    public interface MyService extends Library {
        MyService INSTANCE = (MyService) Native.load("C:\\IRD\\Documentacion\\VisaNet\\App PCL Demo VisaNet - V2.11\\x32\\PCLService.dll", MyService.class);
        boolean startPclService();
        boolean stopPclService();
    }

    public interface MyUtilities extends Library {
        MyUtilities INSTANCE = (MyUtilities) Native.load("C:\\IRD\\Documentacion\\VisaNet\\App PCL Demo VisaNet - V2.11\\x32\\PclUtilities", MyUtilities.class);
        int getUSBDevices(ParamStructure paramStructure, IntByReference pdwSize, IntByReference pdwEntries);
    }

    public static void main(String args[]) {
        System.out.println("start");

        MyService.INSTANCE.startPclService();
        ParamStructure paramStructure = new ParamStructure();
        paramStructure.write();
        //This value is copied straightforward from the original code as well
        int size = (248 + 8 + 7) * 10;
        IntByReference pdwSize = new IntByReference(size);
        IntByReference pdwEntries = new IntByReference(0);

        int Ret2 = MyUtilities.INSTANCE.getUSBDevices(paramStructure, pdwSize, pdwEntries);
        System.out.println("Ret2 = " + Ret2 + ", pdwEntries = " + pdwEntries.getValue());
        if (pdwEntries.getValue() > 0) {
            for (int i = 0 ; i < pdwEntries.getValue() ; i++) {
                InnerStructure inner = paramStructure.USB[i];
                inner.read();
                System.out.println(i + " => " + inner.toString());
                System.out.println("toString 1 => " + inner.szPort.toString());
                System.out.println("toString 2 => " + inner.szPort.getPointer().toString());
                System.out.println(">" + inner.szPort.getPointer().getString(0, "utf8") + "<");
            }
        }
        paramStructure.clear();
        MyService.INSTANCE.stopPclService();
        System.out.println("stop");
    }
}

这是输出。

start
Ret2 = 0, pdwEntries = 1
0 => DllDemo$InnerStructure(allocated@0x59b550 (12 bytes) (shared from auto-allocated@0x59b550 (120 bytes))) {
  PointerByReference szPort@0x0=native@0x4f0043 (com.sun.jna.ptr.PointerByReference@4f0043)
  IntByReference fActivated@0x4=native@0x35004d (com.sun.jna.ptr.IntByReference@35004d)
  PointerByReference szName@0x8=null
}
toString 1 => native@0x4f0043 (com.sun.jna.ptr.PointerByReference@4f0043)
toString 2 => native@0x4f0043
Exception in thread "main" java.lang.Error: Invalid memory access
    at com.sun.jna.Native.getStringBytes(Native Method)
    at com.sun.jna.Native.getString(Native.java:2224)
    at com.sun.jna.Pointer.getString(Pointer.java:681)
    at com.ingenico.DllDemo.main(DllDemo.java:65)
Process finished with exit code 1

第65行是这个

System.out.println(">" + inner.szPort.getPointer().getString(0, "utf8") + "<");

有时,它没有给出错误,但字符串为空。我还不知道为什么会这样。

[这两个类中是否存在构造函数以及paramStructure.write()和inner.read()行没有区别。

对于任何有价值的东西,这就是它在IntelliJ中的调试器中的外观

debugger

我已经尝试过这样改变内部结构

public static class InnerStructure extends Structure implements Structure.ByReference {
    public PointerByReference szPort;
    public IntByReference fActivated;
    public PointerByReference szName;

    public InnerStructure() {};
    public InnerStructure(Pointer p) { super(p); };
}

甚至是这样。

public static class InnerStructure extends Structure implements Structure.ByReference {
    public String szPort;
    public int fActivated;
    public String szName;

    public InnerStructure() {};
    public InnerStructure(Pointer p) { super(p); };
}

在两种情况下,我都知道

Exception in thread "main" java.lang.Error: Invalid memory access
    at com.sun.jna.Native._getPointer(Native Method)
    at com.sun.jna.Native.getPointer(Native.java:2211)
    at com.sun.jna.Pointer.getPointer(Pointer.java:642)
    at com.sun.jna.Pointer.getValue(Pointer.java:390)
    at com.sun.jna.Structure.readField(Structure.java:732)
    at com.sun.jna.Structure.read(Structure.java:591)
    at com.sun.jna.Structure.autoRead(Structure.java:2141)
    at com.sun.jna.Structure.conditionalAutoRead(Structure.java:561)
    at com.sun.jna.Structure.updateStructureByReference(Structure.java:690)
    at com.sun.jna.Pointer.readArray(Pointer.java:492)
    at com.sun.jna.Pointer.getValue(Pointer.java:450)
    at com.sun.jna.Structure.readField(Structure.java:732)
    at com.sun.jna.Structure.read(Structure.java:591)
    at com.sun.jna.Structure.autoRead(Structure.java:2141)
    at com.sun.jna.Function.invoke(Function.java:381)
    at com.sun.jna.Library$Handler.invoke(Library.java:265)
    at com.sun.proxy.$Proxy3.getUSBDevices(Unknown Source)
    at com.ingenico.DllDemo.main(DllDemo.java:49) <- getUSBDevices
jna
1个回答
0
投票

您没有正确访问所需的Pointer值。

您已经将szPort定义为PointerByReference,它是指向String所在实际指针的指针。

但是您正试图用inner.szPort.getPointer()来调用它。那仍然是“指向指针的指针”。使用JNA的一个好的经验法则是,如果您使用的是ByReference类并且从不访问其上的getValue()方法,则可能做错了什么。

您的映射应适用于inner.szPort.getValue().getString(...)

© www.soinside.com 2019 - 2024. All rights reserved.