I am using the GLEW library glew32.dll (standard download from the GLEW website) and I am trying to load the variable GLEW_OK. This variable is defined in the glew.h file (as a uint of 0), so I am assuming it would be included in the glew32.dll file. However, when I use the Java JNA code :
NativeLibrary glew = NativeLibrary.getInstance("glew.dll");
Pointer p = glew.getGlobalVariableAddress("GLEW_OK");
System.out.println(p.getInt(0));
I am given an error of Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up 'GLEW_OK': The specified procedure could not be found.
at com.sun.jna.NativeLibrary.getGlobalVariableAddress(NativeLibrary.java:587)
at mcclean.opengl.glew.GLEWUtils.init(GLEWUtils.java:22)
The library is loaded fine, but it appears the static variable could not be found. Why is the static variable not being loaded?
Looking at the header file glew.h, GLEW_OK is found as such:
/* error codes */
#define GLEW_OK 0
This is a preprocessor definition. This is not a "static final variable" as you would conceive in the Java world. When a C++ project is compiled, preprocessor #defines are essentially "copypasted" by the preprocessor into the code. This means that if (val == GLEW_OK) is literally changed into if (val == 0).
Since the preprocessor simply replaces the text, there is no information about the names or the origins of the values originating from defines in the .dll file.
You need to manually find the values. You can do this by downloading the binaries from GLEW and navigating to the include/GL/glew.h file. After this, you could create a class for the constants in Java:
class GlewConstants {
public static final int GLEW_OK = 0;
// ...
}
Related
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.
I am compiling this Java file and I get one class file.
My task is to change the Static content "Hello" in the Class file and replace with "Hi".
How to read the Class file first, and how to replace the static content?
public class test {
public static void main(String[] args) {
System.out.println("Hello");
}
}
Is there any standard code(A template) for that ?
You need to use some Java bytecode instrumentation libraries like ASM. Good to start to read links are:
A Guide to Java Bytecode Manipulation with ASM
How To Modify Constant Pool Using ASM?
Does your solution need to be in Java? You can use Jawa to accomplish the same in Python with a lot less boilerplate than ASM and its equivalents.
Install it: pip install jawa
Then:
from jawa.constants import String
from jawa.classloader import ClassLoader
# Create a ClassLoader for the current directory
# and load your "test" class.
test = ClassLoader('.')['test']
# Find the first String with the value "Hello"
# in the constant pool.
constant = test.constants.find_one(
type_=String,
f=lambda c: c.string.value == 'Hello'
)
# Change it to your new value
constant.string.value = 'Hi'
# ... and save the new class.
with open('test.class', 'wb') as new_test:
test.save(new_test)
Result:
$ java test
Hi
Full documentation is at http://jawa.tkte.ch. Regardless of what tool you end up using it's absolutely required to read the JVM ClassFile specification or you won't really understand what's going on. You can find it at https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-4.html.
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.
Situation: I have a library of JNI files, the library is comprised of several functions that are called by the main header file in that JNI library (i.e., code1.h). I have a Java file (i.e., code2.java) that I want to pass to and from JNI header file (code1.h). I created a source code for the (code1.h) called (code1.c).
My question is: Does (code1.h), (code1.c), and (code2.java) have to be the same name for the communication between the JNI and the java?
EDIT: So (code1.h), (code1.c), and (code1.java) all have to be the same name in order for the (code1.java) to pass strings to/from (code1.c)/(code1.h)? And it is not possible to have (code2.java) pass strings to/from (code1.c)/(code1.h) because they are not named the same, is this correct?
For instance,
public class code1 { /*this is code2.java, but should the name be changed to (code1.java) to match that of the JNI?*/
static {
System.loadLibrary("myjni");
}
to pass strings to code1.h/code1.c
This will be compiled for android using Linux Debian"Wheezy" and Eclipse with Android SDK and NDK
While Java requires a match between compilation unit name (SomeClass.java being the name and public class SomeClass{ being the declaration, C does not require this.
You may name the C source and header files as you see fit as long as the function names/exported symbol names match the name of the native method on the java side. For example:
//JavaClass.java
public class JavaClass{
public native String getAString(String in);
}
And header would be:
// any name
JNIEXPORT jstring JNICALL
Java_JavaClass_getAString(JNIEnv *, jobject, jstring);
with matching C files. You could name this header catsMakeTheWorldGoRound.h for all Java cares.
Here is an example of what your "JNI object" should look like.
//In my experience, it is better to put the JNI object into a separate package.
package org.example;
public class Code1
{
static
{
// On a Linux system, the actual name of the library
// is prefixed with "lib" and suffixed with ".so"
// -- e.g. "myjni-java.so"
// Windows looks for "myjni-java.dll"
//
// On a Windows system, we also need to load the prequisite
// libraries first. (Linux loaders do this automatically).
//
String osname = System.getProperty("os.name");
if (osname.indexOf("win") > -1 || osname.indexOf("Win") > -1)
{
System.loadLibrary("myjni");
}
System.loadLibrary("myjni-java");
}
// Now we declare the C functions which we will use in our Java code.
public static native void foo(int bar);
public static native int bar(String foo);
//...
}
Given that you have compiled your JNI library correctly, you can then call the C functions from other Java classes like this:
//Again, in my experience, it is better to explicitly give the package name here.
org.example.Code1 Code1= new org.example.Code1();
Code1.foo(123);
int a= Code1.bar("Hello C function from Java function!");
Does this help you with your question? (I am not an expert in JNI, so I might not be able to help further.)
I have created a Java app which loads one Windows DLL from the static initialization block. The code snippet is given below:
// MyTestJava class starts
class MyTestJava
{
static
{
System.loadLibrary("MyLib");
}
} // MyTestJava class def ends here
I heard that for kinds of library initializations performed from static block, JVM should search in java.library.path. So I set library path for JVM as follows and put MyLib.dll under c:\Libs folder.
options[1].optionString = "-Djava.library.path=C:\\Libs";
Unfortunately I'm getting a pesky Unsatisfied Link Error in my Java app.
Not clear what options[1].optionString = "-Djava.library.path=C:\\Libs"; means. You should pass java.library.path as a java arg
java -Djava.library.path=C:\Libs MainClass
First call getEnv and check if "java.library.path" actually refers to your library.
Else
You can try giving absolute path of file by using
static
{
System.load("C:\\Libs");
}