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:

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 Integers.

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)?

#3 2008-01-24 by Johann

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.

#5 2008-02-22 by Johann

Drew,

that's an interesting question. Actually, the spec indicates that you're right and actually the code also works if I change this to dirHandle. 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 ?

#8 2009-02-03 by Johann

Hans,

as simple as that: IntCall ic = new IntCall("my_dll.dll", "fnuh");

#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?

#10 2009-03-14 by Johann

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 ?

#12 2009-04-08 by Johann

Jigs,

as far as I know, LoadLibrary will load the library in the process' address space only once. The IntCall 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.

#14 2009-07-29 by Johann

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

  1. DynaCloud - a dynamic JavaScript tag/keyword cloud with jQuery
  2. 6 fast jQuery Tips: More basic Snippets
  3. xslt.js version 3.2 released
  4. xslt.js version 3.0 released XML XSLT now with jQuery plugin
  5. Forum Scanners - prevent forum abuse
  6. Automate JavaScript compression with YUI Compressor and /packer/

Navigation