C# out IntPtr 等效于 Java

C# out IntPtr equivalent in Java

我在 C# 中有一个从 .dll 调用的方法

[DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int find([MarshalAs(UnmanagedType.AnsiBStr, SizeConst = 64)] string atr, out IntPtr int);

[DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int getData(IntPtr int, int dataId, byte[] dataBuffer, ref int dataBufferSize);

在 C# 中,此方法的调用如下所示

static IntPtr number = IntPtr.Zero;
static int res = 0;
try{
    number = IntPtr.Zero;
    res = find(null, out number);   
    if (number == IntPtr.Zero)
                throw new ApplicationException("Something is wrong");
    uint dataBufferSize = 1024;
    res = getData(number, 1, null, ref dataBufferSize);
}

我没有找到 Java 中的等价物。

如果我这样做:

public int find(String atr, Pointer int);

它说

java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeInt(Native Method)
at com.sun.jna.Function.invoke(Function.java:419)
at com.sun.jna.Function.invoke(Function.java:354)
at com.sun.jna.Library$Handler.invoke(Library.java:244)

如果我这样做:

 public int find(String atr, IntByReference int);

没有任何反应。

Java代码

IntByReference iref = new IntByReference();                     
res = find(null, iref);                                             
Pointer pointer = iref.getPointer();                            
int dataBufferSize = 1024;
byte[] dataBuffer = new byte[dataBufferSize];
res = getData(Pointer.nativeValue(pointer), 1, dataBuffer, dataBufferSize);

find returns 0,表示OK,但是getData returns 6,表示内存地址不好。什么都没有发生,我的意思是除 0 之外的任何其他资源。

IntByReference 是可能的正确映射(取决于指针指向的内容);但是,您要查找的值是相应 Pointer 中的本机 "peer" 值。您可能想改用 PointerType。无论哪种方式, Invalid Memory Access 都与尝试访问您尚未分配的本机端内存有关。一个普通的 Pointer 不分配任何本地内存:通常你会使用 new Memory() 来初始化一个指针并分配它指向的内存。 (如果您创建 ByReference 类型之一,它会在那里分配适当的大小,这就是错误消失的原因。)

一个 IntPtr 是一个整数值,表示实际内存地址,您必须使用 getPointer()IntByReferencePointerType 中提取它以获得 Pointer 对象,然后将其传递给 Pointer.nativeValue() 到 return 对应于您的 IntPtr.

的 (long) 内存地址

已根据您编辑的代码更新:

一些可能导致问题的错误映射:

getData() 的签名要求 dataBufferSize 是对 int 的引用,因此您应该为该变量使用 IntByReference

您不能将普通数组从 Java 传递到本机端。您需要为其分配内存。例如:

Pointer dataBuffer = new Memory(dataBufferSize);
// call getData();
byte[] foo = dataBuffer.getByteArray(0, dataBufferSize);

补充更新:

看起来 find() 方法调用 return 是指向您想要的内存地址的指针,因此您从 find() 中获取结果 IntByReference 并使用 getValue() 将该整数传递给 getData().

您可能需要进一步处理生成的 byte[] 数组。您没有提供 API 的文档,但如果 return 是 dataBufferSize 的不同值,您可能需要截断字节数组以匹配。由于您将传递给 getData()IntByReference 命名为 iref,这将是:

byte[] dataByteArray = new byte[iref.getValue()];
System.arraycopy( dataBuffer, 0, dataByteArray, 0, iref.getValue());
String data = new String(dataByteArray, "UTF-8");

如果 C 字符串的编码不同于 ASCII,您可能需要使用不同的转换。

如果我将 number.getValue() 传递给 getData(),它会起作用。现在 res 为 0.

完整代码:

IntByReference number=new IntByReference(0);                        
res = find(null, number);                                   
int dataBufferSize = 1024;
IntByReference iref=new IntByReference(dataBufferSize);
Pointer array = new Memory(dataBufferSize);                         
res = getData(number.getValue(),1, array,iref);                 
byte[] dataBuffer=array.getByteArray(0,dataBufferSize);