软件的互操作性是一个我们经常面临的问题,如果 Java 可以自由的调用其他语言和平台的成熟代码,可以充分利用您的 Java 技能,大大提高您的生产力。现有的 Java COM 互操作技术有很多种实现,JACOB 开源项目提供了一个简单方便的通用调用框架
COM 组件提供了一种与其他语言的互操作方式,叫做自动化(Automation)。现有的 Java COM 互操作的解决方案有很多种,由于设计目的的不同,在性能、易用性等方面都有很大的区别。本文介绍的 JACOB 开源项目,致力于简化 Java 操作 COM 组件,提供了一个虚拟机独立的自动化服务器实现,由于其通用性设计,您可以非常简单地完成对现有应用的集成。
首先,我们将了解 JACOB 项目的基本情况,探讨在什么样的情况下选择它来完成您的任务
有关自动化的更多细节,您可以参考相关文档和书籍,我们仅做简单介绍。调用 COM 中暴露出来的方法,主要有两种机制:早期绑定和晚期绑定。
早期绑定显式的声明对象、数据类型等,编译器获取了足够的信息在编译期进行链接和优化,这样通常可以获得更好的性能,通过这种机制实现 Bridge 调用可以参考 IBM 的 RJCB 项目,它提供了一套高性能的解决方案。当然您需要了解更多 COM 组件的细节,虽然框架为您完成了大部分的生成 Bridge 代码的工作,但是总体来说,编码工作量还是偏大,编码难度也比较高,而且 RJCB 仅支持那些提供早期绑定的 vtable 接口的 COM API。
而晚期绑定方式是通过 IDispatch接口来实现,类似 Java 的反射机制,您可以按照名称或者 ID 进行方法调用,这种设计主要目的是支持脚本语言操作 COM,因为脚本是解释执行的,通常都不支持指针也就没有 C++ 中的 vtable 机制,无法实现早期绑定。这种方式的缺点是在一定程度上性能较差,由于是在运行时按照名字或者 ID 进行对象的调用,只有运行时才确切知道调用的是哪个对象,哪个方法,这样必然带来一定的开销和延迟。但是这种方式的优点也是非常明显的,简单、灵活,您可以不必关注动态链接库的细节,可以非常快地完成代码开发工作。
JACOB 开源项目提供的是一个 JVM 独立的自动化服务器实现,其核心是基于 JNI 技术实现的 Variant, Dispatch 等接口,设计参考了 Microsoft VJ++ 内置的通用自动化服务器,但是 Microsoft 的实现仅仅支持自身的 JVM。通过 JACOB,您可以方便地在 Java 语言中进行晚期绑定方式的调用。
下图是一个对 JACOB 结构的简单说明
图 1. JACOB 基本结构
配置您的开发和运行环境
目前最新的jacob版本为1.18,主要有三个文件
jacob.jar
jacob-1.18-x64.dll
jacob-1.18-x86.dll
从这个版本开支持64位系统了,所以dll被分为了两个文件
1.将 jacob.jar 文件添加到您的工程的构建路径
2.将jacob-1.18-x64.dll放到64位的jdk或jre下的bin目录下
如果是32位系统就将jacob-1.18-x86.dll放到32位的jdk或jre下的bin目录下
3.将要操作的dll 文件放到相应的系统目录下
32位dll放到 C:\Windows\System32 目录下
64位dll放到 C:\Windows\SysWOW64 目录下
4.您需要在操作系统中注册 COM 组件,可以使用 Redemption 工具包本身提供的安装程序,也可以采用 Windows 系统自带的 REGSVR32 工具来注册 DLL 文件。运行cmd,注册dll
将目录切换到相应的系统目录下运行
regsvr32 xxx.dll
5.通过使用OLE/COM对象查看器查看dll信息
找到dll引用的信息
6.Java程序调用
package com.xfire;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
public class Main {
public static void main(String[] args) {
ActiveXComponent com = new ActiveXComponent("MathTest.Math");
Dispatch disp = (Dispatch) com.getObject();
Variant a = new Variant(2);
Variant b = new Variant(3);
int result = Dispatch.call(disp, "Add", a,b).getInt();
System.out.println(result);
System.exit(0);
}
}
注意32位的dll要用32位的jdk,64位的dll要用64位的jdk