Can't use JNI to call a dll method - java

I try to write a wrapper for the CDRip library. But I'm very new in JNI. I have a problem to get a native method to work:
public class CDRipJNI {
static {
System.loadLibrary("CDRip");
}
// Get the DLL version number
static native int CR_GetCDRipVersion();
}
In another class I call this method:
int version = CDRipJNI.CR_GetCDRipVersion();
The dll is loaded successfully but the method call fails:
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.test.CDRipJNI.CR_GetCDRipVersion()J
at com.test.CDRipJNI.CR_GetCDRipVersion(Native Method)
at Test.main(Test.java:5)

If it doesn't have a method with that signature, which it doesn't, either your Java class is wrong or it isn't a JNI DLL at all.
If it is a JNI DLL, I don't know why you would have to write any kind of wrapper for it at all: they should provide it.
So it isn't. And if it isn't a JNI DLL, you wil have to write your own JNI DLL that calls it, and that matches your Java class, etc. etc. as per usual JNI.

Related

Invalid memory access error from JNA and DLL

I am having trouble with JNA and a DLL file created from LabVIEW. I am able to call it when I don't use this line, the first one:
FileWriter writer = new FileWriter(FirstPath);
BufferedWriter writing = new BufferedWriter(writer);
writing.write("Here goes my strings");
writing.close();
After this the DLL class is followed as:
DLLClass dll = (DLLClass)Native.loadLibrary("DLLFile",DLLClass.class);
dll.myMethodInsideDLLClass(FirstPath,SecondPath,ThirdPath);
It looks that it's trying to access some random FirstPath or I don't know what. It gives me this error.
Exception in thread "AWT-EventQueue-0" java.lang.Error: Invalid memory access
at com.sun.jna.Native.getStringBytes(Native Method)
at com.sun.jna.Native.getString(Native.java:2224)
at com.sun.jna.Pointer.getString(Pointer.java:681)
at com.sun.jna.Function.invokeString(Function.java:667)
at com.sun.jna.Function.invoke(Function.java:434)
at com.sun.jna.Function.invoke(Function.java:361)
at com.sun.jna.Library$Handler.invoke(Library.java:265)
at com.sun.proxy.$Proxy0.myMethodInsideDLLClass(Unknown Source)
I mean how in the world can I access the same file I am trying to write to and then call it again in the dll method? I tried and nothing worked. Can someone help me? I would appreciate this very much!
Note: This is my DllClass:
public interface DLLClass extends Library{
public int myMethodInsideDLLClass(String
FirstPath, String SecondPath, String ThirdPath);
}
extends Libray comes from jna.jar.
This is what inside my FileDll.h file:
#ifdef __cplusplus
extern "C" {
#endif
int32_t __cdecl myMethodInsideDLLClass(
char FirstPath[], char SecondPath[],
char ThirdPath[]);
MgErr __cdecl LVDLLStatus(char *errStr, int errStrLen, void *module);
void __cdecl SetExcursionFreeExecutionSetting(Bool32 value);
#ifdef __cplusplus
} // extern "C"
#endif
#pragma pack(pop)
After you have tried Daniel's suggestion, and in case it does not work, please try this:
Replace this line:
public interface DLLClass extends Library
With this line:
public interface DLLClass extends com.sun.jna.win32.StdCallLibrary
Another potential problem that I can think of is that you might be using a somewhat recent version of java, but your version of JNA is old, or your version of library "DLLFile" is old, and it does not know that the internal representation of java strings has changed in recent java versions, storing the bytes in whatever encoding was used at the time of creation instead of always UTF16. But this is really grasping at straws here.
Your stack trace gives a strong hint at the source of the problem.
at com.sun.jna.Pointer.getString(Pointer.java:681)
at com.sun.jna.Function.invokeString(Function.java:667)
If you look at the JNA source for invokeString() you'll see it's calling the getString() method, assuming 1-byte character encoding (ASCII). But Windows by default uses a 2-byte Unicode character encoding, and this method needs to know to use a wide string so it will call getWideString().
This can be addressed by assigning the appropriate Type Mapper when loading the DLL. The easiest way to do this is to add the default Windows type mapping:
DLLClass dll = (DLLClass) Native.loadLibrary("DLLFile", DLLClass.class,
W32APIOptions.DEFAULT_OPTIONS);
That is the standard way of doing it with a WinAPI method. Technically if your method is not part of the WinAPI you should probably define your own type mapper, using that one as a template.

Can we use ".so" library compiled for Linux into android?

I am writing a wrapper class for C++ ".so". I want to use the library in Java application and Android app using JNI. So I have to create header file and cpp file which will do JNI calls.
I could use that on Linux in Java application.
The steps I followed:
Created java class and called native functions in that class
public class TestWrapper {
static {
System.load("/home/native.so");
}
public static void main(String[] args) {
new TestWrapper().TestWrapper();
}
private native void sayHello();
}
Created header file and cpp file. CCP contains following code
JNIEXPORT void JNICALL Java_TestWrapper_sayHello(JNIEnv *, jobject){
uint16_t data = 0;
void (*func_print_name)(const uint16_t*);
void* handle = dlopen("libCppTobeUsed.so.0", RTLD_LAZY);
if (handle){
*(void**)(&func_print_name) = dlsym(handle, function_name);
func_print_name(&data);
dlclose(handle);
std::cout << "data received .." << data << std::endl;
}
}
}
Compiled this cpp class and generated "native.so"
This is working fine. The "native.so" could call the fuction form "ibCppTobeUsed.so.0" when called from TestWrapper.java.
I want to use same library for android as well. So, I have to write wrapper class all over again in Android NDK? Or I can compile my "native.so" for Android platform?
If I try to use it directly, I get error
"install_failed_no_matching_abis".
No, you cannot use the same shared library. Android is not GNU. You need to compile your libraries for Android.
So, I have to write wrapper class all over again in Android NDK?
No, you can write it in a way that works for both. You need to factor our your JNI wrapper class from your main class, since Android uses Activity instead of main.
I would also strongly recommend against ever relying on dlclose on any platform. The API is not sound, and will lead to surprising behavior with modern C++. A single global thread_local with a non-trivial destructor renders the library un-unloadable, so the next dlopen will not reset library state as you might expect. If you need to implement initialization/finalization logic for your library, make explicit Initialize and Finalize functions a part of the libary and call them directly.
Without knowing your architecture's full architecture I can't be sure, but from the sample you've given here I'd recommend dropping the dlopen/dlsym from your JNI entirely and just link against libCppTobeUsed directly.

java.lang.UnsatisfiedLinkError when loading .dll

I am trying to use a public interface in a .dll file in JAVA. This is what a public interface in the .dll shows:
// Interface declaration.
public interface ISslTcpClient
{
string Encrypt(string requestContent);
string Decrypt(string requestContent);
};
Here is how I load my dll and use it in my Java application in eclipse:
public class NJInew {
static {
System.loadLibrary("ICVTnsClient");
}
native String Encrypt(String requestContent);
public static void main(String[] args) {
NJInew p= new NJInew ();
p.Encrypt("pejman");
}
}
However I get the following error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: NJInew.Encrypt(Ljava/lang/String;)Ljava/lang/String;
at NJInew.Encrypt(Native Method)
at NJInew.main(NJInew.java:13)
Please let me know how to fix this issue and I would greatly appreciate it.
I do not understand how the native DLL can possibly look like a Java interface. Are you sure it's a JNI DLL? You can't call any old DLL from Java. If it isn't a JNI DLL you'll either need to add the required JNI entry points to it, starting with the javah tool, or write a wrapper DLL that contains those and calls this one.
This interface looks definitely like written in .NET C#. Therefore you do not need to call native DLL but managed .NET DLL and that is completely different topic.
You can achieve it yourself by wrapping .NET DLL with native C++ code and calling it from JAVA but number of obstacles and challenges is huge... You need to take care of type conversion, memory management, threads, C++ runtime depndencies and more... so that approach is not recommended.
What you should look for is native bridge like Javonet which takes care of all those things for you and makes sure you can run your code on any machine.
Using your interface would like like this:
Javonet.addReference("ICVTnsClient.dll");
NObject client = Javonet.New("SslTcpClient");
//some concreto type which implements this interface
String encryptedStr = client.invoke("Encrypt", "some string");
You can get free license for academic and non-commercial usage or try the free trial: http://www.javonet.com

Using Java class in Matlab

I've been struggling with this problem for two days now and no resource I've found have been able to solve it.
I am trying to call a java class (added the link at the bottom) from Matlab (version 7.13.0.564 (R2011b)). I've compiled the java class using java 1.6 into a .class file and also added the path to the folder where the file is situated using javaaddpath (I've of course checked that the path is correct in the list of dynamic paths). However, when I try to call the class from Matlab using javaMethod('main','PerlinNoiseGenerator','') I get the error:
"No class PerlinNoiseGenerator can be located on Java class path"
I would be extremely grateful if someone with experience in calling java from Matlab could put together a short tut on how to do this. I am probably going to distribute my code so I kinda need to set the java path dynamically and from what I've read it really should be possible although I've seen post that indicate that it could be the cause of the problem.
http://svn.j3d.org/code/tags/Xj3D-M10/src/java/org/j3d/texture/procedural/PerlinNoiseGenerator.java
Usually I create jar files that contain java classes. I also had problems loading individual java classes before. In your case I did the following on xubuntu 13.04 x64 and Matlab 2013a x64 to load your particular class:
Compile it using java 6 (not the default 7) with option -d . to create a set of package folders, as your class defines a package org/j3d/texture/proecedural/ etc:
/usr/lib/jvm/java-6-openjdk-amd64/bin/javac -d . PerlinNoiseGenerator.java
This will compile the class and make in the current director (thus .) the set of package folders.
Make jar file containing your class again using jar from java 6. I named it javaNoise.jar:
/usr/lib/jvm/java-6-openjdk-amd64/bin/jar cf javaNoise.jar ./org/j3d/texture/procedural/PerlinNoiseGenerator.class
In Matlab, in the directory where javaNoise.jar is:
javaaddpath('./javaNoise.jar');
Create object of your java class:
png=org.j3d.texture.procedural.PerlinNoiseGenerator()
% results in: png = org.j3d.texture.procedural.PerlinNoiseGenerator#3982a033
To test it, I just generated some 1D noise:
png.noise1(1.2)
ans = -0.0960
Hope this helps.
P.S.
javaMethod('main','PerlinNoiseGenerator','') wont work because this class has no main method:-).
Your notation to the compiler of the constructor is a polymorphic class meaning "use appropriate constructor that is called at runtime".
public PerlinNoiseGenerator()
public PerlinNoiseGenerator(int seed)
The first form with no argument can be called but is irrelevent because the line with this(DEFAULT_SEED) attempts to call itself but only one constructor is allowed used
Second constructor has int for an argument but requires being loaded by an already loaded class.
Use the first version and change the case sensitive name of the one with the argument and remove this(DEFAULT_SEED) from it replace with the method name(the one you changed from a constructor that has the argument).
e.g. public perlinNoiseGenerator(int seed)
note: by convention java code method names start with a lower-case letter.
A final note, java arguments from the command line come in as "String" data type through the "main" method, a starter method for applications (gui or command prompt).
The first argument on the main method argument is the first commandline argument.
public static void main(String[] Args){
new PerlinNoiseGenerator(Args); // recursive class call
}//end main method
int[] args; // global
public PerlinNoiseGenerator(String[] Args){
int arglength=Args.length();
args = new int[arglength];
for(int cnt=0;cnt<arglength;cnt++){
Args[cnt].trim();
args[cnt]=new Integer(Args[cnt]).intValue();
}//enfor
perlinNoiseGenerator(args[0]); // call method
}//end constructor

call method from existing external .dll. For ex, CopyFileA from kernel32.dll

The task is to call method from an existing dll.
I'm trying to do that on an example of CopyFileA from kernel32.dll.
The method signature is:
Function long CopyFileA(String lpExistingFileName, String lpNewFileName, boolean bFailifExists) Library "kernel32"
This is how I'm trying to do this in java:
public class Test {
static {
System.loadLibrary("D:\\test\\kernel32");
}
public static void main(String[] args) {
(new Test()).CopyFileA("D:\\test\\hi.txt", "D:\\other\\hi.txt", false);
}
public native long CopyFileA(String lpExistingFileName, String lpNewFileName, boolean bFailifExists);
}
I'm getting:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.CopyFileA(Ljava/lang/String;Ljava/lang/String;Z)J
All manuals that I've found describes examples when you write C code and then create dll for yourself. So, you implement native method with signature from generated header file.
But here we already have a dll.
Thanks!
The examples you have seen are the best way to go. There is some harness code that needs to be done to enable Java to call into a native method and visa-versa. With out this harness code there is no way for either of them to communicate with each other.
If you are desperate to call CopyFileA then create the harness code in some C/C++ code that then calls CopyFileA.
If you are trying to avoid programming in C/C++ then there is no way for your java to communicate with CopyFileA.
There may be a third party code that may help you. I don't know of any.
This is really simple: everything you need is download jna.jar file and include it into your project.
Bellow I put some code snippet how to solve your task:
Function showWindow = Function.getFunction("kernel32", "CopyFileA");
Object[] params = new Object[3];
params[0] = "D:\\test\\hi.txt";
params[1] = "D:\\other\\hi.txt";
params[2] = false;
Object result = showWindow.invoke(params);

Categories