Java Native Interface : Object Passing - java

I was trying to implement a simple object passing code but there was a error by the compiler.
Error
Exception in thread "main" java.lang.NoSuchFieldError: count at
objectpassing.ObjectPassing.changeCount(Native Method)
Here is my Java Code
public class ObjectPassing {
static{
System.load("out.dll");
}
int count=10;
String message="hi";
public static void main(String[] args)
{
ObjectPassing ob=new ObjectPassing();
ObjectPassing.changeCount();
System.out.println("Number in java"+ob.count);
System.out.println(ob.message);
}
private static native void changeCount();
}
My C code is :
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
#include "jnivg.h"
JNIEXPORT void JNICALL Java_objectpassing_ObjectPassing_changeCount
(JNIEnv *env, jclass o)
{
jclass tc=(*env)->GetObjectClass(env,o);
jfieldID fid=(*env)->GetFieldID(env,tc,"count","I");
jint n=(*env)->GetIntField(env,o,fid);
printf("Number in c= %d",n);
n=200;
(*env)->SetIntField(env,o,fid,n);
}

You are trying to get the value of the non-static field from a static method, which is impossible due to common sense, regardless whether your method is native or not.
You should either make your count field static and use GetStaticFieldID and GetStaticIntField functions with it. Or make your changeCount method non-static so it will have a jobject parameter instead of jclass which you then will be able to use with GetIntField function.

Related

Calling NSWindow with JNI

I tried call Swift function from Java using JNI. My Swift function creates NSWindow with some content, and it's work, but when I call it from Java I get the next message:
"NSWindow drag regions should only be invalidated on the Main Thread! This will throw an exception in the future" and my app crashes
My swift function:
#_silgen_name("swiftHelloWorld")
public func swiftHelloWorld() {
let delegate = AppDelegate()
let application = NSApplication.shared
application.setActivationPolicy(.regular)
application.delegate = delegate
application.run()
}
My Java code:
public class SwiftHelloWorld {
static {
System.loadLibrary("SwiftHelloWorld");
}
public static native void printHelloWorldImpl();
public static void main(final String[] args) {
printHelloWorldImpl();
}
}
My C code:
#include <jni.h>
#include <stdio.h>
#include "helloworld_SwiftHelloWorld.h"
#include "helloworld_SwiftHelloWorld_swift.h"
JNIEXPORT void JNICALL Java_helloworld_SwiftHelloWorld_printHelloWorldImpl (JNIEnv *env, jclass clazz) {
swiftHelloWorld();
printf("%s", "Hello World from JNI!");
}
Swift header:
void swiftHelloWorld();

JNI and constructors

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.

How to connect mysql from a file written in C which contains native methods of java?

I need some way to connect MySQL with C which have Java JNI methods in it.
I made a Java file which calls a native method written in C which needs to be connected to MySQL database.
C File
#include <stdio.h>
#include <mysql/mysql.h>
#include "MyClass.h"
JNIEXPORT jstring JNICALL Java_pkg_MyClass_display(JNIEnv *env, jobject obj){
return (*env)->NewStringUTF(env,mysql_get_client_info());
}
Java File
public class MyClass {
public native String display();
public static String Disp(){
MyClass ob = new MyClass();
String str = ob.display();
return str;
}
static {
System.load("Desktop/libdemo.so");
}
public static void main(String []args){
System.out.println(Disp());
}
}
After creating shared object library (.so), an error occurs:
undefined reference to : mysql_get_client_info()
Please suggest some way to rectify this problem.

JNI: Can not get array length

I faced with the next problem: I can not do anything with byte[] (jbyteArray) in C code. All functions that work with array in JNI cause JNI DETECTED ERROR IN APPLICATION: jarray argument has non-array type. What's wrong with my code?
C:
#include <stdio.h>
#include <jni.h>
static jstring convertToHex(JNIEnv* env, jbyteArray array) {
int len = (*env)->GetArrayLength(env, array);// cause an error;
return NULL;
}
static JNINativeMethod methods[] = {
{"convertToHex", "([B)Ljava/lang/String;", (void*) convertToHex },
};
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env = NULL;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
jclass cls = (*env)->FindClass(env, "com/infomir/stalkertv/server/ServerUtil");
(*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0]) );
return JNI_VERSION_1_4;
}
ServerUtil:
public class ServerUtil {
public ServerUtil() {
System.loadLibrary("shadow");
}
public native String convertToHex(byte[] array);
}
Main Activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ServerUtil serverUtil = new ServerUtil();
byte[] a = new byte[]{1,2,3,4,5};
String s = serverUtil.convertToHex(a);
}
}
My environment:
Android Studio 2.0
Experimental Gradle plugin 0.7.0
JAVA 1.8
NDK r11b
Windows 10 x64
Thanks in advance!
The second argument passed to your function isn't a jbyteArray.
Per the JNI documentation, the arguments passed to a native function are:
Native Method Arguments
The JNI interface pointer is the first argument to native methods. The
JNI interface pointer is of type JNIEnv. The second argument differs
depending on whether the native method is static or nonstatic. The
second argument to a nonstatic native method is a reference to the
object. The second argument to a static native method is a reference
to its Java class.
The remaining arguments correspond to regular Java method arguments.
The native method call passes its result back to the calling routine
via the return value.
Your jstring convertToHex(JNIEnv* env, jbyteArray array) is missing the second jclass or jobject argument, so you're treating either a jobject or jclass argument and a jbyteArray.
Your native method signature is incorrect. It should be
static jstring convertToHe(JNIEnv *env, jobject thiz, jbytearray array)

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(...).

Categories

Resources