Java调用动态库最简便方法和最好用的组件

记得以前 Java 要调用C/C++写得动态库都是用的 JNI 方式,还需要自己写不少 C/C++ 代码。比如说要在Java中调用已有的动态库,如 Windows 的 user32.dll 的方法 MessageBoxA,具体步骤是 Java 中声明一个 native 方法,然后用 javah 命令生成JNI样式的头文件,再自己实现头文件中声明的方法,在实现方法中装载动态库 user32.dll,调用 MessageBoxA 方法,需要把自己写的这部分 C/C++ 代码封装成一个动态库,如Sample.dll,最后在 java  中装载 Sample.dll,然后执行其中所声明的本地方法。

可见,用老实的JNI方式,我们在调用一个已知动态库的时候还需要额外生成一个符合JNI规则的动态库作为桥梁,显得有点多余了。

下面我将引入一个开源的组件 JNative,在 http://sourceforge.net/projects/jnative 下载(我是通过在sourceforge中输入java dll搜索到的),通过它调用已有动态库中的方法就非常的方便,因为中间的JNI处理过程它都为我们做好了。JNative 现在还是 Beta1 版,期待正式版的出炉,还不知道这个版本将会有什么Bug出现。

我们下载到的 jnative 的目录中可以看到三个文件,分别是 JNative.dll,libjnative.so,JNative.jar

JNative.dll  为 Windows 平台下用的,可以拷到相应的lib加载路径,如user.dir、path、system32或windows目录下
libnative.so 为Linux平台下用的,可以拷到相应的lib加载路径,如user.dir、path目录下
JNative.jar 这个就是我们编程时候要用的

在下载的 jnatidve 的源代码中有示例代码,观看代码 org\xvolks\test\JNativeTester.java 就知道 jnative 是如何调用动态库方法的

如代码行 User32.messageBox(0, "Demonstrates JNative in action with many Win32 calls", "Welcome to JNative", 0);

是调用的类 User32 的 messageBox 方法,而messageBox的代码是

上面的注释是我加上去的。

Type有一种叫做Type.PSTRUCT枚举值,对C/C++中结构的处理都考虑到了,只要用JNative的Pointer类的实例来与结构对应就行了。org\xvolks\test\SNDPTester.java中演示了如何传递C/C++中的结构相对应的Java数据类型,也就是怎么封装成一个JNative的Pointer类型。

还有在JNative中用了JDK1.5的一些特性,如枚举、静态引入等,所以在JDK1.4下是不能用的。

附:JNative 当前版本到了 1.3.2 了
版本在 Jan 14 2009 又升致 1.4-RC3 了,看来这东西还很活跃的啊。

本文链接 https://yanbin.blog/java-jnative-dll-concise/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

25 Comments
Inline Feedbacks
View all comments
trackback

[…] Java调用动态库最简便方法和最好用的组件 […]

啥事
啥事
16 years ago

有没有如何获得dll传出参数的例子

隔叶黄莺
16 years ago

没有这么聪明,C++也没办法知道,只能按照API约定来调用传参取值,知道一个地址不标识是什么类型的

隔叶黄莺
16 years ago

JNative 的 getRetVal() 方法就是获取dll中函数传出的参数。

啥事
啥事
16 years ago

可能是我没有表述清楚,我想知道jnative如何调用dll的出参。

getRetVal()只是调用是否成功的一个返回值。如:

我们有个HNBridge.dll中有getParam(Long pDataHandle,String paramName,REF String paramValue,UINT nMaxValuseLenth) 其中REF String paramValue

是一个返回值,getRetVal()只是校验该函数调用是否成功。

java如何调用REF String paramValue。

这是我的源码

public static final Object[] getparam(long s1,String s2,long s4 ) throws NativeException,IllegalAccessException{

JNative n=null;

try{

n= new JNative("HNBridge.dll", "GetParam");

n.setRetVal(Type.INT);

// n.setMemory(0, "");

//n.set

int i=0;

Pointer s6 = new Pointer(MemoryBlockFactory.createMemoryBlock(1024));

s6.zeroMemory();

s6.setStringAt(0, "ddd");

use=new UserCall("");

// s6.setStringAt(0, "sss");

//pTar=new Pointer(MemoryBlockFactory.createMemoryBlock(36));

n.setParameter(i++, Type.LONG, ""+s1);

n.setParameter(i++, Type.STRING,s2);

// n.setParameter(i++, use.createPointer());

n.setParameter(i++, s6);

n.setParameter(i++, Type.LONG, ""+s4);

n.invoke(); // 调用方法

//System.out.println(n.getParameter(0));

// s3=s6.getAsString();

System.out.println(s6.getAsString()+"fhz");

Object [] mm=new Object[2];

mm[0]=n.getRetVal();

// mm[1]=use.getValueFromPointer();

mm[1]=s6.getAsString();

// mm[1]=s3;

//use.dispose();

return mm;

}

但总是得不到正确值

隔叶黄莺
16 years ago

getRetVal()是实际的返回值

hill
hill
16 years ago

为什么我用自己写的程序老是收到这样的错误
Exception in thread "main" java.lang.IllegalStateException: JNative library not loaded, sorry !
at org.xvolks.jnative.JNative.<init>(JNative.java:337)
at org.xvolks.jnative.JNative.<init>(JNative.java:269)
at test.TestJNI.main(TestJNI.java:30)
即使是用JNativeTester.java也会收到错误:
Exception in thread "main" java.lang.UnsatisfiedLinkError: org.xvolks.jnative.JNative.nLoadLibrary(Ljava/lang/String;Ljava/lang/String;Z)I
at org.xvolks.jnative.JNative.nLoadLibrary(Native Method)
at org.xvolks.jnative.JNative.<init>(JNative.java:348)
at org.xvolks.jnative.JNative.<init>(JNative.java:269)
at org.xvolks.jnative.util.User32.MessageBox(User32.java:396)
at test.JNativeTester.main(JNativeTester.java:312)

hill
hill
16 years ago

用JNativeTester.java去load “user32.dll"会出现上面的错误,但是load自己的dll却是Exception in thread "main" java.lang.UnsatisfiedLinkError: D:\Project\test\src\test\umenable.dll: Can't find dependent libraries
?难道我的dll是不能用的?

隔叶黄莺
16 years ago

把JNative.dll 为 Windows 平台下用的,可以拷到相应的lib加载路径,如user.dir、path、system32或windows目录下,保证你的java程序能加载到这个动态库

hill
hill
16 years ago

谢谢你的解答,现在可以load我的dll了不过问题又来了,我发现看不到dll里面的方法,即使用JNative的getDLLFileExports返回的都是(几个dll都是返回这样的)
DllCanUnloadNow
DllGetClassObject
DllRegisterServer
DllUnregisterServer
我的这些dll好像是基于com的,不多我对这些都不熟悉,咋办?

隔叶黄莺
16 years ago

基于Com的,有些不一样的。Com初始化过程在哪里完成的呢

hill
hill
16 years ago

谢谢你的解答,我发现我这个程序用jacob(Java-Com Bridge)来做就可以了,非常简单,只要new一个ActiveXComponent,就可以调用里面的函数了

cary
cary
15 years ago

我用JNative来调用DLL但是现在遇到问题是类型间的转换,DLL里的类型始终不能被JAVA认出来。那怕我在DLL里这样写:return "hello",java里:

n3.setRetVal(Type.STRING);

System.out.println(n3.getRetVal())

都不能被打印出来,不知道你有遇到过类型方面的问题吗?

隔叶黄莺
15 years ago

没遇到这种问题,请把你能重现问题的完整代码贴出来看看

cary
cary
15 years ago

n3=new JNative("CID_test","CID_GetNum");

n3.setRetVal(Type.STRING);

System.out.println("n3=="+n3.getRetVal());

这是JAVA代码。

c++

extern "C" _declspec(dllexport) string CID_GetNum()

{

char buf[32];

string s;

int i=0;

i=SW_ReceiveCID(0,buf);

if(i!=0)

{

s += buf;

return "hello";

}

return "hello";

}

你看看我这样写有问题吗?

@隔叶黄莺

隔叶黄莺
15 years ago

尽量能的话还是严谨一点吧

_declspec(dllexport) 前面是两个下划线,你少写了一个

你用 string 也该把 string.h 或 string #include 进来

SW_ReceiveCID 不存在的函数

存在两个问题

1. 你的动态库必须返回一个 c-style 字符串,如 char*,用 std 的 string,java 那边不会认成是 String,你可以中间过程用 string,最后返回的字符串要是 c-style 格式。这应该不成问题,你见过许多 windows api 就是用的这种字符串,如 LPSTR 样的

2. 你在 java 代码忘了调用 dll 的相应方法,在取返回值之前必须 n3.invoke() 一下。

重新试试吧,祝你好运。

cary
cary
15 years ago

多谢您的解答。

我做了相应的改动,现在我存在的问题是dll返回的字符串格式实际是一个地址,Java没法认出来。

return “Hello”;这样写的话,Java直接认出来;

char *buf="Hello";

return buf;这样写,Java无法认出来,报错。

印象中C++中传字符串时都是传首地址的,Java有办法通过处理这个地址拿到字符串么?

wen
wen
15 years ago

现在那个网站还能下吗,好象打不开下载页面了.如果你有希望能发我一份,现在急用这东西.

隔叶黄莺
15 years ago

在QQ群2:64701647的共享里我放了一个最新版的 JNative。发展民族软件业,咱不指望 SourceForge,更不指望 GWF 那个机器了。

wen
wen
15 years ago

支持,民族软件还需要更多的开源与交流.实践与理论都需要开源

隔叶黄莺
15 years ago

政治异见思想是不需要碰撞的,不然擦迸出的火花必将燃灭了自己。

dengyigang@126.com
dengyigang@126.com
15 years ago

@hill
进入dos界面,或者“运行”--“CMD”
运行“set path=D:\Project\test\src\test\;%path%”
或者,写个批处理文件.bat,程序运行前,运行XXX.bat处理文件。
加入这个动态库 载入的 路径。

new JNative("动态库名称.dll", "动态库中的需要调用的方法名称");

kjniuzemin2003@163.com
kjniuzemin2003@163.com
15 years ago

你好。我想问一下,我怎么用JNative取得.dll中的属性值呀?另外,我的.dll中的方法是属于某某对象的,那我调用该方法的时候,也是像前面所说的方法那样调用吗?

hyint
hyint
15 years ago

希望楼主能贴出一些比较经典的例子,譬如说:传回调函数指针的。请问下在Java中如何定义呢?用JNative的Callback,好像不如人意啊

隔叶黄莺
15 years ago

你要参考一下这个:使用JNative,在Java中传递一个C/C++结构参数到动态库中(http://www.blogjava.net/Unmi/archive/2006/05/18/124089.html),处理参数或返回值时要了解你的C/C++中变量的内存布局。