简体   繁体   中英

How to pass int as a C++ function parameter from Java using JNA

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM