I'm using JNI successfully to call some C code, however when I want to change to C++ JNI throws an UnsatisfiedLinkError whenever I try to call a method.
This one works:
g++ -c -Icryptopp562 -O3 -fPIC -fpermissive CI3CppEncryptionToolsImpl.cpp
gcc -I${JAVA_HOME}/include -O3 -shared -fPIC -o libCI3CppEncryptionTools.so de_zdv_research_emdu_CI3CppEncryptionTools.c CI3CppEncryptionToolsImpl.o -lcryptopp
With this one, I get an UnsatisfiedLinkError:
g++ -c -Icryptopp562 -O3 -fPIC -fpermissive CI3CppEncryptionToolsImpl.cpp
g++ -I${JAVA_HOME}/include -O3 -shared -fPIC -fpermissive -o libCI3CppEncryptionTools.so de_zdv_research_emdu_CI3CppEncryptionTools.cpp CI3CppEncryptionToolsImpl.o -lcryptopp
The generated header is as follows:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class de_zdv_research_emdu_CI3CppEncryptionTools */
#ifndef _Included_de_zdv_research_emdu_CI3CppEncryptionTools
#define _Included_de_zdv_research_emdu_CI3CppEncryptionTools
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: de_zdv_research_emdu_CI3CppEncryptionTools
* Method: encrypt
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_de_zdv_research_emdu_CI3CppEncryptionTools_encrypt
(JNIEnv *, jclass, jstring);
/*
* Class: de_zdv_research_emdu_CI3CppEncryptionTools
* Method: decrypt
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_de_zdv_research_emdu_CI3CppEncryptionTools_decrypt
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
And my implementation (.cpp) is as follows, I omitted the decrypt method:
#include <jni.h>
#include "CI3CppEncryptionToolsImpl.h"
#include "de_zdv_research_emdu_CI3CppEncryptionTools.h"
jstring
Java_de_zdv_research_emdu_CI3CppEncryptionTools_encrypt(JNIEnv *env, jobject obj, jstring s) {
return env->NewStringUTF(encrypt(env->GetStringUTFChars(s, JNI_FALSE)));
}
For the C version I simply write return (*env)->NewStringUTF(env, encrypt((*env)->GetStringUTFChars(env, s, JNI_FALSE))); instead.
The C version works, the C++ version fails with:
Exception in thread "main" java.lang.UnsatisfiedLinkError: de.zdv.research.emdu.CI3CppEncryptionTools.encrypt(Ljava/lang/String;)Ljava/lang/String;
Any ideas?
In implementation (.cpp) you have written function as
jstring Java_de_zdv_research_emdu_CI3CppEncryptionTools_encrypt(JNIEnv *env, jobject obj, jstring s){
//---------
}
Please write it as
JNIEXPORT jstring JNICALL Java_de_zdv_research_emdu_CI3CppEncryptionTools_encrypt
(JNIEnv *, jclass, jstring){
//------------
}
If you compile C++, you should have an extern "C" prefix before the functions declarations/definitions.
But there are others differences, see JNI Calls different in C vs C++?
Make sure java can find your dynamic native library in the same directory where you launch the JVM. Check also that your dynamic library is compiled for the same architecture as the JVM you are running your program with (for example, 32-bit for a 32-bit JMV, or 64 for a 64-bit JVM)
Related
I have a repo with two simple implementations for JNI with C/C++.
I have the java.lang.UnsatisfiedLinkError error for C++ static JNI method.
(base) GlushenkovYuri:java y.glushenkov$ /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/bin/java -Djava.library.path=. MyJNIExample
Hello World from C!
Exception in thread "main" java.lang.UnsatisfiedLinkError: MyJNIExample.sayHelloCpp()V
at MyJNIExample.sayHelloCpp(Native Method)
at MyJNIExample.main(MyJNIExample.java:51)
But for C native method the same approach work well.
For C++ works without static.
You can reproduce the same behaviour in the following way:
1) Uncomment lines with the static native method for C++ and this one. And comment lines with the NON static native method for C++ and this one;
2) And just perform the next steps described in my README.md file;
Can somebody explain to me why the native method static/non static works for C, but only the native NON static method works for C++?
UPD: my header file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class MyJNIExample */
#ifndef _Included_MyJNIExample
#define _Included_MyJNIExample
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: MyJNIExample
* Method: sayHelloC
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_MyJNIExample_sayHelloC
(JNIEnv *, jclass);
/*
* Class: MyJNIExample
* Method: sayHelloCpp
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_MyJNIExample_sayHelloCpp
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
The type of the function that javah generates differs according to whether the native method is static or not:
JNIEXPORT void JNICALL Java_MyJNIExample_sayHelloCppstatic
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_MyJNIExample_sayHelloCppnonstatic
(JNIEnv *, jobject);
In addition, the generated header contains an extern "C" declaration to make sure the compiler keeps the name intact of mangling it like __Z29Java_MyJNIExample_sayHelloCppP7JNIEnv_P8_jobject.
So, what happened here is that you generated a header file with a static native void sayHelloCpp, so there is an extern "C" declaration for the function with signature (JNIEnv *, jclass), but you provide a (JNIEnv *, jobject) function. The compiler uses its default mangling scheme as the signature does not match, and the Java runtime fails to find it.
So, long story short: always regenerate your header file and check C++ function signatures if you change your Java class. I could not reproduce your issue because I wrote a Makefile that always regenerated the header file after recompiling the Java file.
Here is my problem: I'm trying to use JNI in my Java project as a way of talking to the Win32 API. I get this error at run time in eclipse:
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.printer.PrinterWinAPI.GetStatus(Ljava/lang/String;)J
at com.printer.PrinterWinAPI.GetStatus(Native Method)
at com.printer.PrinterWinAPI.<init>(PrinterWinAPI.java:14)
at com.printer.PrinterWinAPI.main(PrinterWinAPI.java:25)
But what is really weird about my issue is that I can successfully compile my project using the cmd:
javac PrinterWinAPI.java
Then the program runs fine by running:
java PrinterWinAPI
From what I understand, eclipse successfully locates my .dll file but can't find the GetStatus() function inside the file. I tried to compile it using visual studio both in x86 and x86_64 AND mingw gcc in both x86 and x86_64. Here are my files:
The java file which implements the JNI interface:
PrinterWinAPI.java
package com.printer;
public class PrinterWinAPI {
static {
System.load("C:\\GetPrinterStatus.dll");
}
public Long status;
public String name;
public PrinterWinAPI(String printerName) {
this.name = printerName;
this.status = this.GetStatus(this.name);
}
private native long GetStatus(String str);
public static void main(String[] args) {
PrinterWinAPI printer = new PrinterWinAPI("PRINTER NAME EXAMPLE");
System.out.println(printer.status);
}
}
PrinterWinAPI.h, generated using javah PrinterWinAPI:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> /* Header for class PrinterWinAPI */
#ifndef _Included_PrinterWinAPI
#define _Included_PrinterWinAPI
#ifdef __cplusplus extern "C" {
#endif /* * Class: PrinterWinAPI * Method:
GetStatus * Signature: (Ljava/lang/String;)J */
JNIEXPORT jlong JNICALL Java_PrinterWinAPI_GetStatus (JNIEnv *, jobject, jstring);
#ifdef __cplusplus }
#endif
#endif
and here is the PrinterWinAPI.c file itself:
#include "PrinterWinAPI.h" // Generated
#include <windows.h>
#include <wincon.h>
#include <winspool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <io.h>
// Implementation of the native method
JNIEXPORT jlong JNICALL Java_PrinterWinAPI_GetStatus(JNIEnv *env,
jobject thisObj, jstring str) {
//some stuff...
return (int64_t)dwStatus;
}
Once again, the program is compiling and running fine using javac, javah and java from the command prompt, I'm 99% sure the issue is from eclipse but I really don't know where to start.
I spent several hours searching for a solution online but couldn't find anything. Why can't eclipse run my project but the java binaries ran manually can?
My jre/jdk is build 1.8.0_101-b13 64-bits
Eclipse: Oxygen.3a Release (4.7.3a)
OS: Windows 7 64-bits
#user2543253 Thank you it worked! It was correctly compiling because my java file wasn't in a package so the header was correct but since the java file was nested in a package in my project the correct header was Java_com_printer_PrinterWinAPI_GetStatus instead of Java_PrinterWinAPI_GetStatus.
I have a library say libraryOne.so (File1.c) which contains pure C code.
Now I want to access this code from my Java file. For this I shall use the standard JNI procedure.
But for using JNI, I should also modify the C code inside my File1.c (like including the header files for JNI and some standard JNIEXPORT stuff).
So I am creating a wrapper library (libraryWrapper.so made from say File2.c). This library will contain the required JNI declarations and shall interact with my Java file.
Now I want to know how can I call the pure C functions of File1.c from File2.c
Firstly, you should read ndk-tutorial. Cause you need to setup ndk and so on. And after you can look for ndk samples to learn how to do things.And how to build
Actually Above is your answer.
Coming to your question.
You should have source code of libraryOne.so. AS you say you have. Cause android target many platforms on which your so can not be run .so it need to be compiled with ndk
OK lets be our file File1.c
int myfunction(const char* st){
int answer=0;
//stufs here
return answer;
}
Firstly, Add Java class from which we want to access
package example.com.myapplication;
public class JniClass {
static {
System.loadLibrary("libraryWrapper");
}
public native int myFunction(String a );
}
Now we need wrapper. For this we should know jni interface. Fortunately there is tool called javah which can generate it for us and save us from doing it manually for all our calls.
We should build our android project so that class files be generated.
I build it with debug so my generated files will be on this /app/build/intermediates/classes/debug folder.
Ok then we run javah
javah -jni example.com.myapplication.JniClass
if you got erros see Javah error while using it in JNI
It generated header file called example_com_myapplication_JniClass.h which contains
#include <jni.h>
/* Header for class example_com_myapplication_JniClass */
#ifndef _Included_example_com_myapplication_JniClass
#define _Included_example_com_myapplication_JniClass
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: example_com_myapplication_JniClass
* Method: myFunction
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_example_com_myapplication_JniClass_myFunction
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
OK now your answer.
Easy way will be to to copy generated code and paste it on the bottom of File1.c code. and implementing it:
Note that I named arguments env,obj and str after pasting it and cleaned comments
//..here your File1.c source code content
//
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_example_com_myapplication_JniClass_myFunction
(JNIEnv * env, jobject obj, jstring str){
const char * x=(*env)->GetStringUTFChars(env,str,0);
//call our function
int ret= myfunction(x);
(*env)->ReleaseStringUTFChars(env,str, x);
return ret;
}
#ifdef __cplusplus
}
#endif
But actually you might use header files on your c code. for example you could add generated header file and implement it on .c file. And on .c file you could include your File1.c header. And this way it be more clean way.
Assuming you already read ndk tutorial.And you setup ndk tools and know how to build
And finally we add File1.c to jni folder and change Android.mk file
LOCAL_MODULE := libraryWrapper
LOCAL_SRC_FILES := File1.c
and
APP_MODULES := libraryWrapper
then we build it with ndk-build which will add our so files to libs
Op wanted only with File1.c and File2.c
inside File2.c
//declaration of funtions inside File1.c
extern int myfunction(const char* st);
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_example_com_myapplication_JniClass_myFunction
(JNIEnv * env, jobject obj, jstring str){
const char * x=(*env)->GetStringUTFChars(env,str,0);
//call our function
int ret= myfunction(x);
(*env)->ReleaseStringUTFChars(env,str, x);
return ret;
}
#ifdef __cplusplus
}
#endif
And change Android.mk
LOCAL_SRC_FILES := File1.c
LOCAL_SRC_FILES += File2.c
I've been looking for 2 days now and no solution could help me, so here we go again:
How to fix the UnsatisfiedLinkError... in JNI?
So here's my java code:
package org.lingenio.util;
import java.util.*;
public class PTAPIWrapperForOmegaT {
private native String translateWithPTAPI(String sentence);
private native void test();
public PTAPIWrapperForOmegaT(String sentence) throws Exception{
System.out.println(sentence);
test();
}
static {
System.load("C:/Users/michael/Desktop/OmegaT/OmegaT2.3_src/native/PTAPIWrapperForOmegaT.dll");
}
}
And here's my C++ Code:
#include <iostream>
#include <windows.h>
#include <jni.h>
#include "PTAPIWrapperForOmegaT.h"
using namespace std;
JNIEXPORT jstring JNICALL Java_PTAPIWrapperForOmegaT_translateWithPTAPI(JNIEnv *env, jobject obj, jstring sentence)
{
/* stuff */
}
JNIEXPORT void JNICALL Java_PTAPIWrapperForOmegaT_test(JNIEnv *, jobject)
{
cout << "This comes from PTAPIWrapperForOmegaT.cpp test();" << endl;
}
int main(){
return 0;
}
And the header file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class PTAPIWrapperForOmegaT */
#ifndef _Included_PTAPIWrapperForOmegaT
#define _Included_PTAPIWrapperForOmegaT
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: PTAPIWrapperForOmegaT
* Method: translateWithPTAPI
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_PTAPIWrapperForOmegaT_translateWithPTAPI
(JNIEnv *, jobject, jstring);
/*
* Class: PTAPIWrapperForOmegaT
* Method: test
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_PTAPIWrapperForOmegaT_test
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
and how I build it:
call g++ -Wl,--add-stdcall-alias -c -DBUILDING_EXAMPLE_DLL -I G:/Software/Java/jdk1.7.0_01/include -I G:/Software/Java/jdk1.7.0_01/include/win32 PTAPIWrapperForOmegaT.cpp
call g++ -shared -Wl,-kill-at -o PTAPIWrapperForOmegaT.dll -I G:/Software/Java/jdk1.7.0_01/include -I G:/Software/Java/jdk1.7.0_01/include/win32 PTAPIWrapperForOmegaT.cpp
and finally, the error:
10211: Error: Uncatched exception in thread [Thread-14]
10211: Error: java.lang.UnsatisfiedLinkError: org.lingenio.util.PTAPIWrapperForOmegaT.test()V
10211: Error: at org.lingenio.util.PTAPIWrapperForOmegaT.test(Native Method)
10211: Error: at org.lingenio.util.PTAPIWrapperForOmegaT.<init>(PTAPIWrapperForOmegaT.java:13)
10211: Error: at org.omegat.core.machinetranslators.LingenioTranslate.translate(LingenioTranslate.java:32)
10211: Error: at org.omegat.core.machinetranslators.BaseTranslate.getTranslation(BaseTranslate.java:64)
10211: Error: at org.omegat.gui.exttrans.MachineTranslateTextArea$FindThread.search(MachineTranslateTextArea.java:122)
10211: Error: at org.omegat.gui.exttrans.MachineTranslateTextArea$FindThread.search(MachineTranslateTextArea.java:102)
10211: Error: at org.omegat.gui.common.EntryInfoSearchThread.run(EntryInfoSearchThread.java:85)
I don't know exactly about these two lines of g++ here, I think the second one would be sufficient, but some tutorial must have offered the other line as well and I kept it.
I'm on Windows 7, using MingW and the latest Java (1.7xxx I believe).
Any help is appreciated, I suspect the error lies in the compilation, but I just don't know how to go on from here.
EDIT:
Looking into the dll with DependencyWalker I can see the functions are named like I named them in the .cpp file. Of course I am calling them from the Java Wrapper with their respective names, i.e. test(). Could that be a problem? Can someone who used JNI often in the past tell me whether this is the correct way?
Turns out all the code is fine. Actually I did make mistakes compiling the header files. You can see if you look at the header files' function names, i.e.:
JNIEXPORT jstring JNICALL Java_PTAPIWrapperForOmegaT_translateWithPTAPI
(JNIEnv *, jobject, jstring);
Now, take a look at your Java files' package membership, in my case:
package org.lingenio.util;
Because I did compile the header file the wrong way, JNI was later not able to find the symbols it was looking for, because it was actually looking for this:
JNIEXPORT jstring JNICALL Java_org_lingenio_util_PTAPIWrapperForOmegaT_translateWithPTAPI(JNIEnv *env, jobject obj, jstring sentence)
So, good luck to the people out there dangling with the same problems. I'm obviously not the greatest Java programmer, that's why I had to worry about this for so long. I should have compiled my header files in the correct way in the first place.
Check your package and classpath!
I tried to implement JNI.
first I create Java class containing one native method, and compile it using "javac HelloWorld.java" and then create header file using "javah HelloWorld" ... here is the code
class HelloWorld {
private native void print();
public static void main(String[] args) {
new HelloWorld().print();
}
static {
System.loadLibrary("HelloWorld");
}
}
HelloWorld.h file is as shown below .....
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* 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
after this i created HelloWorld.c file ... here is the code
#include
#include
#include "HelloWorld.h"
JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
printf("Hello World!\n");
return;
}
and then compile my HelloWorld.c file using below mention command in Visual studio 2008
cl -Ic:\java\jdk\include -Ic:\java\jdk\include\win32 -MD -LD HelloWorld.c -FeHelloWorld.dll
it compiles pretty well and dll and other files are created in the same bin folder where "HelloWorld.class" file is .
but while running java file using "java HelloWorld" command msvcr90.dll file missing error occurs....
I tried to reinstall my JDK but still same problem
what should I do ...
This error is related to build settings in Visual Studio. You can select static link of CRT library (use /MT option instead /MD) or copy msvcr90.dll to directory with your HelloWorld.dll or other directory in %PATH%.