JNA to call a DLL without source code from java - java

I have to call a dll method and I don't have the source code from dll, I was reading about JNI and understood that you should have the source to input the JNI library in the code (.h).
My second shoot is JNA, but I am getting the same error, although you don't have to change anything in DLL.
I created two classes to test:
interface:
package icom;
import com.sun.jna.Library;
public interface IConectorT extends Library {
int StartConector(byte[] conectorStatus, String icomPath);
}
DLL method call:
package icom;
import com.sun.jna.Native;
public class ConectorTJna {
public static void main(String args[]) {
IConectorT lib = (IConectorT) Native.loadLibrary("ConectorT", IConectorT.class);
int teste = lib.StartConector(null, "C:\\ICOM");
System.out.println("RESULT: " + teste);
}
}
When I call the lib.StartConector method I get this:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error
looking up function 'StartConector': The specified procedure could not
be found. at com.sun.jna.Function.(Function.java:179) at
com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:350) at
com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:330) at
com.sun.jna.Library$Handler.invoke(Library.java:203) at
$Proxy0.StartConector(Unknown Source) at
icom.ConectorTJna.main(ConectorTJna.java:10)

Did you specify path to the library, e.g. using system property?
Here are the details from "Getting Started with JNA" guide:
Make your target library available to your Java program. There are two
ways to do this:
The preferred method is to set the jna.library.path system property to
the path to your target library. This property is similar to
java.library.path, but only applies to libraries loaded by JNA.
Change the appropriate library access environment variable before launching
the VM. This is PATH on Windows, LD_LIBRARY_PATH on Linux, and
DYLD_LIBRARY_PATH on OSX.
Taken from: https://github.com/twall/jna/blob/master/www/GettingStarted.md

Related

Ecplise Java JNI, java.lang.UnsatisfiedLinkError loading dll

I'm having a problem with loading printer dll. I have a dll file from the printer manufacturer (JniPrinterStatusLib.dll). I wrote code like printer manufacturer suggested. The code is:
package com.printer.test
public class JniPrinterStatus {
static{
System.loadLibrary("JniPrinterStatusLib");
}
public native int GetStatus(String printer);
}
package com.printer.test
public class TestSample {
public static void main(String[] args) {
int status;
String printer = "MY PRINTER";
JniPrinterStatus jps = new JniPrinterStatus();
System.out.println("PRINTER NAME = " + printer);
status = jps.GetStatus(printer);
if (status == -1) {
System.out.println("status = -1");
}
else if (status == 0) {
System.out.println("status = NORMAL");
}
else if ((status & 0x00000080) != 0) {
System.out.println("status = PRINTER_STATUS_OFFLINE");
}
else if ((status & 0x00400000) != 0) {
System.out.println("status = PRINTER_STATUS_DOOR_OPEN");
}
else if ((status & 0x00000010) != 0) {
System.out.println("status = PRINTER_STATUS_PAPER_OUT");
}
else if ((status & 0x00000800) != 0) {
System.out.println("status = PRINTER_STATUS_OUTPUT_BIN_FULL");
}
else if ((status & 0x00000040) != 0) {
System.out.println("status = PRINTER_STATUS_PAPER_PROBLEM");
}
}
}
I used Eclipse to run the code, i put the dll library in the folder project and the error is
PRINTER NAME = MY PRINTER
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.printer.test.JniPrinterStatus.GetStatus(Ljava/lang/String;)I
at com.printer.test.JniPrinterStatus.GetStatus(Native Method)
at com.printer.test.TestSample.main(TestSample.java:10)
If i move the source from the package "com.printer.test" to default package the code works and show:
PRINTER NAME = MY PRINTER
status = -1
I don't know how it's possible. If i compile and run the code from command prompt without package it works.
Where is the problem?
Thank you
From the javadoc for class UnsatisfiedLinkError...
Thrown if the Java Virtual Machine cannot find an appropriate
native-language definition of a method declared native.
That means that function Java_com_printer_test_JniPrinterStatus_GetStatus is not found.
Method loadLibrary in class java.lang.System usually searches the directories listed in the [System] property "java.library.path". For Windows machines, the value of this property is generally the value of the PATH environment variable.
So I suggest printing out the value of that property in your code to see whether it includes the directory containing your DLL. If it doesn't then you need to fix that, either by relocating the DLL or changing the PATH environment variable or launching your java program with the -Djava.library.path=... option. After that you need to check the signature of the native method. Dependency Walker is a tool I use at my work to accomplish this.
EDIT
Having re-read your question, I feel I did not accurately address your question, so let me add...
The default behaviour of Eclipse is to copy resource files, like DLLs, to the output folder. So if you put your DLL in folder src\com\printer\test, it will get copie to folder bin\com\printer\test. My guess is that the current, working directory, i.e. . is in your "java.library.path" which is why it works when your java code is in the default package.
Sorry, actually I wanted to write a comment, but as I'm still low on reputation, I have to try and guess an answer.
There should be no need to recompile the dll - it's just some native code to be invoked.
The java package of the class loading the dll should not make a difference, either.
You have to take care about your system architecture: A 64-bit dll file will fail in a 32-bit JRE and vice versa. Make sure, your JRE architecture matches the dll architecture.
Another thing to take into account is your working directory. Eclipse may use a working directory different from what you used when you ran you program from console.
Last but not least, please have a look at your java.library.path variable.
This page might also help: https://www.chilkatsoft.com/java-loadLibrary-Windows.asp
I covers all the details.
The expected package of the Java classes is hard-coded in the JNI library. In your case, it's the default package.
Let me expand on that. When one implements a native method in a JNI library, one has to create a public C function with a name in the following format:
Java_com_mypackage_MyClass_MyMethod
In other words, the JNI library can't provide methods for the classes in arbitrary packages - only for classes in packages that the JNI library authors had in mind.
In your case, it's the default one. The C function goes Java_JniPrinterStatus_GetStatus. If you call your class MyPrinterStatus, or place it into package com.foobar, the JNI run-time won't be able to associate the C function with the declared Java native method. That's just how JNI was designed.

Using VC++ dll using JNA

I am using a BTICARD.DLL, which is the dll of Arinc429 card. I need to write wrapper class in Java for the functions like BTICard_CardOpen for example.
I Had written an interface below BTICardAPI.java:
package NLIPjt;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.Native;
// import com.sun.jna.ptr.IntByReference;
import com.sun.jna.Pointer;
public interface BTICardAPI extends StdCallLibrary {
BTICardAPI INSTANCE = (BTICardAPI) Native.loadLibrary("BTICARD", BTICardAPI.class);
int BTICard_CardOpen(Pointer LPHCARD, int cardnum);
}
and my Java implementation prog
BTICardTest.java:
package NLIPjt;
// import com.sun.jna.ptr.IntByReference;
import com.sun.jna.Pointer;
public class BTICardTest {
public static void main(String args[]) {
BTICardAPI BTI1 = BTICardAPI.INSTANCE;
int iErr;
int CardNo = 0;
Pointer CardHandle = null;
iErr = BTI1.BTICard_CardOpen(CardHandle, CardNo);
System.out.println("Error Value: " + iErr);
}
}
i get the following error in netbeans IDE:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'BTICard_CardOpen': The specified procedure could not be found.
at com.sun.jna.Function.<init>(Function.java:245)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:566)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:542)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:528)
at com.sun.jna.Library$Handler.invoke(Library.java:228)
at com.sun.proxy.$Proxy0.BTICard_CardOpen(Unknown Source)
at NLIPjt.BTICardTest.main(BTICardTest.java:14)
Looking for a solution!!
According to the documentation you need to make the library available. There are three ways to do this.
Make your target library available to your Java program. There are
several ways to do this:
The preferred method is to set the jna.library.path system property to
the path to your target library. This property is similar to
java.library.path, but only applies to libraries loaded by JNA.
Change the appropriate library access environment variable before
launching the VM. This is PATH on Windows, LD_LIBRARY_PATH on Linux,
and DYLD_LIBRARY_PATH on OSX.
Make your native library available on your classpath, under the path
{OS}-{ARCH}/{LIBRARY}, where {OS}-{ARCH} is JNA's canonical prefix for
native libraries (e.g. win32-x86, linux-amd64, or darwin). If the
resource is within a jar file it will be automatically extracted when
loaded.

Getting java.lang.UnsatisfiedLinkError at runtime

I'm trying to use JNI to access C++ methods from a Java class. I'm able to compile (both in Eclipse or on command line) my Java class fine, but on executing the class at runtime, I'm getting:
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.domain.services.CallServiceAPIS.createSession()I
at com.domain.services.CallServiceAPIS.createSession(Native Method)
at com.domain.services.CallServiceAPIS.main(CallServiceAPIS.java:18)
Java code is as follows:
package com.domain.services;
public class CallServiceAPIS {
static {
System.loadLibrary("service.client");
}
public native int createSession();
public static void main(String[] args) {
System.out.println(System.getProperty("java.library.path"));
new CallServiceAPIS().createSession();
}
}
I included the printout of the java.library.path just to make sure it's pointing to the correct location of the C++ library - and it is. I also tried setting the LD_LIBRARY_PATH in my Eclipse environment. But neither worked.
Note that the System.loadLibrary call IS working since 1) the code compiles and 2) the error occurs on line 18, which is the new CallServiceAPIs call.
C++ code:
int createSession(const PosServiceInfo info, const SessionArgs& args, Domain::UUID& uuidSession)
{
return int::undefined;
}
Any ideas?
Never mind. I realized that I was using the JNI interface incorrectly. I was thinking you could load an EXISTING C++ library using EXISTING C++ source. But you basically have to rewrite the existing code to make use of the JNI interface.

JNI Use native method

I'm working on a project where there's the need to call some methods from dll files.
These two dlls are
EasySign.dll
EasySignJNI
EasySignJNI depends on EasySign.
I wrote the class to load EasySignJNI as follows:
package easysign;
class EasySign {
EasySign(){}
public native String EasyHashFile(String filename);
public native int EasySign(String pkcs11_driver,String pin, int type, String file_data, int out_format, String signed_file, String cert_out, int cert_format);
public native int EasyVerify(String cert_user, String file_data, String signed_file, String crl_file, String ca_file, String out_document);
static {
System.loadLibrary("EasySignJNI");
}
}
Now I would call these method from my main method like this:
public class Test {
public static void main(String[] args) throws IOException{
EasySign es = new EasySign();
System.out.println("EasyHashFile : " + es.EasyHashFile("test.txt"));
}
}
What I have to specify in the -Djava.library.path ? Only the path where my EasySignJNI.dll is located? It is possible to call native method in this way?
I'm using NetBeans for completeness.
EDIT:
I have noticed that the third party dll provided to me (the JNI dll in particular) defines method names without any package, so I'm forced to put the class that loads the dll in the default package. Is there any way to change only the dll method names including my own package name?
EDIT 2:
What I mean is that both EasySign.dll and EasySignJNI.dll are provided me as they are and I can't modify them (I have not the source code). The EasySignJNI is the JNI portion but inspecting it I have noticed thath the method sign is in the form: _java_EasySign_MethodName. When I load the dll in Java from my Easysign class (this class must reside in the "mypackage" package), I receive the jni unsatisfiedlinkerror because, if I understood right, I'm calling the "_java_mypackage_EasySign_MethodName" method, i.e the sign is different from the dll's one. So the only way to make it work is to rewrite the JNI part and build it to have the correct sign of the JNI method?
What I have to specify in the -Djava.library.path ? Only the path where my EasySignJNI.dll is located?
Correct, the operating system will locate the dependent EasySign.dll for you as long as it is available where the OS expects it to be.
It is possible to call native method in this way? I'm using NetBeans for completeness.
I read through your edit and you have successfully lost me. What default package are you referring to? (Remember that none of us know what EasySign.dll is) So, I am going to provide some info about how I do what you originally described and hopefully it will help.
First start by compiling EasySign if you have the src. Do not build a DLL or shared object, instead build a static library. If you do not have the src code for EasySign, or a prebuilt static library, you will be stuck with the dll and can continue to the next step.
Now you are ready to compile the jni portion. All of your JNI C code should basically translate your Java input/output to their JVM/Native types and call the appropriate functions in the DLL library. You want to keep this layer and thin and simple as possible because it is incredibly difficult to debug. Your C++ package names shouldn't really matter here and you can use what ever package name you want for your Java classes. You should be able to compile the JNI code and preferably static link to the EasySign.dll file so you don't need to worry about distributing it. If you must dynamically link, make sure EasySign.dll gets installed to a location that is on the DLL PATH / LDPATH because the OS will need to locate and load that file right after the JVM loads the JNI DLL.
At this point you should just be able to point -Djava.library.path at your JNI DLL's path and all should work.

Any DLL import throws an UnsatisfiedLinkError, though the DLL seems to load

I use Eclipse Java 32Bit JDK. I am trying to import a 32-Bit DLL, which i got very few documentation for. I am using com.sun.jna.
The documentation tells me to call the method InitImagerIPC without any parameters.
As in this example i created an interface:
public interface ImagerIPC extends com.sun.jna.win32.StdCallLibrary{
HRESULT InitImagerIPC ();
}
When I try to import the DLL no error occurs.
ImagerIPC lib = (ImagerIPC) Native.loadLibrary ("ImagerIPC", ImagerIPC.class);
But I receive an UnsatisfiedLinkError if I mistype my DLL name. So I guess it's loaded correctly?
But when I try to call
lib.InitImagerIPC();
I get the UnsatisfiedLinkError. :(
Where's the mistake?
BTW: The DLL Export Viewer pulls out this info for the wanted method:
_InitImagerIPC#0 0x10001fc0 0x00001fc0 45 (0x2d)
ImagerIPC.dll J:\<my Path>\ImagerIPC.dll Exported Function
I am not sure about the naming. Do I also have to add the _?
Your DLL uses the stdcall calling convention, which typically has the compiler mangle function names to include the arguments' stack size. You need to use a StdCallFunctionMapper passed in as the Library.OPTION_FUNCTION_MAPPER option when you load your library (or recompile your library to use undecorated names).
Library.loadLibrary("myLib", myLib.class, new HashMap() { { put(Library.OPTION_FUNCTION_MAPPER, new StdCallFunctionMapper()); } });

Categories

Resources