I'm trying to invoke a C++ function from java that uses C++-style strings. The program executes just fine when I'm using C-style strings but just as I declare std::string somehow it can't find dependent libraries anymore. I checked my includes folder in eclipse environment and it does contain <string> library and all its dependencies.
package test_strings;
public class TestString {
static {
System.load("C:\\Users\\aurok\\eclipse-workspace\\native_cpp\\Debug\\libnative_cpp.dll");
}
public native String sayHelloC();
public native String sayHelloCpp();
public static void main(String[] args) {
TestString test = new TestString();
System.out.println(test.sayHelloC());
System.out.println(test.sayHelloCpp());
}
}
And this is my native file:
#include "test_strings_TestString.h"
#include<string>
using namespace std;
JNIEXPORT jstring JNICALL Java_test_1strings_TestString_sayHelloC
(JNIEnv *env, jobject thisObj){
jstring str = env->NewStringUTF("Hello World C-style !!");
return str;
}
JNIEXPORT jstring JNICALL Java_test_1strings_TestString_sayHelloCpp
(JNIEnv *env, jobject thisObj){
//std::string str = "Hello World C++ style !!";
//return env->NewStringUTF(str.c_str());
return env->NewStringUTF("Hello World C++ style !!");
}
This code compiles fine and runs well from java but as soon as I try to use the std::string version(commented) the code compiles and the dynamic library is created but on running the java code I get the following error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\Users\aurok\eclipse-workspace\native_cpp\Debug\libnative_cpp.dll: Can't find dependent libraries
at java.base/jdk.internal.loader.NativeLibraries.load(Native Method)
at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:383)
at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:227)
at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:169)
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2383)
at java.base/java.lang.Runtime.load0(Runtime.java:746)
at java.base/java.lang.System.load(System.java:1857)
at test_strings.TestString.<clinit>(TestString.java:6)
I have searched various sources for a possible explanation but couldn't find one. Guys, help me!
<string> is a header file, and if your C++ code containing #include <string> directive compiles this means that paths of standard include folders are configured correctly.
However, depending on how your project is configured to be linked to the C and C++ runtime libraries (statically or dynamically), the resulting executable may or may not depend on additional DLLs. (For example, in case of Microsoft Visual Studio 2019 C++ compiler, the C runtime library DLL is vcruntime140.dll and C++ runtime library DLL is msvcp140.dll.)
Using std::string probably causes your executable to depend on the C++ runtime library DLL (in addition to the C runtime DLL), and perhaps it is not found in the same folder as the executable (and not in the DLL search path).
On Windows, C runtime DLL is often already available system-wide, but C++ runtime DLL needs to be either installed (using the Microsoft Visual C++ Redistributable package) or placed into the same folder along the executable itself.
Another option is to link runtime libraries statically, so the resulting executable won't have those DLL dependencies. But usually, dynamic linking is preferred.
Note that depending on which C++ compiler you are using with your Eclipse IDE — either GCC (G++), Clang, MSVC or some another — the required C++ runtime DLL will have a different filename.
Thanks a lot heap underrun for your answer. I finally figured out a way to make it work. I used the Dependencies tool by lucasg and found that libgcc_s_seh-1.dll and libstdc++-6.dll were missing even though they are present in MinGW bin.
So I explicitly added them in the same folder as my dynamic library and loaded them in the java virtual environment. This is how the java side looks now:
package test_strings;
import java.io.IOException;
public class TestString {
static {
System.load("C:\\Users\\aurok\\eclipse-workspace\\native_cpp\\Debug\\libwinpthread-1.dll");
System.load("C:\\Users\\aurok\\eclipse-workspace\\native_cpp\\Debug\\libgcc_s_seh-1.dll");
System.load("C:\\Users\\aurok\\eclipse-workspace\\native_cpp\\Debug\\libstdc++-6.dll.dll");
System.load("C:\\Users\\aurok\\eclipse-workspace\\native_cpp\\Debug\\libnative_cpp.dll");
}
public native String sayHelloC();
public native String sayHelloCpp();
public static void main(String[] args) {
TestString test = new TestString();
System.out.println(test.sayHelloC());
System.out.println(test.sayHelloCpp());
}
}
NOTE: The order in which the dlls are loaded is important. It won't work in any another order.
Related
I'm having trouble loading a statically compiled library from Java using System.loadLibrary("") but I can load it as a dynamically compiled library (when I build it that way) just fine. I'm using JDK 8 and my understanding is it can load static libraries via System.loadLibrary("") if you provide a JNI_OnLoad_L in the *.cpp and *.h files.
My kdu_jni.h has:
extern "C"
JNIEXPORT jint JNICALL JNI_OnLoad_kdu_1jni(JavaVM *, void *);
My kdu_jni.cpp has:
JNIEXPORT jint JNICALL JNI_OnLoad_kdu_1jni(JavaVM *vm, void *reserved)
{
return JNI_VERSION_1_8;
}
I have the libkdu_jni.a file in my java.library.path directory when I try to run with the compiled version. It's working fine with a libkdu_jni.so file in that same directory when I try to load it dynamically. When trying with the static file (libkdu_jni.a), I get:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no kdu_jni in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
I've taken out the .so file before trying to load the .a file.
I'm not sure what I'm doing wrong. I don't think it's even seeing the libkdu_jni.a file's JNI_OnLoad_kdu_1jni() because I put an exception in there and I don't see that getting thrown. I've tried several iterations on that name: JNI_OnLoad_kdu_jni(), JNI_OnLoad_kdu_1jni(), JNI_OnLoad(), etc.
Any ideas?
my understanding is it can load static libraries via System.loadLibrary("") if you provide a JNI_OnLoad_L in the *.cpp and *.h files.
Your understanding is incorrect. You can't load a .a file dynamically. It isn't executable in any way shape or form:
infra-library references are not resolved
references outside the library are not resolved either: for example, to the C library.
The link step is essential, and the JVM doesn't do it for you. What you have read applies to libraries statically linked into the JVM.
I suggest you try JNI_OnLoad_kdu_jni as the function name. If that doesn't work, it might not work with library names that contain an underscore.
--- Original post follows ---
Prior to Java 8, only shared object libraries were supported.
This means that to know if the static library is Java 8, a new function must be implemented in the library.
JNI_OnLoad_libname must return a value of JNI_VERSION_1_8 or higher.
I'm guessing since your code works dynamically, but not staticly, perhaps this function is not present. The portion of JEP 178 below lead me to believe this:
The specifications of the java.lang.System.loadLibrary and
java.lang.Runtime.loadLibrary methods will be revised to read:
Loads the native library specified by the libname argument. The libname must not contain any platform-specific prefix, file extension,
or path.
If a native library called libname is statically linked with the VM, then the JNI_OnLoad_libname function exported by the
library is invoked. See the JNI Specification for more details.
Otherwise, the libname is loaded from a system library location and mapped to a native-library image in an
implementation-dependent manner.
Also the notes in the enhancement echo this sentiment
The source code for the loader is helpful
I'd fire up java under debug (gdb) and put a break point in at Java_java_lang_ClassLoader_00024NativeLibrary_findBuiltinLib. You're right, there aren't many great examples.
We are trying to develop a Java application that loads a DLL (written in C++) and uses its functions.
When opening this DLL in "DLL Export Viewer" we can see the full signature of the functions exported, unlike any other DLL we load into the viewer:
Exported view of the DLL that doesn't work
We have tried to create some sample DLLs and load them to Java, and we were successful. The visibile difference was that when we loaded these DLLs we created into "DLL Export Viewer", we saw the functions without full signatures (only names):
DLL We created, works from Java
The code we use to load the DLL from Java is using JNA, and looks like this:
Declaring an interface that mathces the DLL functions:
public interface Ariel extends Library {
Ariel INSTANCE = (Ariel) Native.loadLibrary("ariel", Ariel.class);
void _ZN5ArielC1Ev();
int _ZN5Ariel8getArielEv();
}
Loading it and calling its functions:
public static void main(String[] args) {
Ariel ariel = Ariel.INSTANCE;
ariel._ZN5ArielC1Ev();
System.out.println("done");
}
Only when trying to load the DLL shown in the first image, we can't call any function and we always get the following error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'resetScale': The specified procedure could not be found.
at com.sun.jna.Function.<init>(Function.java:208)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:536)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:513)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:499)
at com.sun.jna.Library$Handler.invoke(Library.java:199)
at com.sun.proxy.$Proxy0.resetScale(Unknown Source)
at Bolet.main(Bolet.java:6)
Your DLL is exporting C++-mangled names. The DLL export is un-mangling them for you. If you examine the DLL with a different viewer, you should see the raw names instead.
If you want to export the names in a non-mangled manner, you need to use the extern "C" decorator on a given function declaration. This generally will only work for static methods (not class methods).
However, this is only part of your problem. If you want to directly map Java classes onto C++ classes, you'll need to use something like SWIG and compile some native glue code.
I have been through so many forum posts on similar issues, but I feel like I've seen it all. In short I have Native code through the wonders of JNI, accessed by Java. The code worked fine before I started using JAVA packages. I have followed all forum posts about ensuring the Javah cmd runs in the correct folder, and have successfully built my DLL. I have ensured that the package in which the wrapper is contained is present in the names of the C++ functions. Due to the size of the project I will simply show an example of this code. NOTE all the code (DLL's, JVM C++ Compiler) operates in 32-bit, and the target is a PC running windows 8.1.
All my Java Code is contained in the package:
package com.optin.executableContainer.client;
So in my Java wrapper class, I load the DLL(32-bit) expressly as follows
static
{
System.load("C:\\JNITests\\JNIBridge.dll");
}
I know this loads the DLL as without the DLL in that folder I get the "Cannot load library" Run-time error. The header code used, generated by the javah -jni command is
JNIEXPORT void JNICALL Java_com_optin_executableContainer_client_COMControl_setControlBackColor
(JNIEnv *, jobject, jlong);
The asscociated C++ code contained within a .cpp file is:
JNIEXPORT void JNICALL Java_com_optin_executableContainer_client_COMControl_setControlBackColor
(JNIEnv *env, jobject obj, jlong color)
{
printf("\nHello World\n");
}
There are no errors when I compile this with Visual Studio 2010. I have verified that this function exists within the targeted DLL, by using DLL export viewer which sees the function name as:
void __stdcall Java_com_optin_executableContainer_client_COMControl_setControlBackColor(struct JNIEnv_ *,class _jobject *,__int64)
However when I run this code, in the Eclipse IDE, I get the dreaded
Exception in thread "main" java.lang.UnsatisfiedLinkError:
com.optin.executableContainer.client.COMControl.setControlBackColor(J)V
at com.optin.executableContainer.client.COMControl.setControlBackColor(Native Method)
If I have missed something along the way, please point it out to me.
Regards
Jarren
UPDATED:
I went and compared the working dll (done before the packaging) and the new dll and spotted a few major differences. Here is the function name of the unpackaged working dll
_Java_COMControl_destroy#8
And here is the function name of the new packaged, unworking dll
void __stdcall Java_com_optin_executableContainer_client_COMControl_destroy(struct JNIEnv_ *,class _jobject *)
The Java code COMControl.java (that produces the COMControl header) declares this function as follows
private native void destroy();
I have tried comparing the differences between the header file that produced the working one and that producing the broken one, and other than the obvious packaging based changes (com_optin etc) there are no other differences. The only difference in the COMControl.java file is the inclusion of the package com.optin.executableContainer.client; at the top of the Java file.
What is going on here?
You are exporting the native routine as a name-mangled C++ function. The reason we can tell this is because the parameters to the function are listed in the export information of the library. Whenever you can tell the types of the parameters that are being passed in the name of a function it implies that the function is being exported as a decorated C++ method. If you looked at the raw export information of the library without demangling, it would look something like:
?Java_Package_ComControl_destroy##YGXPAUJNIEnv_##PAV_jobject###Z
When it looks like this, the java run-time cannot find it, it is supposed to look more like:
_Java_Package_ComControl_destroy#8
which is an undecorated C routine. The #8 is stdcall shorthand for 8 bytes are passed in the stack to the routine
The most common reason for this happening is that the .h file which declares the function and was generated by javah does not match the .cpp file that defines the content of the function - i.e. there is some subtle difference between the .h file and the .cpp file. You should copy-paste the declaration of the function in the .h file into the .cpp file, making sure that all the parameters line up and that all the types line up.
Additionally, for the .cpp file, you have to make sure that it #includes the .h file that was generated by javah, and doesn't just does #include <jni.h>. If you don't do this then the compiler will not know that the routine is to be exported as a C style routine. This is a common reason why the resulting compiled dll does not contain the unmangled version of the method.
If you want to manually enforce the routines being exported using the undecorated C calling convention, in the .cpp file, at the function definition, you can put in:
extern "C"
JNIEXPORT void JNICALL
Java_com_optin_executableContainer_client_COMControl_setControlBackColor
(JNIEnv *env, jobject obj, jlong color)
{
printf("\nHello World\n");
}
By using the extern "C" in that manner, the function will be defined for export as a C routine rather than a C++ routine; but this is undesirable as you should really be including the .h that came from javah.
I don't know if visual studio has an equivalent to the -Wmissing-declarations of gcc which notices inconsistencies like this, which are kind of important for .dlls.
I'm a newbie to JNI, so I was trying this introduction to JNI tutorial earlier that just calls native to print Hello World! Everything went fine until the point that I wanted to run the java file, at which I keep getting the error: Exception in thread "main": java.lang.UnsatisfiedLinkError: no hello library found in java.library.path. I have googled the error and looked at a lot of peoples' suggestions, but none worked for me unfortunately! I have tried the following:
Running with command: java -Djava.library.path = "Path to library" HelloWorld
setting the LD_LIBRARY_PATH to my .so path
Everyone else had their issues resolved after doing one of the two above, but not me!
Here is the Java Code:
public class HelloWorld {
static {
System.loadLibrary("hello");
}
private native void printHelloWorld();
public static void main(String[] args) {
new HelloWorld().printHelloWorld();
}
}
And code for native is as follows:
void JNICALL Java_printHelloWorld(JNIEnv *env, jobject obj) {
printf("HelloWorld!");
}
EDIT: I even tried copying the library to the actually directory of java.library.path, but it's still giving me the same error!
What is your library called? If your paths are correct, your library name is probably wrong. If the library you are loading is called hello, on Windows the file needs to be called hello.dll, on every other SO you also need to prepend the lib prefix:
on OS X (Java < 1.7) libhello.jnilib
on OS X (Java >= 1.7) libhello.dylib
on just about everything else will be libhello.so.
Notice that the Windows dll file is the only file name without the "lib" prefix and that the "lib" prefix is not used when calling System.loadLibrary("hello").
If you are still experiencing a problem loading the lib, try System.load("/path/to/my/libhello.so") to try and load the library directly.
You can always check the file the system will look for by running
System.mapLibraryName(libName)
#Alex Barker
The version
System.load("/path/to/my/libhello.so")
does not resolve dependencies. If there are dependencies upon other user-defined libraries, they need to be loaded before.
I need to call a method in dll using Java Applet.
The dll is written in C++.
Whether it is possible, if so what are the things needed.
can anyone provide sample api
You should use JNI.
Take a look on this article.
Make sure that the environment variable, CLASSPATH, contains a
reference to "[WINDIR]\Java\Classes\Classes.zip" and "C:" (assuming
that C: is your development drive).
Make sure that your "[SDK-Java]\Bin" directory is included in your
path (for JavaH, JVC, and JView).
Make sure that Visual C++ is properly set up for command-line use.
See your Visual C++ documentation for details.
Write your Java code:
public class TestJNI {
public native void greetings();
static {
System.loadLibrary("greet");
}
public static void main(String args[]) {
new TestJNI().greetings();
}
}
Compile the Java file:
jvc TestJNI.java
Run JavaH on the generated class file:
javah -jni TestJNI
Write the C/C++ code based on the generated header file:
#include "TestJNI.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_TestJNI_greetings(JNIEnv *env,jobject jobj) {
printf("Hello from Visual C++!");
}
Compile the C/C++ code:
cl greet.cpp -Ic:\sdk-java.31\include -Fegreet.dll -MD -LD
Test the application:
jview TestJNI
See the linked threads by rkosegi re. 'trusted code' - important to understand. aviad has covered many details of one way to do it. This post will simply focus on deploying the natives.
The real problem with applets using natives is getting the natives installed in a place where the applet can access them. That is where deploying the applet using Java Web Start becomes useful. JWS can not only partition the download of natives according to OS & architecture (32/64 bit), but then make the natives available on the run-time class-path of the app., ready for loading.
As of the 'Next Generation' plug-in (Sun's 1.6.0_10+, for e.g.), JWS could deploy embedded applets (previously it could only launch them free-floating).
But then, why do you want an applet at all?
The better alternative is usually to launch a free-floating frame direct from a link (using JWS). The same security restrictions apply, but it is easier to deploy and a better user experience.