ndk-build 'JNI_CreateJavaVM' was not declared in this scope - java

I have error while trying invoke java method from native code.
[arm64-v8a] Compile++ : hell <= hell.cpp
/home/zns/AndroidStudioProjects/Test/app/src/main/jni/hell.cpp: In function 'int main()':
/home/zns/AndroidStudioProjects/Test/app/src/main/jni/hell.cpp:8:42: error: 'JNI_CreateJavaVM' was not declared in this scope
JNI_CreateJavaVM(&jvm, &env, &vm_args);
^
make: *** [/home/zns/AndroidStudioProjects/Test/app/src/main/obj/local/arm64-v8a/objs/hell/hell.o] Error 1
hell.cpp
#include <string.h>
#include <jni.h>
int main(){
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
vm_args.version = JNI_VERSION_1_6;
JNI_CreateJavaVM(&jvm, &env, &vm_args);
jclass cls = env->FindClass("MainActivity");
jmethodID mid = env->GetStaticMethodID(cls, "test", "()V");
env->CallStaticVoidMethod(cls, mid, 100);
jvm->DestroyJavaVM();
}
extern "C" {
jstring
Java_com_oxide_app_MainActivity_stringFromJNI
(JNIEnv *env, jobject obj)
{
main();
return env->NewStringUTF("Hello from C++ over JNI!");
}
}
MainActivity.java
public class MainActivity extends ActionBarActivity {
static{
System.loadLibrary("hell");
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = new TextView(this);
tv.setText(stringFromJNI());
setContentView(tv);
}
public native String stringFromJNI();
public void test(){
Log.d("NATIVE", "WHOA");
}
}
OS: linux;
jdk:/opt/icedtea-bin-6.1.12.7/;
P.S.
I have seen two similar questions, but they did not help to solve the problem
Calling a JAVA method from C++ with JNI, no parameters
Using JNI to execute a java jar from a C++ program, using g++ or eclipse

From the NDK's jni.h
#if 0 /* In practice, these are not exported by the NDK so don't declare them */
jint JNI_GetDefaultJavaVMInitArgs(void*);
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
#endif
As the only supported way to use the NDK is from a Java application so the Java JM is already loaded.
I think you should remove your main function and look into replacing it with JNI_OnLoad and remove the calls to control the VM's lifetime.

Related

Full Native Implementation

I am designing an application through JNI in order to prevent third party edits. I have moved forth towards registering native methods in order to remove jni bridge linkages, but as you can see, methods with override attribute still need to exist within the java code as native linked. Is there a way to fully port the remaining java code for this specific file?
Java:
#Override
protected native void onCreate(Bundle savedInstanceState);
CPP:
void onCreate(JNIEnv *env, jobject classObject, jobject bundle) {
/**super.onCreate**/
gObjects[0] = env->NewGlobalRef(classObject);
gClasses[0] = env->GetObjectClass(gObjects[0]);
jclass s_Class = env->GetSuperclass(gClasses[0]);
jmethodID oc_ID = env->GetMethodID(s_Class, "onCreate", "(Landroid/os/Bundle;)V");
env->CallNonvirtualVoidMethod(gObjects[0], gClasses[0], oc_ID, bundle);
}
extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *curVM_, void *reserved) {
curVM = curVM_;
curVM->GetEnv((void **) &environments[0], JNI_VERSION_1_6);
/**Start of Native Method Registers**/
JNINativeMethod natives[1];
uint64_t pCount = sizeof(natives) / sizeof(natives[0]);
jclass klass = environments[0]->FindClass("com/company/app/Activity");
natives[0] = {"onCreate", "(Landroid/os/Bundle;)V", (void *)onCreate};
environments[0]->RegisterNatives(klass , natives, pCount);\
for (uint64_t i = 0; i < pCount; i++) natives[i] = {"", ""};\
return JNI_VERSION_1_6;
}
It does seem quite silly to think I can simply remove the native linkage between the activity and lib. Instead, I will be utilizing BaseDexClassLoader to sideload my dex file during runtime. I will not be giving up my code, but further information about this can be founded at: https://developer.android.com/reference/dalvik/system/BaseDexClassLoader

Jni UnsatisfiedLinkError. native library has been added and there is no error in the cpp file [duplicate]

This is my first attempt at JNI. My ultimate goal is to get all tasks currently running on a machine, but need to get even a simple example running. I keep getting this error when I try to execute my main program. I have supplied my simple Java main program, the header file generated, and the error.
I do not know what this DLL could be dependent on. It was initially referencing a DLL I tracked down and placed into system32 (msvcr90.dll).
Here is the command I used to compile the C code as well which produced the DLL, OBJ, LIB, EXP and manifest files.
cl -I"C:\Program Files\Java\jdk1.6.0\include" -I"C:\Program Files\Java\jdk1.6.0\include\win32" -MD -LD HelloWorld.c -FeHelloWorld.dll
class HelloWorld {
private native void print();
public static void main(String[] args) {
new HelloWorld().print();
}
static {
System.load("C:\\temp\\HelloWorld.dll");
}
}
#include <jni.h>
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
printf("Hello World!\n");
return;
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
java.lang.UnsatisfiedLinkError: C:\temp\HelloWorld.dll: A dynamic link library (DLL) initialization routine failed
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(Unknown Source)
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.load0(Unknown Source)
at java.lang.System.load(Unknown Source)
at HelloWorld.<clinit>(HelloWorld.java:7)
Exception in thread "main"
The Unsatisfied Link Error can mean many things went wrong. I would use
System.loadLibrary("HelloWorld");
Instead of
System.load();
As TwentyMiles suggested.
Also, when invoking your program you need to (assuming your DLL is on the same directory as your class files:
java -Djava.library.path=. HelloWorld
Here's a simple demo I made that calls a Win32 API function (MessageBox)
Java class
class CallApi{
private native String showMessageBox(String msg);
private native double getRandomDouble();
static{
try{
System.loadLibrary("CallApi");
System.out.println("Loaded CallApi");
}catch(UnsatisfiedLinkError e){
//nothing to do
System.out.println("Couldn't load CallApi");
System.out.println(e.getMessage());
}
}
public static void main(String args[]){
CallApi api = new CallApi();
double randomNumber = api.getRandomDouble();
String retval = api.showMessageBox("Hello from Java!\n"+
"The native random number: "+randomNumber);
System.out.println("The native string: "+retval);
}
}
Generated header file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class CallApi */
#ifndef _Included_CallApi
#define _Included_CallApi
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: CallApi
* Method: showMessageBox
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_CallApi_showMessageBox
(JNIEnv *, jobject, jstring);
/*
* Class: CallApi
* Method: getRandomDouble
* Signature: ()D
*/
JNIEXPORT jdouble JNICALL Java_CallApi_getRandomDouble
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
The C DLL code
#include "CallApi.h"
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#pragma comment(lib,"user32.lib")
JNIEXPORT jstring JNICALL Java_CallApi_showMessageBox
(JNIEnv *env, jobject thisObject, jstring js)
{
//first convert jstring to const char for use in MessageBox
const jbyte* argvv = (*env)->GetStringUTFChars(env, js, NULL);
char* argv =(char *) argvv;
//Call MessageBoxA
MessageBox(NULL, argv, "Called from Java!", MB_ICONEXCLAMATION | MB_OK);
return js;
}
JNIEXPORT jdouble JNICALL Java_CallApi_getRandomDouble
(JNIEnv *env, jobject thisObject)
{
double num1;
srand((unsigned)(time(0)));
num1 = ((double)rand()/(double)RAND_MAX);
return num1;
}
Compile instructions
I compile with the Visual C++ express 2008 cl, removing the -ML flag since it causes an exception when the Java code tries to call the native code:
cl /I"c:\Program Files\Java\jdk1.6.0_10\include" /I"c:\Program Files\Java\jdk1.6.0_10\include\win32" -LD CallApi.c -FeCallApi.dll
Then, to run the code:
java -Djava.library.path=. CallApi
I'm not claiming to understand the situation enough to explain it, however some users reported the error when using the "-MD" compiler flag.
For more information see Java Native Interface (JNI) - Impossible to use VS2005 with Java? which discusses this problem and offers possible work-arounds and think techie blog for alternatives.
I believe that you should be using
System.loadLibrary("HelloWorld");
instead of System.load. LoadLibrary will check your system path (not the Java library path) so make sure that HelloWorld.dll is in a directory where it can found. Also note that it does not require the full path, and you don't need to add the dll extension to the end.
I just removed -MD option and compiled it worked like charm
cl -I"C:\Program Files\Java\jdk1.6.0_21\include" -I"C:\Program Files\Java\jdk1.6.0_21\include\win32" -LD HelloWorld.c -FeHelloWorld.dll
If you change the location (package) of your native function declaration from the java side without updating the h file, and the signature of the method in the c++ side, It won't resolve to the method and will throw unsatisfied..
package x;
public class A {
private native void print();
...
}
moved to:
package x.y;
public class A {
private native void print();
...
}
This will require regeneration of the H file (to something like Java_x_y_A_print ).
Note you can change those signatures manually but I won't recommend

JNI: Callback from JVM to C++ fails to run

I start JVM from C++ program.
C++ code:
JNIEXPORT jobject JNICALL com_javelin_JavelinMarketData_callBackIntoNative(JNIEnv* env, jobject obj, jlong ptr)
{
std::cout << "com_javelin_JavelinMarketData_callBackIntoNative called" << std::endl;
}
int main()
{
JavaVM* jvm;
JNIEnv* env;
...
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
jmethodID mainMethod = env->GetStaticMethodID(helloWorldClass, "main", "([Ljava/lang/String;)V");
env->CallStaticVoidMethod(helloWorldClass, mainMethod, ...);
}
then I need my C++ function called back from java:
Java code:
native void callBackIntoNative(long ptr);
public void onResponse(long param)
{
System.out.println("Calling callBackIntoNative()");
callBackIntoNative(param);
}
When I run my C++ program, JVM starts correctly, but after it prints "Calling callBackIntoNative()" the following acception appears:
Exception during callBackIntoNative(): 'com.javelin.JavelinMarketData.callBackIntoNative(J)V'
java.lang.UnsatisfiedLinkError: com.javelin.JavelinMarketData.callBackIntoNative(J)V
Can you please help?
Thanks, but I found a solution: I should have registered my C++ function as a native method using the code:
const JNINativeMethod methods[] = { { "callBackIntoNative", "(J)V", (void*)&com_javelin_JavelinMarketData_callBackIntoNative } };
const int methods_size = sizeof(methods) / sizeof(methods[0]);
jclass jClass = env->FindClass("com/javelin/JavelinMarketData");
env->RegisterNatives(jClass, methods, methods_size);
Now it works fine.
I would compile your callback apart into a dynamic library (.dll, .so, whatever your OS) and put it accesible to your java program. Then you just load your library by using JNI and call from your java classes whatever functionality you have in the library.

JNI does not work with __stdcall

I'm playing around with JNI on Windows 7x64, Java version is 1.7.0_40 and MinGW / GCC / G++ 4.7.2.
Trying to power off my monitor from Java. So, I've created a class:
public class MonitorTrigger {
static {
try {
System.load(new ClassPathResource("MonitorTrigger.dll").getFile().getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
public native void on();
public native void off();
}
Then, generated h file from it (with Eclipse, but I beleive it uses javah):
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger */
#ifndef _Included_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
#define _Included_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
* Method: on
* Signature: ()V
*/
JNIEXPORT void
JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_on(
JNIEnv *, jobject);
/*
* Class: by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
* Method: off
* Signature: ()V
*/
JNIEXPORT void
JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_off(
JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
And implemented it:
#include "by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger.h"
#include <iostream>
#include <windows.h>
JNIEXPORT void JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_on
(JNIEnv * env, jobject object) {
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) -1);
}
JNIEXPORT void JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_off
(JNIEnv * env, jobject object) {
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) 2);
}
It does not work! I get exception:
Exception in thread "main" java.lang.UnsatisfiedLinkError: by.dev.madhead.bubikopf.desktop.monitortrigger.util.MonitorTrigger.off()V
at by.dev.madhead.bubikopf.desktop.monitortrigger.util.MonitorTrigger.off(Native Method)
at by.dev.madhead.bubikopf.desktop.monitortrigger.App.main(App.java:7)
I was stuck for a bit, but after googling, I've found a solution - use __cdecl instead of __stdcall convention for exported function. So, I've made a very dirty hack:
#define JNICALL __cdecl
And it works!
It was previously defined as __stdcall at jni_md.h. I realize that I'm doing a very bad thing, which harm kittens, but I lack of experience in C/ C++, and I cannot figure out, why I should redefine this? Why standard header (jni_md.h) defines this incorrectly?
For 32 bit DLLs being used by jni, the code has to be compiled without the usual __stdcall adornments - i.e. the code can't have the #N postfix on the symbol name; but it still needs to be compiled in __stdcall mode.
When compiling the dll under mingw, you need to add the option --kill-at to the linker. This is generally passed using -Wl,--kill-at. This will cause the #N postfix to be removed, and so appear as a simple symbol which can be linked at run-time by the JVM. The option can be abbreviated to -Wl,-k as well.
An alternative is to use a map file, which exports the symbols in their unmangled form, which is the mechanism that's used most often when compiling with microsoft's own visual studio compiler.
This is pretty poorly documented, and the resulting error you get when it happens doesn't help too well.
I'd recommend looking at JNA, which makes writing native code wrappers a lot simpler, and in this case would mean no C++ code needed.
The JNA code to accomplish this looks like:
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.WPARAM;
public class JNAMonitorTrigger {
public void monitor(LPARAM param) {
User32.INSTANCE.PostMessage(WinUser.HWND_BROADCAST,
WinUser.WM_SYSCOMMAND,
new WPARAM(0xf170), param);
}
public void on() {
monitor(new LPARAM(-1));
}
public void off() {
monitor(new LPARAM(2));
}
public static void main(String args[]) throws Exception {
JNAMonitorTrigger me = new JNAMonitorTrigger();
me.off();
Thread.sleep(1000);
me.on();
}
};
Even though you're on Windows, you're using GCC, which means this: Is there STDCALL in Linux? is still basically going to apply.
The functions in your file generated by java.h are declared as extern "C"
You have to add it in your cpp file :
extern "C"
JNIEXPORT void JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_on
(JNIEnv * env, jobject object) {
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) -1);
}

JNI Hello World Unsatisfied Link Error

This is my first attempt at JNI. My ultimate goal is to get all tasks currently running on a machine, but need to get even a simple example running. I keep getting this error when I try to execute my main program. I have supplied my simple Java main program, the header file generated, and the error.
I do not know what this DLL could be dependent on. It was initially referencing a DLL I tracked down and placed into system32 (msvcr90.dll).
Here is the command I used to compile the C code as well which produced the DLL, OBJ, LIB, EXP and manifest files.
cl -I"C:\Program Files\Java\jdk1.6.0\include" -I"C:\Program Files\Java\jdk1.6.0\include\win32" -MD -LD HelloWorld.c -FeHelloWorld.dll
class HelloWorld {
private native void print();
public static void main(String[] args) {
new HelloWorld().print();
}
static {
System.load("C:\\temp\\HelloWorld.dll");
}
}
#include <jni.h>
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
printf("Hello World!\n");
return;
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
java.lang.UnsatisfiedLinkError: C:\temp\HelloWorld.dll: A dynamic link library (DLL) initialization routine failed
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(Unknown Source)
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.load0(Unknown Source)
at java.lang.System.load(Unknown Source)
at HelloWorld.<clinit>(HelloWorld.java:7)
Exception in thread "main"
The Unsatisfied Link Error can mean many things went wrong. I would use
System.loadLibrary("HelloWorld");
Instead of
System.load();
As TwentyMiles suggested.
Also, when invoking your program you need to (assuming your DLL is on the same directory as your class files:
java -Djava.library.path=. HelloWorld
Here's a simple demo I made that calls a Win32 API function (MessageBox)
Java class
class CallApi{
private native String showMessageBox(String msg);
private native double getRandomDouble();
static{
try{
System.loadLibrary("CallApi");
System.out.println("Loaded CallApi");
}catch(UnsatisfiedLinkError e){
//nothing to do
System.out.println("Couldn't load CallApi");
System.out.println(e.getMessage());
}
}
public static void main(String args[]){
CallApi api = new CallApi();
double randomNumber = api.getRandomDouble();
String retval = api.showMessageBox("Hello from Java!\n"+
"The native random number: "+randomNumber);
System.out.println("The native string: "+retval);
}
}
Generated header file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class CallApi */
#ifndef _Included_CallApi
#define _Included_CallApi
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: CallApi
* Method: showMessageBox
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_CallApi_showMessageBox
(JNIEnv *, jobject, jstring);
/*
* Class: CallApi
* Method: getRandomDouble
* Signature: ()D
*/
JNIEXPORT jdouble JNICALL Java_CallApi_getRandomDouble
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
The C DLL code
#include "CallApi.h"
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#pragma comment(lib,"user32.lib")
JNIEXPORT jstring JNICALL Java_CallApi_showMessageBox
(JNIEnv *env, jobject thisObject, jstring js)
{
//first convert jstring to const char for use in MessageBox
const jbyte* argvv = (*env)->GetStringUTFChars(env, js, NULL);
char* argv =(char *) argvv;
//Call MessageBoxA
MessageBox(NULL, argv, "Called from Java!", MB_ICONEXCLAMATION | MB_OK);
return js;
}
JNIEXPORT jdouble JNICALL Java_CallApi_getRandomDouble
(JNIEnv *env, jobject thisObject)
{
double num1;
srand((unsigned)(time(0)));
num1 = ((double)rand()/(double)RAND_MAX);
return num1;
}
Compile instructions
I compile with the Visual C++ express 2008 cl, removing the -ML flag since it causes an exception when the Java code tries to call the native code:
cl /I"c:\Program Files\Java\jdk1.6.0_10\include" /I"c:\Program Files\Java\jdk1.6.0_10\include\win32" -LD CallApi.c -FeCallApi.dll
Then, to run the code:
java -Djava.library.path=. CallApi
I'm not claiming to understand the situation enough to explain it, however some users reported the error when using the "-MD" compiler flag.
For more information see Java Native Interface (JNI) - Impossible to use VS2005 with Java? which discusses this problem and offers possible work-arounds and think techie blog for alternatives.
I believe that you should be using
System.loadLibrary("HelloWorld");
instead of System.load. LoadLibrary will check your system path (not the Java library path) so make sure that HelloWorld.dll is in a directory where it can found. Also note that it does not require the full path, and you don't need to add the dll extension to the end.
I just removed -MD option and compiled it worked like charm
cl -I"C:\Program Files\Java\jdk1.6.0_21\include" -I"C:\Program Files\Java\jdk1.6.0_21\include\win32" -LD HelloWorld.c -FeHelloWorld.dll
If you change the location (package) of your native function declaration from the java side without updating the h file, and the signature of the method in the c++ side, It won't resolve to the method and will throw unsatisfied..
package x;
public class A {
private native void print();
...
}
moved to:
package x.y;
public class A {
private native void print();
...
}
This will require regeneration of the H file (to something like Java_x_y_A_print ).
Note you can change those signatures manually but I won't recommend

Categories