从Java调用Win32 API方法

37
我需要从Java程序中调用Wininet.dll中的一些方法
我可以找到很多关于如何从Java调用我创建的新DLL的教程,但似乎找不到任何有关如何从Java调用已经存在的DLL的教程。
我知道这涉及JNI,但是我应该如何做?我需要在Wininet.h上调用javah吗?我在哪里获取Wininet.h的副本?指向现有详细教程的指针就足够了。
3个回答

47

2
这需要一个提升 - 对于本地库访问,JNA比纯JNI要好得多。 - Chris K
+1:JNA是一个不错的选择。不过,你需要弄清楚如何在Java中映射函数签名。最近我使用了JNA来通过本机Win32 API与蓝牙设备进行通信。 - wierob
JNA非常流畅。上周我在工作时向一个朋友解释它时,我甚至告诉他这是“魔法”。 - rob
5
这种偶发的本地调用,JNA是完美的选择。但是如果你正在使用WinInet进行低级套接字操作,想要高性能的话,最好使用真正的JNI实现。 - Kevin Day
自从3.2.7版本以来,JNA已经广泛覆盖了Win32 API,并提供了一组抽象类的Util类。请参阅com.sun.jna.platform.win32。如果您正在映射不在JNA中但是属于Win32的函数,请将它们贡献给JNA。 - dB.
如果我们想通过Java获取Windows操作系统下的PrinterJob怎么办?这可能吗?我需要打印的“副本数量”信息,但最终没有得到任何信息... - gumuruh

14

我之前也需要做这个。你需要一个C编译器和Windows头文件。我使用了mingw,因为它是免费的,而且我只编译了一个小文件。

首先,你需要创建你的类。这里有一个例子:

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 */
}

然后,您将使用JDK中提供的一个命令,它将自动获取您的类并生成.h和.c文件。该命令是“javah”。方法签名将类似于:

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

在 .c 文件中,您需要包含所需的 Windows 标头文件,并完善该方法。
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...
}

我很重要的是不要重命名方法,这样它就会与您特定的类关联起来。

一旦您掌握了这个,您可以将这些文件编译成DLL。以下是我在mingw中使用的命令,您需要调整类/路径等。

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

这将生成一些文件,包括 JNIHelper.dll,这是我为我的 DLL 命名的文件。
此时,你基本上已经完成了。你可以像平常一样使用你的 Java 类,并在调用静态方法时运行 Win32 代码。你只需要导入库。在你的代码中的某个地方(我把它放在我的类的静态块中),你需要这行代码:
System.loadLibrary("JNIHelper");

这将导致Java加载名为“JNIHelper.dll”的库并将其链接到代码中。它必须在Java已知的库路径中的某个地方。

就是这样。这是一堆样板文件,但如果您正在进行一些简单的包装,那么很容易。如果您必须处理Java类型或分配内存,则情况变得更糟(注意:我没有经验)。

这里有整个教程(今天我找到的第一个看起来不错的教程,您也可以在网络上找到其他教程。JNI的Wikipedia条目还提供了更多信息。

希望这有所帮助。


1
你的Java类中不需要使用“native”关键字吗? - Thorbjørn Ravn Andersen
@Thorbjørn:你说得对,我漏掉了,谢谢。已编辑添加。 - MBCook

2

不能直接调用本地库:这是因为某些方法中的一些参数不受Java Native Interface支持。

有一个名为GlueGen的东西,它会创建一个单独的二进制文件,该文件将动态链接到您的本地DLL。生成的二进制文件与JNI兼容,因此可以从Java中调用。

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

获取wininet.dll的头文件,您可能需要安装平台SDK(或Windows SDK,最新版本)。您还可以在网络上搜索包含此头文件的在线存储库。
GlueGen将需要一个头文件、一个ANSI C编译器等。
还有一个不太知名的库叫做NativeCall,它不需要GlueGen。它已经有了与Java兼容的二进制文件。这显然会更慢,因为它会在Java请求时动态加载DLL。我还没有使用过这个,但听起来很有前途:

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接