views:

667

answers:

3

I need to call some methods in Wininet.dll from a Java program.

I can find plenty of tutorials on how to call a new DLL that I create from Java, but I can't seem to find any tutorials on how to call an already existing DLL from Java.

I know this involves JNI, but how, exactly, do I do this? Do I need to call javah on Wininet.h? Where do I get a copy of Wininet.h? A pointer to an existing, detailed tutorial would be sufficient.

+2  A: 

One cannot directly call native libraries: this is because some arguments in methods are not supported by the Java Native Interface.

There is something called GlueGen, this will create a seperate binary which will dynamically link against your native DLL. The generated binary is compatible with the JNI and thus callable from Java.

http://en.wikipedia.org/wiki/Gluegen

To get a header-file for wininet.dll, you probably need to have the Platform SDK (or Windows SDK, newest version). You can also search the net for an online repository containing this header-file.

GlueGen will need a header-file, a ANSI C compiler, etc.

There is also a less known library called NativeCall, which doesn't require GlueGen. It already has binaries which are Java compatible. This is obviously slower, since it will load the DLL dynamically upon request from Java. I haven't used this, yet, but it sounds promising:

http://johannburkard.de/software/nativecall/

Pindatjuh
+9  A: 
  1. JNA seems the industry standard of what you want, "provides Java programs easy access to native shared libraries (DLLs on Windows) without writing anything but Java code—no JNI or native code is required"

  2. There is also Java Foreign Function Interface - example usage
    If is ok for you, you could embed the JRuby interpreter and call winapi via jruby-ffi wich is a lot easier, see here, here, and here

clyfe
This needs a bump - JNA is far far better than pure JNI for native library access.
Chris Kaminski
+1: JNA is good choice. Although, you need to figure out how to map the function signature in Java. I recently used JNA to communicate with a bluetooth device via native Win32 API.
wierob
JNA is really slick. When I was explaining it to a friend at work the other week, I actually went so far as to tell him it's "magic."
rob
For this sort of incidental native call, JNA is perfect. You wouldn't want to use it for high performance stuff, though - if you are doing low level socket stuff with WinInet, going with a true JNI implementation will be the way to go.
Kevin Day
JNA since 3.2.7 has a large coverage of Win32 APIs and a set of Util classes that abstract that away. See com.sun.jna.platform.win32. If you're mapping functions that are not in JNA but are part of Win32, please contribute them to JNA.
dblock
+5  A: 

I had to do this a little while ago. You'll need a C compiler and the Windows header files. I used mingw because it's free and I was only compiling one little file.

First you make your class. Here is an example:

package org.whatever.thingy;

public class MyClass {
    // Here is a JNI method, identified by 'native'

    public static native callWin32Thingy(int x, int y, boolean z);

    /* At this point, normal class stuff, other methods, variables, whatever */
}

You then use one of the commands that comes in the JDK, which will automatically take your class and generate .h and .c files. The command is "javah". The method signature will look something like this:

JNIEXPORT void JNICALL Java_com_whatever_1thingy_MyClass_callWin32Thingy
    (JNIEnv *, jclass, jint, jint, jboolean);

In the .c file, you include whatever windows headers you need, and flesh out the method.

JNIEXPORT void JNICALL Java_com_whatever_1thingy_MyClass_callWin32Thingy
    (JNIEnv *a, jclass b, jint c, jint d, jboolean e) {
    // Prep steps....

    Win32MethodCallWeCareAbout(x, y, z, hWhatever);

    // Cleanup stuff...
}

It's very important you don't rename the method, that's how it gets associated with your specific class.

Once you've got that, you compile those files into a DLL. Here are the commands I used for mingw, you'll have to adjust classes/paths/etc.

c:/MinGW/bin/gcc -c -Ic:/MinGW/include -I"c:/Program Files/Java/jdk1.5.0_12/include"
    -I"c:/Program Files/Java/jdk1.5.0_12/include/win32" -D__int64="long long"
    com_whatever_thingy_MyClass_JNIHelper.c

c:/MinGW/bin/gcc -shared -o JNIHelper.dll
    com_whatever_thingy_MyClass_JNIHelper_JNIHelper.o
    -Wl,--add-stdcall-alias,--kill-at,--output-def,def_file

This will produce some files, including JNIHelper.dll, which is what I named my DLL.

At this point, you're basically done. You use your Java class as normal, and it will run your Win32 code when you call the static method. All you have to do is import the library. Somewhere in your code (I put it in a static block in my class) you'll need this line:

System.loadLibrary("JNIHelper");

This will cause Java to load the library named "JNIHelper.dll" and link it into the code. It has to be somewhere in the library path that Java knows about.

That's it. It's a bunch of boilerplate, but if you're doing some simple wrapping, it's easy. If you have to deal with Java types or allocating memory, it gets worse (note: I didn't, so I don't have experience there).

There's a whole tutorial here (first I found today that looked decent, and you can find others on the web. The Wikipedia article on JNI has more info too.

Hope this helps.

MBCook
Don't you need a "native" keyword in your Java class?
Thorbjørn Ravn Andersen
@Thorbjørn: Your right, I missed that, thanks. Edited to add.
MBCook