Calling dll implemented JNI from C++ - java

Situation:
I have a dll which implemented JNI,I want to call the methods in it from a Cpp application.
Current Status:
According to my understanding, a dll which implemented JNI actually has nothing to do with JAVA, for example:
In Test.java, I wrote
public native int Add(int a,int b);
And realized it with Cpp In TestDll.Cpp
JNIEXPORT jint JNICALL Java_SomeNamespace_Add(JNIEnv* _Env, jobject _Object, jint a, jint b)
{
return a+b;
}
I think that such a program has nothing to do with JVM, the jint structure seems have been fully defined in jni.h.
So, I wonder if it's possible to call Java_SomeNamespace_Add directly without creating a VM from a Cpp application, if it do is possible,:
What should the `JNIEnv*` and the `jobject` in the parameter list be?
How can i convert a `jint` variable to a standard `int` variable?
How can i convert a `jstring` variable to a standard `string` variable without using `_Env->FindClass("Ljava/lang/String;")` and a bunch of following code?

For number 1 I think it would be a good idea to just create a simple alternative using a bit of refactoring.
Instead of putting the logic of the algorithm into the JNI call itself, move the logic to a separate c++ function and invoke that function from both use cases.
ie:
// MyJNIWrapper.cpp
#include "MyMathFunctions.h"
JNIEXPORT jint JNICALL Java_SomeNamespace_Add(JNIEnv* _Env, jobject _Object, jint a, jint b)
{
return (jint)add((int)a, (int)b);
}
// MyMathFunctions.h
int add(int a, int b)
{
return a + b;
}
// MyCppProgram.cpp
#include "MyMathFunctions.h"
int main()
{
int c = add(a, b)
}
For number 2, you can convert between jint and int using standard C style casts:
int a = 10;
jint b = (jint) a;
int c = (int) b
As for number 3:
If you are calling the function from within JNI, you would use the pair of functions described here:
JNIEXPORT void JNICALLJava_MyJavaClass_printName(JNIEnv *env, jobject obj, jstring name)
{
const char *str= (*env)->GetStringUTFChars(env,name,0);
printf(ā€œ%sā€, str);
//need to release this string when done with it in order to
//avoid memory leak
(*env)->ReleaseStringUTFChars(env, name, str);
}
To convert from a C string like above to std::string you can do the following:
std::string cpp_string = str;
If you are not calling the function using JNI, it looks like you will need to try and roll your own conversion. I would see if you can use an existing VM implementation, figure out how they do it and mimic that.
For example you could try the Android VM with these as a starting point:
https://android.googlesource.com/platform/dalvik/+/donut-release/vm/Jni.c#2230
https://android.googlesource.com/platform/dalvik.git/+/android-4.3_r3/vm/UtfString.cpp#284

Generally speaking, there is one cause that can prevent you to load a JNI DLL without Java. The DLL may need some JVM external symbols. But you can fake them with dummies, if you need.
If the DLL is loaded from Java for you, it's even easier: you can simply call any exported function of the DLL without fear.
If an exported JNI method call does not use the JNIEnv parameter (as in your example of Add()), you can simply pass nullptr to satisfy the calling convention.
This won't let you pass parameters like jstring or jarray, though. The good practice, in any case, is to separate the layers of JNI and the actual logic, which takes parameters converted from Java to native.
But if you have no control of the DLL, you can try to fake the JNI altogether. See jni.h and implement the necessary functions the way that suits you.

Related

How can I go about passing a Java Long Object to C in JNI

This does it for Java Strings :
public native Entity[] getALLEntityTYPES(String entityType);
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_my_jni_project_getALLEntityTYPE(JNIEnv *env, jobject instance, jstring entityType_) {
const char *entityType = env->GetStringUTFChars(entityType_, 0);
How can I go about passing a Java Long Object to C in JNI:
public native String getEntityTypeByEntityGUID(Long entityGUID);
Thank you all in advance.
If you can pass long instead of Long, take a look here for full sample of passing primitive types
http://jnicookbook.owsiak.org/recipe-No-011/
If you want to pass Long (as Object) take a look here
http://jnicookbook.owsiak.org/recipe-No-020/
where sample code for getting values from Object is shown. Of course, in your case, you need to access fields of Long class.
Update
For unboxing (Long, Boolean, Integer, etc.) samples take a look here:
https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo055
If you simply need to pass the long value to jni you may use int64_t. don't forget to include stdint.h to make this type avalable. The java native signature would need to use long (not Long object)
The simplest solution might be to use a long value rather than a Longobject:
public native String getEntityTypeByEntityGUID(long entityGUID);
and declare your native function using the jlong type (cf the docs):
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_my_jni_project_getEntityTypeByEntityGUID(jlong entityGUID) {
// (...)

JNI - how to use multiple Jni wrapper instances with different fields?

background
I have an android project that uses JNI (using NDK) to code in both Java and C/C++.
I've created a Jni java wrapper on the java side that will do all the Jni oprerations by itself, while no other java class can access the jni operations directly other than this wrapper.
the problem
problem is , i wish to create multiple instances of this wrapper, while the Jni part should have an instance per Jni wrapper.
this is a problem, since the Jni part holds the same fields for all instances.
the question
How can i solve this problem, so that for each java instance of the jni wrapper, there will be an instance on the jni part?
I was thinking, maybe I could put all of the fields into a C++ class, and have an init() function that will return a new instance of it for the CTOR of the JniWrapper, and from then, for each JNI function that needs fields, it will get this class as a parameter . maybe it could be a pointer as shown on this link.
sadly, i have no idea how to do it.
can anyone please help?
sample
here's a sample code that I hope will make things clearer for those who didn't understand the problem:
java part:
public class JniWrapper
{
static
{
System.loadLibrary("JniTest");
}
private native void foo(Bitmap bitmap);
}
jni part:
...
// sadly, all of those fields are actually a global fields
int _intField;
float _floatField;
//those are just sample fields. i would also like to store pointers and objects...
JNIEXPORT void JNICALL ...foo(JNIEnv * env, jobject obj, jobject bitmap)
{
// do something with the fields, as if they all belong to the JniWrapper,
// and no other instances of JniWrapper are allowed to change them
}
You need to have C++ classes on the JNI side, and you need to associate an instance of the C++ class with each instance of your JNI wrapper class. You will need to add native methods to new and delete the C++ class instances, and you will need a bullet-proof way of ensuring that the delete-calling method gets called every time an instance of your JNI wrapper class is released, e.g. via a close() method, finally{} blocks, or even a finalize() method: this is one case where its usage is legitimate. You need to store a pointer to the C++ instance in each Java instance, e.g. as a Java long, and you need to get hold of that on the C++ side and cast it to a C++ class instance to get hold of the per-instance data.
I've found a possible solution (link here), to either use a jlong or jobject to be a handle (or pointer, if you wish) to the object that was created on the JNI side.
people said it's better to use a jobject as ByteBuffer instead of jlong for better compatibility.
the solution is :
Java side:
private native ByteBuffer init();
private native void foo(ByteBuffer handle);
JNI side:
/**a class to hold the fields*/
class FieldsHolder
{
... //private fields, for each instance
}
creating the JNI object and sending to java side:
JNIEXPORT jobject JNICALL ...init(JNIEnv * env, jobject obj)
{
FieldsHolder* myClass= new FieldsHolder();
... //prepare fields of the class
return env->NewDirectByteBuffer(myClass, 0);
}
re-using the JNI object :
JNIEXPORT void JNICALL ...foo(JNIEnv * env, jobject obj, jobject handle)
{
FieldsHolder* myClass= (FieldsHolder*) env->GetDirectBufferAddress(handle);
//now we can access the fields again.
}

Passing array/struct from native code (C) to Java [duplicate]

I've got some C functions which I am calling through JNI which take a pointer to a structure, and some other functions which will allocate/free a pointer to the same type of structure so that it is a bit easier to deal with my wrapper. Surprisingly, the JNI documentation says very little about how to deal with C structures.
My C header file looks like so:
typedef struct _MyStruct {
float member;
} MyStruct;
MyStruct* createNewMyStruct();
void processData(int *data, int numObjects, MyStruct *arguments);
The corresponding JNI C wrapper file contains:
JNIEXPORT jobject JNICALL
Java_com_myorg_MyJavaClass_createNewMyStruct(JNIEnv *env, jobject this) {
return createNewMyStruct();
}
JNIEXPORT void JNICALL
Java_com_myorg_MyJavaClass_processData(JNIEnv *env, jobject this, jintArray data,
jint numObjects, jobject arguments) {
int *actualData = (*env)->GetIntArrayElements(env, data, NULL);
processData(actualData, numObjects, arguments);
(*env)->ReleaseIntArrayElements(env, data, actualData, NULL);
}
...and finally, the corresponding Java class:
public class MyJavaClass {
static { System.loadLibrary("MyJniLibrary"); }
private native MyStruct createNewMyStruct();
private native void processData(int[] data, int numObjects, MyStruct arguments);
private class MyStruct {
float member;
}
public void test() {
MyStruct foo = createNewMyStruct();
foo.member = 3.14159f;
int[] testData = new int[10];
processData(testData, 10, foo);
}
}
Unfortunately, this code crashes the JVM right after hitting createNewMyStruct(). I'm a bit new to JNI and have no idea what the problem could be.
Edit: I should note that the C code is very vanilla C, is well-tested and was ported from a working iPhone project. Also, this project is using the Android NDK framework, which lets you run native C code from an Android project from within JNI. However, I don't think that this is strictly an NDK issue... it seems like a JNI setup/initialization error on my part.
You need to create a Java class with the same members as C struct, and 'map' them in the C code via methods env->GetIntField, env->SetIntField, env->GetFloatField, env->SetFloatField, and so on - in short, lots of manual labor, hopefully there already exist programs that do it automatically: JNAerator (http://code.google.com/p/jnaerator) and SWIG (http://www.swig.org/). Both have their pros and cons, the choice is up to you.
It's crashing because Java_com_myorg_MyJavaClass_createNewMyStruct is declared to return jobject, but is actually returning struct MyStruct. If you ran this with CheckJNI enabled, the VM would complain loudly and abort. Your processData() function is also going to be fairly upset about what it gets handed in arguments.
A jobject is an object on the managed heap. It can have extra stuff before or after the declared fields, and the fields don't have to be laid out in memory in any particular order. So you can't map a C struct on top of a Java class.
The most straightforward way to deal with this was identified in an earlier answer: manipulate the jobject with JNI functions. Allocate the objects from Java or with NewObject, Get/Set the object fields with appropriate calls.
There are various ways to "cheat" here. For example, you could include a byte[] in your Java object that holds sizeof(struct MyStruct) bytes and then use GetByteArrayElements to get a pointer to it. A bit ugly, especially if you want to access the fields from the Java side as well.
C structure is the collection of variables (some are function pointer). Pass to java is not a good idea. In general, it is the problem how to pass more complex type to java, like pointer.
In JNI book, to keep the pointer/structure in native and export manipulation to java is recommended. You can read some useful articles. The JavaTM Native Interface Programmer's Guide and Specification, I have read. 9.5 Peer Classes have a solution to deal with it.
Make the class on both the Java and C++ sides, just putting in the member variables. C++ structs are really just classes with public data members. If you are really in pure C, stop reading now.
Use your IDE(s) to make setters and getters for the member variables automatically.
Use javah to generate the C header file from the Java class.
Do some editing on the C++ side to make the setters and getters match the generated header file.
Put in the JNI code.
This is not an ideal solution, but it may save you a little time, and it will at least give you a skeleton that you can edit. This functionality could be added to an IDE, but without a big demand, it probably won't happen. Most IDEs don't even support mixed language projects, let alone having them talk to each other.

Calling C++ dll from Java

I'm using Java for a small app. It's a rewrite of an existing MFC project. There is an existing dll that I need to change to enable access from Java using JNI. All of this Java stuff is new to me, so I'm having a little trouble and feeling rather dense when I read other forum posts. In the existing dll I have a function like this:
extern "C" __declspec(dllexport) bool Create()
{
return TRUE;
}
Dumb question time. How do I properly set it up to be called by Java?
I tried this:
JNIEXPORT jboolean JNICALL Create()
{
return TRUE;
}
I'm including jni.h and everything compiles fine. However, when I call it from Java I get UnsatisfiedLinkError. I'm calling it from Java using this:
public static native boolean CreateSession();
System.load("D:\\JavaCallTest.dll");
Create();
Could someone kindly push me in the proper direction? I sincerely appreciate any help.
Thanks,
Nick
You need to include the Java class name and path in your native code, for example if your native method was declared in Java as:
public class NativeCode {
public static native boolean CreateSession();
}
and the class path was (for example) com.example.NativeCode you would declare your method in native as follows:
extern "C"
JNIEXPORT jboolean JNICALL Java_com_example_NativeCode_CreateSession(JniEnv* env, jclass clazz)
{
return JNI_TRUE;
}
All JNI methods have a JNIEnv pointer and class as their first two parameters.
A static native method still needs at least two parameters:
JNIEnv *env
jclass clazz
The function name also has to correspond the the java package structure.
JNIEXPORT jboolean JNICALL Java_com_example_CreateSession(JNIEnv *env, jclass clazz)
Ideally, you would use the javah tool to create a header file from the java class declaring the native method and then implement the declared function prototypes.
I had a similar problem - an existing C-Codebase which i needed to access from Java. It paid off for me, to get familiar with SWIG, a tool to generate an intermediate C++ DLL (which calls the C-Code), plus Java-Code that is calling into the C++ DLL.
If you have more than just 1 function of the DLL to wrap, it might pay off to check out this tool, otherwise you'd have to get familiar with JNI...
EDIT:
It seems like your DLL is not found by the System.load() call. You might want to try System.loadLibrary(), but note that your DLL must then be located in the path denoted by the Java system property java.library.path.
Also dont pass the full filename in this case, but just the filename without extension.

How to pass C structs back and forth to Java code in JNI?

I've got some C functions which I am calling through JNI which take a pointer to a structure, and some other functions which will allocate/free a pointer to the same type of structure so that it is a bit easier to deal with my wrapper. Surprisingly, the JNI documentation says very little about how to deal with C structures.
My C header file looks like so:
typedef struct _MyStruct {
float member;
} MyStruct;
MyStruct* createNewMyStruct();
void processData(int *data, int numObjects, MyStruct *arguments);
The corresponding JNI C wrapper file contains:
JNIEXPORT jobject JNICALL
Java_com_myorg_MyJavaClass_createNewMyStruct(JNIEnv *env, jobject this) {
return createNewMyStruct();
}
JNIEXPORT void JNICALL
Java_com_myorg_MyJavaClass_processData(JNIEnv *env, jobject this, jintArray data,
jint numObjects, jobject arguments) {
int *actualData = (*env)->GetIntArrayElements(env, data, NULL);
processData(actualData, numObjects, arguments);
(*env)->ReleaseIntArrayElements(env, data, actualData, NULL);
}
...and finally, the corresponding Java class:
public class MyJavaClass {
static { System.loadLibrary("MyJniLibrary"); }
private native MyStruct createNewMyStruct();
private native void processData(int[] data, int numObjects, MyStruct arguments);
private class MyStruct {
float member;
}
public void test() {
MyStruct foo = createNewMyStruct();
foo.member = 3.14159f;
int[] testData = new int[10];
processData(testData, 10, foo);
}
}
Unfortunately, this code crashes the JVM right after hitting createNewMyStruct(). I'm a bit new to JNI and have no idea what the problem could be.
Edit: I should note that the C code is very vanilla C, is well-tested and was ported from a working iPhone project. Also, this project is using the Android NDK framework, which lets you run native C code from an Android project from within JNI. However, I don't think that this is strictly an NDK issue... it seems like a JNI setup/initialization error on my part.
You need to create a Java class with the same members as C struct, and 'map' them in the C code via methods env->GetIntField, env->SetIntField, env->GetFloatField, env->SetFloatField, and so on - in short, lots of manual labor, hopefully there already exist programs that do it automatically: JNAerator (http://code.google.com/p/jnaerator) and SWIG (http://www.swig.org/). Both have their pros and cons, the choice is up to you.
It's crashing because Java_com_myorg_MyJavaClass_createNewMyStruct is declared to return jobject, but is actually returning struct MyStruct. If you ran this with CheckJNI enabled, the VM would complain loudly and abort. Your processData() function is also going to be fairly upset about what it gets handed in arguments.
A jobject is an object on the managed heap. It can have extra stuff before or after the declared fields, and the fields don't have to be laid out in memory in any particular order. So you can't map a C struct on top of a Java class.
The most straightforward way to deal with this was identified in an earlier answer: manipulate the jobject with JNI functions. Allocate the objects from Java or with NewObject, Get/Set the object fields with appropriate calls.
There are various ways to "cheat" here. For example, you could include a byte[] in your Java object that holds sizeof(struct MyStruct) bytes and then use GetByteArrayElements to get a pointer to it. A bit ugly, especially if you want to access the fields from the Java side as well.
C structure is the collection of variables (some are function pointer). Pass to java is not a good idea. In general, it is the problem how to pass more complex type to java, like pointer.
In JNI book, to keep the pointer/structure in native and export manipulation to java is recommended. You can read some useful articles. The JavaTM Native Interface Programmer's Guide and Specification, I have read. 9.5 Peer Classes have a solution to deal with it.
Make the class on both the Java and C++ sides, just putting in the member variables. C++ structs are really just classes with public data members. If you are really in pure C, stop reading now.
Use your IDE(s) to make setters and getters for the member variables automatically.
Use javah to generate the C header file from the Java class.
Do some editing on the C++ side to make the setters and getters match the generated header file.
Put in the JNI code.
This is not an ideal solution, but it may save you a little time, and it will at least give you a skeleton that you can edit. This functionality could be added to an IDE, but without a big demand, it probably won't happen. Most IDEs don't even support mixed language projects, let alone having them talk to each other.

Categories