I have a methods in C# which I call from .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);
In C# call of this methods looks like this
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);
}
I didn't find what would be equivalent in Java.
If I do it like this:
public int find(String atr, Pointer int);
It says
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)
If I do it like this:
public int find(String atr, IntByReference int);
Nothing happens.
Java code
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, which means OK, but getData returns 6 which means that memory address is not good. By nothing happens I mean any other res than 0.
IntByReference
is a possible correct mapping (depending on what the pointer is pointing to); however, the value you're looking for is the native "peer" value inside the corresponding Pointer
. You may want to use PointerType
instead. Either way, the Invalid Memory Access
is related to trying to access Native-side memory that you have not allocated. A plain Pointer
doesn't allocate any memory Native-side: normally you would use a new Memory()
to initialize a Pointer and allocate the memory it's pointing to. (If you create one of the ByReference
types, it allocates the appropriate size there, which is why that error goes away.)
An IntPtr
is an integer value representing the actual memory address, which you have to extract from the IntByReference
or PointerType
by using getPointer()
to get the Pointer
object, and then passing that to Pointer.nativeValue()
to return the (long
) memory address corresponding to your IntPtr
.
Updated in response to your edited code:
A few wrong mappings that may be causing problems:
The signature for getData()
requires dataBufferSize
to be a reference to an int, so you should use IntByReference
for that variable.
You cannot pass a plain array from Java to the native side. You need to allocate memory for it. For example:
Pointer dataBuffer = new Memory(dataBufferSize);
// call getData();
byte[] foo = dataBuffer.getByteArray(0, dataBufferSize);
Additional update:
It appears that the find()
method call returns a pointer to the memory address you want, so you take the resulting IntByReference
from find()
and use getValue()
to pass that integer to getData()
.
You may need to further process the resulting byte[]
array. You haven't given the documentation for the API but if it returns a different value for dataBufferSize
you may need to truncate your byte array to match. Since you named the IntByReference
you passed to getData()
as iref
, this would be:
byte[] dataByteArray = new byte[iref.getValue()];
System.arraycopy( dataBuffer, 0, dataByteArray, 0, iref.getValue());
String data = new String(dataByteArray, "UTF-8");
If the C string is encoded differently than ASCII you may need to use a different conversion.