I'm currently working on a project which requires developing a native DLL (in C++) to be accessed by a Java application. I have chosen JNA for the bridging job and I am facing problems passing correct int values from Java to C++ functions.
Simply put, I have a function that accepts an int value as parameter in C++: (code is stripped and methods are renamed to maintain confidentiality)
JAVALINK_EXPORT SomeStructure WINAPI GetSomeStructureFromIndex(int index) {
std::string debugMsg("Received index of ");
debugMsg.append(toString(index));
OutputDebugString(debugMsg.c_str());
SomeStructure result = defaultStructure;
if (index >= 0 && index < structListSize)
result = structList[index];
return result;
}
toString
is a simple method that converts a value of any data type to std::string
using a std::stringstream
. The implementation is as follows:
template <class T>
inline std::string toString (const T& t) {
std::stringstream ss;
ss << t;
return ss.str();
}
SomeStructure
is renamed from an actual struct I'm using in the code. structList
is an array of SomeStructure
. structListSize
and structList
are all global variables in shared memory.
This is the method signature in the Java interface for the DLL:
SomeStructure.ByValue GetSomeStructureFromIndex(int index);
This is how I use the method in Java for the test case:
SomeStructure.ByValue received = library.GetSomeStructureFromIndex(1);
library
is an instance of the interface for the DLL file (subclass of StdCallLibrary
) generated using Native.loadLibrary
. When the above code is executed in Java, I get something like the following output in my Windows debug output:
Received index of 86701080
(The program will then proceed to encounter an access violation error if I have omitted the check for index
with the line if (index >= 0 && index < structListSize)
before obtaining the structure from the array)
86701080
can be any arbitrary value. I realized it changes according to the signature of the exported function. Am I missing something here? The function properly receives an expected value of 1
had the function signature been void PrintIndex(int index)
EDIT(0): I have modified the sample code to match the actual code more closely.
EDIT(1): According to @technomage's pointers I have started using ByValue
for all method signatures and variables collecting returned structures.
EDIT(2): The Java class SomeStructure
has an extra variable and a Java method compared to the SomeStructure
struct in C++. I'm currently testing if that is the cause of the discrepancy.
PROBLEM SOLVED
@technomage explained that for a C++ function to interpret its arguments and return values as it is expected to, the size of the structure used as return type (and also those used as function parameters) should not be different from its Java counterpart. This can be checked, in the case of SomeStructure
, in C++ with sizeof(SomeStructure)
and in Java with SomeStructure.size()
.
Basically, what happened was that the SomeStructure
struct has a different size than its Java representation. SomeStructure
contained a fixed-length array as illustrated in the code below:
#define MAX_LIST_SIZE 256
typedef struct {
int list[MAX_LIST_SIZE];
int length;
} SomeStructure;
However, the Java representation did not specify the size of the fixed-length array. list
was initialized to contain a single value of 0
.
package model;
import com.sun.jna.Structure;
public class SomeStructure extends Structure {
public static class ByValue extends SomeStructure implements Structure.ByValue { }
public int[] list = {0};
public int length = 0;
}
I solved the problem by replacing the faulty initialization statement with the following instead:
private static final int MAX_LIST_SIZE = 256;
public int[] list = new int[MAX_LIST_SIZE];
Note : The integer constant MAX_LIST_SIZE
is declared private
to keep it Java-only.
After making all these amendments, my code is working fine and no longer runs into access violations.
'WINAPI' implies stdcall calling convention, but you'd have to look at your local definition of the macro to be certain. If so, you need StdCallLibrary
instead of Library
. This would likely affect your incoming index
argument.
You are also copying your structure (by value semantics) as opposed to a pointer to it. When you pass a structure by value or return a structure by value, you need to tell JNA that you're doing so .
EDIT
Make sure SomeStructure.size()
matches the native sizeof(SomeStructure)
. Usually structures returned by value are implemented such that the caller allocates memory on the stack and passes an implicit pointer to the callee, which then writes to that memory. If the caller and callee disagree on the size of that memory, it can affect other things on the stack (such as arguments and return values). You can also add several more arguments to the function and print their values (as hex) to get an approximation of what's on the stack. If you pass in recognizable arguments (eg 0x12345678), it often becomes clear what's pushing or pulling the stack in the wrong direction.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.