I need to access a "native" C library from Java via Java Native Access (JNA). The idiomatic way to do this seems to be defining an interface that extends Library
and that declares all the functions which I need to access. Then create a singleton instance, via Native.load()
method:
public interface MyLibrary extends Library {
MyLibrary INSTANCE = Native.load("mylib", MyLibrary.class);
int some_function(int param);
}
This works fine with functions. But how do I access global variables via the JNA interface?
In the C header file, it is defined like:
extern const uint16_t SOME_GLOBAL_VARIABLE;
There is little to no information available on how to access global variables via the JNA interface. The closest thing I have found is the NativeLibrary.getGlobalVariableAddress()
method. But, unfortunately, this method is in NativeLibrary
class, not in the Library
interface!
And there also seems to be no way to get a NativeLibrary
instance when working with a custom interface (that is derived from the Library
interface) and with the Native.load()
method. I probably could use NativeLibrary.getInstance()
instead, in order to get a NativeLibrary
instance and to be able to use getGlobalVariableAddress()
. But then, how do I apply my Library
-interface to a NativeLibrary
instance in order to be able to invoke the functions?
Loading the same library twice, once via Native.Load()
in order to be able to call the functions via my interface, and once via NativeLibrary.getInstance()
in order to be able to get the global variable address, seems like a very ugly workaround/hack, provided that it would work at all.
So, to make a long story short, is there any way in JNA to go from the Library
interface to the underlying NativeLibrary
instance, or vice versa?
Or, alternatively, how to declare a global variable directly in the JNA interface? 🤔
Based on the suggestion by Daniel Widdis, I was able to come up with a solution that is based on java.lang.reflect.Proxy
in order to get the Handler
instance out of the Library
interface via the Proxy.getInvocationHandler()
method, which then allows me to call getNativeLibrary()
on the Handler object to obtain the required NativeLibrary
instance:
import java.lang.reflect.Proxy;
import com.sun.jna.Library;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public interface MyLibrary extends Library {
static final MyLibrary INSTANCE = Native.load("mylib123", MyLibrary.class);
static class NativeLibraryHolder {
public static final NativeLibrary NATIVE_LIBRARY = ((Handler)Proxy.getInvocationHandler(MyLibrary.INSTANCE)).getNativeLibrary();
}
static class SOME_GLOBAL_VARIABLE {
private static final Pointer ADDRESS = NativeLibraryHolder.NATIVE_LIBRARY.getGlobalVariableAddress("SOME_GLOBAL_VARIABLE");
int get() { return ADDRESS.getInt(0L); }
void set(final int newValue) { ADDRESS.setInt(0L, newValue); }
}
int some_function(int param);
}