Call native methods in a DLL from Java without JNI
Posted 2007-05-27 in Java by Johann.
In this example, we will monitor a directory without continously polling it’s contents. Instead, we’ll rely on the Windows API that we access through NativeCall – that is, without JNI.
The functions
To monitor the directory, we need the following functions:
CreateFileA
to create a handle to the directory.CloseHandle
to close the handle.ReadDirectoryChangesW
to monitor it for changes.
These functions are all implemented in kernel32.dll
. Fortunately, this is the default DLL in NativeCall so we don’t need to specify it.
Getting started
After unpacking the NativeCall distribution, we add the JAR to the CLASSPATH
and set up java.library.path
to point to the directory with the NativeCall native library in it. We then initialize NativeCall by calling NativeCall.init()
.
Functions
We obtain references to the functions with the following code:
IntCall createFile = null; IntCall closeHandle = null; IntCall readDirectoryChanges = null; int dirHandle = 0; try { createFile = new IntCall("CreateFileA"); closeHandle = new IntCall("CloseHandle"); readDirectoryChanges = new IntCall("ReadDirectoryChangesW"); … } finally { if (closeHandle != null) { if (dirHandle != 0) { closeHandle.executeBooleanCall( new Integer(dirHandle)); } closeHandle.destroy(); } if (readDirectoryChanges != null) { readDirectoryChanges.destroy(); } if (createFile != null) { createFile.destroy(); } }
Calling destroy
in a finally
block is important to ensure that all resources are freed. If you forget this, you will leak memory.
Directory handle
The directory handle is acquired with the following code:
dirHandle = createFile.executeCall(new Object[] { new Integer(dirHandle), DESIRED_ACCESS, SHARE_MODE, null, CREATION_DISPOSITION, FLAGS_AND_ATTRIBUTES, null });
The constants you see are simple Integer
s.
Monitoring the directory
All that’s left to do now is to call the ReadDirectoryChangesW
method. In this example, this is a synchronous call which will block until the directory is changed.
Holder returnedBufferLength = new Holder(0); byte[] buffer = new byte[1024]; Arrays.fill(buffer, (byte) 0xff); Holder fileNotifyInformation = new Holder(buffer); readDirectoryChanges.executeBooleanCall(new Object[] { directory.getCanonicalPath(), fileNotifyInformation, new Integer(buffer.length), false, NOTIFY_FILTER, returnedBufferLength, null, null });
Get the code
Download DirectoryMonitor.java
and DirectoryMonitorTest.java
, the test case and start hacking.
15 comments
#1 2007-07-21 by Paul
Just to let you know that NativeCall works for me. I had the problem that my server would spontanuously go back to sleep after a hibernate, which is solved by calling the windows SetThreadExecutionState periodically in a separate thread within my webapp, using NativeCall. Works like a charm on both XP and 2000. Thanks for the good work and keep it going !
#2 2008-01-24 by Alex
How does this compare to JNative?
(http://sourceforge.net/projects/jnative)
Does NativeCall support callbacks / events (e.g. passing functionpointers to and methodcalling by the dll)?
Alex,
JNative looks like a higher level JNI bridge to me. It does look a lot more complicated than NativeCall though.
NativeCall currently does not support callback functions.
#4 2008-02-20 by Drew
I the example you have the code:
readDirectoryChanges.executeBooleanCall(new Object[] {
directory.getCanonicalPath(), fileNotifyInformation,
new Integer(buffer.length), false, NOTIFY_FILTER,
returnedBufferLength, null, null });
shouldn't this really be,
readDirectoryChanges.executeBooleanCall(new Object[] {
dirHandle, fileNotifyInformation,
new Integer(buffer.length), false, NOTIFY_FILTER,
returnedBufferLength, null, null });
Notice the first object in the parameter array.
Drew,
that's an interesting question. Actually, the spec indicates that you're right and actually the code also works if I change this todirHandle. The questions is why does that function also allow directory names?
#6 2008-02-22 by Drew
On my Windows 2000 machine this block of code returns false,
readDirectoryChanges.executeBooleanCall(new Object[] {
directory.getCanonicalPath(), fileNotifyInformation,
new Integer(buffer.length), false, NOTIFY_FILTER,
returnedBufferLength, null, null });
when I add this code,
System.out.println(readDirectoryChanges.getLastError());
I get: "The handle is invalid."
After a few minutes of banging my head against the wall and checking out the MSDN, I realized that it was right, the handle is invaild (since we specified it as the path and not the CreateFileA returned handle).
The supplied test case passes since it just checks if "mon.monitor();" returns; it does not check if an error caused it to return.
But anyway, its a simple fix. Great work on NativeCall; as a Java developer, I hate the need for JNI, and I welcome anything that makes the job faster and easier.
#7 2009-02-03 by Hans
What if I had to use another dll (not kernel32.ddl) ? What extra statement do I have to put where ?
#9 2009-03-14 by David
You mention that nativecall.dll needs to be in the java.library.path. Where does the dll that nativecall is calling need to be? ie how does nativecall.dll find it?
David,
NativeCall delegates to LoadLibrary so you use a full path if you need to.
#11 2009-04-07 by Jigs
How Can I call multiple methods by using only one Dll .
I just want to load that dll only onces and call all the other required methods and unload the dll at last.
So ,
NativeCall.init();
IntCall t=new IntCall("CustomDLL","first function") ;
t.executableCall();
IntCall t1=new IntCall("CustomDLL","first function1") ;
t1.executableCall();
I dont want to load the same dll again ,
Is there any way we can do that with NativeCall ?
Jigs,
as far as I know,LoadLibrary
will load the library in the process' address space only once. TheIntCall
initialization is nearly free, too.
If you are concerned with the initialization costs, keep references to the NativeCall objects longer.
#13 2009-07-29 by CC
I am newbie using NativeCall.
NativeCall allows us to invoke "Exported Function" DLL Type. Any ideas how to invoke "COM Method" inside a custom DLL?
Please advice.
CC,
what did you try so far and why didn't it work? As long as there are some exported symbols, I don't see why NativeCall shouldn't work.
#15 2009-07-29 by CC
Hi Johann,
I can see 2 DLL types from DLL Viewer. one is Exported Function, another is COM Method. NativeCall can call Exported Function but i have no idea on how to call COM Method. Any clues?
For info, i am calling custom developed DLL and not W32 DLL.
Thanks.
Subscribe
RSS 2.0, Atom or subscribe by Email.
Top Posts
- DynaCloud - a dynamic JavaScript tag/keyword cloud with jQuery
- 6 fast jQuery Tips: More basic Snippets
- xslt.js version 3.2 released
- xslt.js version 3.0 released XML XSLT now with jQuery plugin
- Forum Scanners - prevent forum abuse
- Automate JavaScript compression with YUI Compressor and /packer/