JNI and constructors - java

I have a compiled library that I need to use in a project. To keep it short, it's a library for interacting with a specific piece of hardware. What I have is .a and .dll library files, for linux and windows respectively, and a bunch of C++ .h headers with all the public functions and classes described there.
The problem is that the project needs to be in Java, so I need to write a JNI wrapper for this library, and honestly, I've never done that. But that's ok, I'm down to learn the thing.
I've read up a bunch of documentation online, and I figured out passing variables, creating java objects from native code, etc.
What I can't figure out, is how to work with native constructors using JNI? I have no idea what the source code of these constructors are, I only have the headers like this:
namespace RFDevice {
class RFDEVICE_API RFEthernetDetector
{
public:
//-----------------------------------------------------------------------------
// FUNCTION RFEthernetDetector::RFEthernetDetector
/// \brief Default constructor of RFEthernetDetector object.
///
/// \return void : N/A
//-----------------------------------------------------------------------------
RFEthernetDetector();
RFEthernetDetector(const WORD wCustomPortNumber);
So basically if I was to write my program in C++ (which I can't), I would do something like
RFEthernetDetector ethernetDetector = new RFEthernerDetector(somePort);
and then work with that object. But... How do I do this in Java using JNI?
I don't understand how am I supposed to create a native method for constructor, that would call the constructor from my .a library, and then have some way of working with that specific object? I know how to create java objects from native code - but the thing is I don't have any information about internal structure of the RFEthernetDetector class - only some of it's public fields and public methods.
And I can't seem to find the right articles on the net to help me out. How do I do that?
Update: A bit further clarification.
I create a .java wrapper class like this:
public class RFEthernetDetector
{
public RFEthernetDetector(int portNumber)
{
Init(portNumber);
}
public native void Init(int portNumber); // Void? Or what?
}
then I compile it with -h parameter to generate JNI .h file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class RFEthernetDetector */
#ifndef _Included_RFEthernetDetector
#define _Included_RFEthernetDetector
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: RFEthernetDetector
* Method: Init
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_RFEthernetDetector_Init
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
I then create an implementation that will call the functions from my .a library:
#include "RFEthernetDetector.h" // auto-generated JNI header
#include "RFEthernetDetector_native.h" // h file that comes with the library,
//contains definition of RFEthernetDetector class
/*
* Class: RFEthernetDetector
* Method: Init
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_RFEthernetDetector_Init(JNIEnv *env, jobject thisObj, jint value)
{
RFEthernetDetector *rfeDetector = new RFEthernetDetector(value); // constructor from the library
// now how do I access this new object from Java?
// if I need to later call rfDetector->doSomething() on that exact class instance?
}

You would need to build a RFEthernetDetector Java class that, through a pointer, owns a RFEthernetDetector on the C++ side. This is no fun, but inter-language glue never is.
// In this design, the C++ object needs to be explicitly destroyed by calling
// close() on the Java side.
// I think that Eclipse, at least, is configured by default to complain
// if an AutoCloseable is never close()d.
public class RFEthernetDetector implements AutoCloseable {
private final long cxxThis; // using the "store pointers as longs" convention
private boolean closed = false;
public RFEthernetDetector(int port) {
cxxThis = cxxConstruct(port);
};
#Override
public void close() {
if(!closed) {
cxxDestroy(cxxThis);
closed = true;
}
}
private static native long cxxConstruct(int port);
private static native void cxxDestroy(long cxxThis);
// Works fine as a safety net, I suppose...
#Override
#Deprecated
protected void finalize() {
close();
}
}
And on the C++ side:
#include "RFEthernetDetector.h"
JNIEXPORT jlong JNICALL Java_RFEthernetDetector_cxxConstruct(JNIEnv *, jclass, jint port) {
return reinterpret_cast<jlong>(new RFEthernetDetector(port));
}
JNIEXPORT void JNICALL Java_RFEthernetDetector_cxxDestroy(JNIEnv *, jclass, jlong thiz) {
delete reinterpret_cast<RFEthernetDetector*>(thiz);
// calling other methods is similar:
// pass the cxxThis to C++, cast it, and do something through it
}
If all that reinterpret_casting makes you feel uncomfortable, you could choose to instead keep a map around:
#include <map>
std::map<jlong, RFEthernetDetector> references;
JNIEXPORT jlong JNICALL Java_RFEthernetDetector_cxxConstruct(JNIEnv *, jclass, jint port) {
jlong next = 0;
auto it = references.begin();
for(; it != references.end() && it->first == next; it++) next++;
references.emplace_hint(it, next, port);
return next;
}
JNIEXPORT void JNICALL Java_RFEthernetDetector_cxxDestroy(JNIEnv *, jclass, jlong thiz) {
references.erase(thiz);
}

You'll need to build the native class in Java, then run the javah program which will build the stubs that Java expects. You'll then need to map the java stubs to the C++ code to compile and distribute that binding library along with your java program.
https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html

So, what I ended up doing for now is basically storing the address in my .java class as "long" variable, and then have Init() method return the address of C++ instance as jlong.
Then, when I need to call something on that instance of a class - I pass the address as an additional argument and do the transformation in the C code like this (tested on test class / custom .so):
//constructor wrapper
JNIEXPORT jlong JNICALL Java_Test_Greetings(JNIEnv *env, jobject thisObj, jint value)
{
Greetings *greetings = new Greetings(value);
return (jlong)greetings;
}
JNIEXPORT void JNICALL Java_Test_HelloWorld(JNIEnv *env, jobject thisObj, jlong reference)
{
Greetings *greetings;
greetings = (Greetings*)reference;
greetings->helloValue();
}
I have no idea if that's the correct way to do it, but it works... would appreciate if someone tells me how wrong I am.

Related

Adding native hooks dynamically

Is there some way to add native hooks dynamically using JNI? What I mean by that is, I would like to override some methods in a class (or in a new class) so that the override calls my native code, without writing any Java code for that.
If you are referring to native methods, maybe registering might be the answer for you.
For example, here, I am registering native method
JNIEXPORT jint JNICALL addOne(JNIEnv *env, jclass obj, jint a) {
return a + 1;
}
...
...
static JNINativeMethod methods[] = {
{"addOne", "(I)I", (void *)&addOne}
};
...
...
(*env)->RegisterNatives(env, cls_Main,
methods, sizeof(methods)/sizeof(methods[0]));
that will be assigned to class
public class Main {
public static native int addOne(int a);
...
...
}
Full sample: https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo052

How to call a method of a subclass / child class of an Java Object passed to JNI `jobject`

I have:
public class MyEntity {
private String _myEntityType;
public String get_myEntityType() {
return _myEntityType;
}
public void set_myEntityType(String _myEntityType) {
this._myEntityType = _myEntityType;
}
}
Then :
public class MyObjectEntity extends MyEntity {
public MyObjectEntity() {
super();
}
private String _myObjectDescription;
public String get_myObjectDescription() {
return _myObjectDescription;
}
public void set_myObjectDescription(String _myObjectDescription) {
this._myObjectDescription = _myObjectDescription;
}
}
Now I start getting into JNI.
public class MyPers {
// Load the 'my-pers-lib' library on application startup.
static {
System.loadLibrary("my-pers-lib");
}
public native Long myPersInit(MyEntity myEntity);
}
Then :
#include <jni.h>
#include <android/log.h>
extern "C"
JNIEXPORT jobject JNICALL
Java_my_ca_my_1gen_1lib_1pers_c_1libs_1core_RevPers_myPersInit(JNIEnv *env, jobject instance,
jobject myEntity) {
jclass myEntityClazz = env->GetObjectClass(myEntity);
/** START GET STRING **/
const char *myEntityType;
// and the get_myObjectDescription() method
jmethodID get_myObjectDescription = env->GetMethodID
(myEntityClazz, "get_myObjectDescription", "()Ljava/lang/String;");
// call the get_myObjectDescription() method
jstring s = (jstring) env->CallObjectMethod
(myEntity, get_myObjectDescription);
if (s != NULL) {
// convert the Java String to use it in C
myEntityType = env->GetStringUTFChars(s, 0);
__android_log_print(ANDROID_LOG_INFO, "MyApp", "get_myObjectDescription : %s\n",
myEntityType);
env->ReleaseStringUTFChars(s, myEntityType);
}
/** END GET STRING **/
return myEntityGUID;
}
I start it all up :
MyObjectEntity myObjectEntity = new MyObjectEntity();
myObjectEntity.set_myObjectDescription("A Good Day To Find Answers To Life's Important Questions.");
MyPers revPers = new MyPers();
Log.v("MyApp", "GUID : " + myPers.myPersInit(myObjectEntity));
The error I get is :
JNI CallObjectMethodV called with pending exception java.lang.NoSuchMethodError: no non-static method "Lmy_app/MyEntity;.get_myObjectDescription()Ljava/lang/String;"
QUESTION
How can I go about calling a method of a subclass / child class of an Java Object passed to JNI jobject, myEntity in this case?
extern "C"
JNIEXPORT jobject JNICALL
Java_myPersInit(JNIEnv *env, jobject instance, jobject myEntity)
Thank you all in advance.
How can I go about calling a method of a subclass / child class of an
Java Object passed to JNI jobject, myEntity in this case?
The question does not make sense. Objects do not have subclasses, classes do. Whatever the class of a given object happens to be defines the methods that you may invoke on that object. You seem to be trying to ask how to invoke methods of a subtype of the declared type of the native method parameter, but the premise of such a question runs counter to the actual fact that you do so exactly the same way you invoke any other instance method from JNI.
Consider: in Java, you would downcast to the subtype and then, supposing the cast succeeded, you would invoke the wanted method normally. In JNI, on the other hand, object references are not differentiated by pointed-to object type (they are all jobjects), so no casting is involved; you just go straight to invoking the wanted method normally.
Of course, a JNI error will occur if the class of the target object does not provide such a method. Whether it's a good idea to do what you describe is an entirely separate question.

Java JNI LoadLibrary protected dll

I created JNI export dll with visual c++ with one simple function. it work correctly when i use it in my java code with LoadLibrary method.
but when i use Unikey Enveloper and protected this dll it not work in java and this error appear
Invalid access to memory location
when i test this protected dll with visual c++ console application it work correctly.
how can I use protected dll in java LoadLibrary Method?
Visual C++ Code:
extern "C" {
JNIEXPORT jint JNICALL Java_Bikaran_Start_JniTestFunction(JNIEnv *env, jclass c, jint a, jint b)
{
return a + b;
}
}
JAVA Code :
package Bikaran;
public class Start {
public static native int JniTestFunction(int s, int s1);
public static void main(String[] args) {
System.loadLibrary("...");
System.out.println(JniTestFunction(1,2));
}
}

How to call a function with arguments in C++ from JAVA using JNI?

I am messing around with this task for a while...
I am trying to call C# DLL methods from java.
I used this as a tutorial, and it suggests building an intermediate c++ dll. But it uses a method without an argument, and I am affraid that it needs a modification for using a method with an argument. This is becuase I get an unsatisfiedlinkerror exception when I call t.SetCounter0("aaa") function in java.
This is the java code:
package jniTester;
import java.io.Console;
public class Test1 {
static {
//System.load("c:\\Users\\ttene\\Documents\\Visual Studio 2012\\Projects\\CPM\\CPMPerformanceCountersController\\x64\\Debug\\CppWrapperDll.dll");
System.load("c:\\Users\\ttene\\Documents\\Cpm2Java\\CppWrapperDll.dll");
}
public native void SetCounter0(String x);
public static void main(String[] args) {
try {
Test1 t = new Test1();
System.out.println("1");
t.SetCounter0("aaa");
System.out.println("2");
} catch (Exception e) {
e.printStackTrace();
}
}
}
This is the cpp:
#include <jni.h>
#include <iostream>
#include "Java\jnicall.h"
#include "MCPP\CppWrapperDll.h"
JNIEXPORT void JNICALL Java_Test1_SetCounter0 (JNIEnv *jn, jobject jobj) {
std::cout << "Java_Test1_SetCounter0";
// Instantiate the MC++ class.
CppWrapperDllC* t = new CppWrapperDllC();
// The actual call is made.
t->callCountersControl();
}
This is the h file:
#using <mscorlib.dll>
#using "CountersControl.netmodule"
using namespace System;
public __gc class CppWrapperDllC
{
public:
// Provide .NET interop and garbage collecting to the pointer.
CountersControl __gc *t;
CppWrapperDllC() {
t = new CountersControl();
// Assign the reference a new instance of the object
}
// This inline function is called from the C++ Code
void callCountersControl() {
t->SetCounter0("aaa");
}
};
And at last this is the jni h file:
#include <jni.h>
/* Header for class Test1 */
#ifndef _Included_Test1
#define _Included_Test1
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Test1
* Method: SetCounter0
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_Test1_SetCounter0(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
I'll appreciate you assistance. Thanks.
you should use javah to create JNI-headers. If you had used it, the declaration in the header would actually have looked like this:
JNIEXPORT void JNICALL Java_Test1_SetCounter0(JNIEnv *, jobject, jstring);
where jstring is the string passed as argument to SetCounter0(...).

Store a c++ object instance inside JNI jobject and retrieve later

I have a JNI class with methods init() work(), and cleanup(). On the C++ side I create an instance of a C++ class Foo during init(), then call some methods on it during work(), and finally delete it inside cleanup(). Right now I store instance of Foo as a global singleton on the C++ so that I can retrieve it from the different JNI calls. What I would really like to do is store a pointer to the Foo instance inside the jobject instance that gets passed to each JNI call, so that I can avoid having a global singleton and also so that I can support multiple instances of Foo. Is something like this possible?
You can store a pointer to your C++ object as a Java class member. For example, in Java:
class Foo
{
public long ptr = 0;
public native void init();
public native void work();
public native void cleanup();
}
And in C++:
jfieldID getPtrFieldId(JNIEnv * env, jobject obj)
{
static jfieldID ptrFieldId = 0;
if (!ptrFieldId)
{
jclass c = env->GetObjectClass(obj);
ptrFieldId = env->GetFieldID(c, "ptr", "J");
env->DeleteLocalRef(c);
}
return ptrFieldId;
}
class Foo
{
/* ... */
};
extern "C"
{
void Java_Foo_init(JNIEnv * env, jobject obj)
{
env->SetLongField(obj, getPtrFieldId(env, obj), (jlong) new Foo);
}
void Java_Foo_work(JNIEnv * env, jobject obj)
{
Foo * foo = (Foo *) env->GetLongField(obj, getPtrFieldId(env, obj));
foo->work();
}
void Java_Foo_cleanup(JNIEnv * env, jobject obj)
{
Foo * foo = (Foo *) env->GetLongField(obj, getPtrFieldId(env, obj));
delete foo;
}
}
Absolutely.
Create a Foo instance in JNI. Simply return the pointer (points to the instance created) as a jlong type. So you can use it as a handler later. Here is an example:
JNIEXPORT jlong JNICALL Java_com_example_init(JNIEnv *env, jobject thiz) {
Foo* pFoo = new Foo();
if (NULL == pFoo) {
// error handling
}
pFoo->initialize();
return reinterpret_cast<jlong>(pFoo);
}
JNIEXPORT void JNICALL Java_example_start(JNIEnv *env, jobject thiz,
jlong fooHandle) {
Foo* pFoo = reinterpret_cast<Foo*>(fooHandle);
pFoo->start();
}
You can do it with a long in java, however I would argue that its not a very good idea to put a pointer to some native memory address in an instance variable of a language that is expected to be operating in a sandbox. Its sloppy and it could be a exploit vector depending on what your doing.
I am guessing you are running into this problem because your native code is very close to your JNI code. If you structure your JNI layer as translation between your native code and Java, you may find it easier to work with.

Categories

Resources