项目上有调用Visual Basic 6.0编写的DLL文件的需求,在使用JNA调用无果后,在网上找了部分资料,整理如下。
为什么用JNA无法调用?
VB生成的COM组件,虽然是dll后缀,但事实上,它应该叫做activex dll,是一种com组件,与标准dll是两个概念。vb生成的dll,虽然也是dll文件,但是它在生成的时候,屏蔽了输出函数,也就是说,你通过dll查看工具查看dll文件可以调用的方法时,是找不到这些函数的入口的,而标准dll则可以通过这种方式查找到函数入口。(如何让vb生成标准dll文件,实际上已经有成熟方法,只是比较麻烦,具体可以参考http://pydoncy.blog.163.com/blog/static/14380839020111149717308/)
而Java调用dll,根据这种区分,也要使用不同的工具。首先,无论是com组件还是dll文件,它们都是windows下的一种接口规范,对于Java来说,不像C#那样天然可以使用。
Java本身的机制是通过虚拟机运行程序的,这种方式,对于实体机器的利用效率不高。为此,Java的开发工程师发明了Jni。Jni本身是为了实现Java程序与实体机器的链接,通过调用一些本地已编译的其他语言的代码,尤其是c和c++,从而实现提高效率的目的。(也使得代码重用率提高了)
因此,想调用vb的com组件,就必须使用jni。但是jni的调用方法太过于繁琐,而且它首先设计于对c和c++代码的调用,需要对调用的函数编写.h的头文件。因此,有其他的一些开源工具,在基于jni的基础上,简化了开发人员的负担,使得开发人员可以通过简单的代码调用,实现目的。
一开始,我们尝试了jawin这个开源工具。jawin对于如何调用dll这种动态链接库,在jni的基础上实现了简单调用的目的。然而,我们一开始就遇到了找不到函数的问题,也就是前文所说的,vb生成的dll文件是屏蔽了函数入口的。当我们发现这种情况后,我们尝试了让vb生成标准dll,然后还是没有达到目的。
之后,通过对vb的进一步了解,我们终于了解到com组件与dll(动态链接库)的区别,然后找到了另一个工具-JACOB。JACOB就是 JAVA-COM Bridge的缩写,提供自动化的访问com的功能,也是通过JNI功能访问windows平台下的com组件或者win32系统库的。jacob对于com组件的访问调用,在方式上已经大大简化,能够像C#一样通过简单的代码调用。
看到此文章后,在网上搜了下jacob的用法。并且动起手来。
版本选择
jacob-1.18.jar 对应 JDK1.7*32
jar包和环境配置
jacob.jar 放到项目的lib目录
(方案1)
win7
jacob-1.17-x64.dll 放到C:\Windows\System32
jacob-1.17-x86.dll 放到C:\Windows\SysWOW64
(方案2)
把jacob-1.17-x86.dll 放到jdk/jre/bin下,目前选择的这种
判断环境是否配置成功
先使用以下测试代码,判断是否是自己的dll出现问题
public static void main(String[] args) {
ActiveXComponent xl = new ActiveXComponent("Excel.Application");
Dispatch xlo = (Dispatch)(xl.getObject());
try {
System.out.println("version="+xl.getProperty("Version"));
System.out.println("version="+Dispatch.get(xlo, "Version"));
} catch (Exception e) {
e.printStackTrace();
} finally {
xl.invoke("Quit", new Variant[] {});
}
}
未出现问题,下一步编写测试代码
public static void main(String[] args) {
//初始化
ComThread.InitSTA();
//调用Test.Class1
ActiveXComponent axc = new ActiveXComponent("Test.Class1");
//axc.invoke("hello","dll");
//get MS level dispatch object
Dispatch myCom = (Dispatch) axc.getObject();
//调用runme ,返回 hello,world
Variant result = Dispatch.call(myCom, "runme","world");
System.out.println(result.getString());
}
控制台打印hello,world。是不是过程很顺利,哈哈哈要是这么顺利,我会写这个嘛~~~
问题主要出现在new ActiveXComponent对象时参数的传递。在网上找了一些资料,用以下方法解决问题。
报错问题:
1.使用jacob时总是报错“com.jacob.com.ComFailException: Can't get object clsid from progid”
解决方案:
出现这个错误的原因,是因为没有把DLL文件注册到系统注册表。
注册方法: regsvr32 dll文件(绝对路径)
对于dll文件,可以通过这个工具打开查看:eXeScope 下载地址:http://pan.baidu.com/s/1bpsPzWz
打开查看dll文件,关注下CLSID值。然后在windows的注册表中搜这个CLSID。
其实我用eXeScope打开这个dll时并没有看到CLSID值,而是看到了GUID
百般无奈下在注册表中搜索到了这个,ProgID的值就是需要传递的参数
最后需要注意,在VB项目工程中,工程名不能为中文名。不知道是不是乱码原因,调用报错。
附:常用操作指令
注册时用 regsvr32 DLL组件名,
卸载时用 regsvr32 /u DLL组件名。
参考链接:
http://blog.sina.com.cn/s/blog_c0f210170101ctx2.html