gpt4 book ai didi

java - JNA 从结构内的结构数组中读取无效的内存访问

转载 作者:行者123 更新时间:2023-11-30 09:59:37 26 4
gpt4 key购买 nike

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

DLL 的 header 在 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() 没有区别。

无论值(value)如何,这就是它在 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

最佳答案

您没有正确映射字符串。

您已将 szPort 定义为 PointerByReference,它是指向包含指针的内存位置的指针。然后您尝试使用 inner.szPort.getPointer() 调用它。那仍然是相同的“指向指针的指针”。 JNA 的一个很好的经验法则是,如果您正在使用 ByReference 类并且从不访问它的 getValue() 方法,那么您可能做错了什么。您的意思可能是 inner.szPort.getValue().getString(...)。但这会失败,因为您实际上没有真正的指针。填充 szPort 元素的前 4 个字节实际上是 unicode 字符。

您已经定义了包含 3 个 4 字节元素(3 x 4 字节指针)的结构,总共 12 个字节(根据您的调试图像)。您实际上需要一个 7 个字符的固定宽度字符串、4 个字节的 boolean 值和 248 个字符的固定宽度字符串。如果您的编码是 ASCII,则为 259 个字节,或者在您的情况下,Unicode 字符串为 514 个字节。

查看您的调试图像,我们可以看到发生了什么。前 4 个字节包含 0x0043004f,您将其视为指针(地址 0x4f0043)并尝试从中读取数据。但它实际上是 unicode 字符 0x0043(“C”)和 0x004f(“O”)。当您尝试从该地址读取时,您并不拥有内存。如果幸运的话,内存为零并且您从未读取任何内容,并且您的代码返回一个零长度的空终止字符串。但是,如果该内存不是 null,则会出现错误。

接下来的 4 个字节是 0x004d0035,您将其称为 IntByReference“指针”(0x35004d),但它实际上是字符“M”和“5”。由于 szName 字段有一个空指针,接下来的 4 个字节似乎是 0x00000000 并且您已经命中了空终止符。所以它看起来像你的 szPort 字符串是“COM5”,隐藏在众目睽睽之下!

现在我们知道您的代码为什么会出错了。你如何修复它?

您的 C# 映射定义 szPort 的类型是 UnmanagedType.ByValTStraccording to MS Docs , 是:

A fixed-length array of characters; the array's type is determined by the character set of the containing structure.

您可能需要该结构中的字节数组(您可以使用适当的编码将其放入 String 构造函数中),长度为 PORT_SIZE x 字符宽度(如果您的编码是 ASCII,则为 1;对于 Unicode,则为 2)。您应该使用 charwidth x MAX_NAME_SIZE 类似地映射 szName

最后,IntByReference 也是一个指针(在您的系统上是 4 个字节),但 C# 中的原始变量是一个 bool。如果不对其进行编码(marshal)处理,它会映射到 Windows BOOL,这是一个普通的 4 字节 C int。 Java 的 boolean 也是 4 个字节,所以你可以在这里使用它。所以你可能想要这样的东西:

int CHAR_WIDTH = W32APITypeMapper.DEFAULT == W32APITypeMapper.UNICODE ? 2 : 1;
int PORT_SIZE = 7;
int MAX_NAME_SIZE = 248;

class InnerStructure ... {
public byte[] szPort = new byte[CHAR_WIDTH * PORT_SIZE];
public boolean fActivated;
public byte[] szName = new byte[CHAR_WIDTH * MAX_NAME_SIZE];
...
}

关于java - JNA 从结构内的结构数组中读取无效的内存访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58700626/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com