Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top