通过 JACOB 实现 Java 与 COM 组件的互操作

2021-11-04  本文已影响0人  上善若泪

1 JACOB

1.1 概述

jacobjava com bridgejava com桥)分为两个部分,jacob.jar,jacob.dll,使用时两个东西的版本要一致,而且还分32位64位,它的位数和jdk的位数有关,与操作系统的位数无关。它的原理是通过javajni功能,调用系统组件dll,通过这个com桥来操作com组件(windows的一种软件编程技术)

如果可以在 Java 中调用 COM 组件,就可以充分利用 Java 技能和现有的成熟 COM 工具包,大大简化应用开发的过程。
COM 组件提供了一种与其他语言的互操作方式,叫做自动化(Automation)。
现有的 Java COM 互操作的解决方案有很多种,由于设计目的的不同,在性能、易用性等方面都有很大的区别。本文介绍的 JACOB 开源项目,致力于简化 Java 操作 COM 组件,提供了一个虚拟机独立的自动化服务器实现,由于其通用性设计,您可以非常简单地完成对现有应用的集成。

1.2 JACOB 项目的由来

首先,我们将了解 JACOB 项目的基本情况,探讨在什么样的情况下选择它来完成任务调用 COM 中暴露出来的方法,主要有两种机制:早期绑定晚期绑定

JACOB 开源项目提供的是一个 JVM独立的自动化服务器实现,其核心是基于 JNI 技术实现的 Variant, Dispatch 等接口,设计参考了 Microsoft VJ++内置的通用自动化服务器,但是 Microsoft 的实现仅仅支持自身的 JVM。通过 JACOB,可以方便地在 Java 语言中进行晚期绑定方式的调用。
下图是一个对 JACOB 结构的简单说明

在这里插入图片描述
可以综合考虑时间、技术难度、性能要求等各方面来选择适合您的解决方案,下面结合在实际的订单处理系统的部分功能的开发,展示使用 JACOB 技术进行开发的成本优势。

1.3 JACOB操作邮箱

1.3.1 使用背景

某开发团队要利用一个订单处理的工作流系统,对现有的业务方式进行集成、优化。原有的业务处理方式是基于邮件系统的半人工方式,业务人员将订单以邮件方式发送到特定邮箱,然后后续人员处理订单。
现在设计的集成方式是,处理引擎定期轮询订单邮箱,以特定命名规则区分订单,获取存储在邮件正文和命名属性中的订单信息,将订单放入处理队列进行进一步处理,最后将源邮件标记状态并移出订单目录。
开发工作中遇到的难点是,应用的核心部分是基于 Java 实现的工作流应用,而客户使用 Outlook/Exchange 邮件系统,开发团队并没有足够的人力和时间去为其开发纯 Java 实现的客户端,Java 程序也难以操作邮件命名属性的信息。
下面我们就尝试利用 JACOB 和一些成熟的 COM 组件来解决这个问题。

1.3.2 配置您的开发和运行环境

我们的例子是一个基于 EclipseJava 工程。首先,使用 JACOB搭桥,下载最新的 JACOB 相关类库,包括Jacob.jarJacob.dll,将jar 文件添加到Eclipse工程的构建路径。

在这里插入图片描述
由于我们要通过 JNI调用 DLL 文件,所以您需要把 DLL 文件添加到系统的 PATH 环境变量中,或者您也可以在程序中设置运行时的 PATH 变量。
在这里插入图片描述
一个简单的已经搭起来了,就可以轻松的调用熟悉的 COM 类库了,我们这里以 Redemption 工具包为例来完成 Outlook 相关操作,它提供了对 Outlook 对象的比较完整的操作,可以方便的绕过 Outlook 对象模型所隐藏的一些细节部分。同时,Redemption 提供了对任何 IDispatch 友好的语言的支持。
需要在操作系统中注册 COM 组件,可以使用 Redemption 工具包本身提供的安装程序,也可以采用 Windows 系统自带的 REGSVR32 工具来注册 DLL 文件

1.3.3 实现业务逻辑

完成上面的准备工作后,就开始实现具体的业务逻辑了。
首先,要获取邮件您需要登录到指定邮箱,Outlook profile 中存储了您的邮箱的基本信息,下面的例子是如何登录特定 profile:

public void logon(String profile, String password) { 
    // 登录到指定 Profile,并获取一个会话
    ActiveXComponent session = new ActiveXComponent("Redemption.RDOSession"); 
    Dispatch.call(session, "Logon", profile, password, false, true); 
}

现在已经能够访问用户邮箱,我们要做的是获取收件箱中的标题以ORDER NO:开始的邮件,把信息存储到订单管理系统之中

Dispatch items = Dispatch.call(folder, "Items").toDispatch(); 
int count = Dispatch.call(items, "Count").getInt(); 
// 轮询所有邮件,并检查符合标准的邮件
for (int x = 1; x <= count; x++) { 
    Dispatch item = Dispatch.call(items, "_Item", new Integer(x)).toDispatch(); 
    ActiveXComponent sMail = new ActiveXComponent("Redemption.SafeMailItem"); 
    sMail.setProperty("Item", item); 
    Variant vSubj = Dispatch.get(sMail, "Subject"); 
    String subj = vSubj.getString(); 
    if (subj.startWith(“ORDER NO”)){ 
        // 将获取的定单信息存储到订单处理队列之中
        OrderMail mail = new OrderMail(item); 
        mail.persistToOrderQueue(); 
    } 
}

获取订单之后,将订单标记为已处理,这里我们通过操作命名属性的方式来标记状态,然后移动邮件到放置已处理邮件的目录:

// 获取命名属性的 ID 
ActiveXComponent utils = new ActiveXComponent("Redemption.MAPIUtils"); 
Variant namedPropID = Dispatch.call(utils, "GetIDsFromNames", orderMail, 
    GUID_OF_USERPROP, ORDER_STATE_DONE, true ); 
// 将邮件标记处理状态
ActiveXComponent utils = new ActiveXComponent("Redemption.MAPIUtils"); 
Dispatch.call(utils, "HrSetOneProp", orderMail, namedPropID, "Done", true ); 
// 将处理过的邮件移出现在的目录
Dispatch.call(orderMail, "Move",  folderForProcessedMsg);

可以看到,无需关注太多类库的细节,只需要知道要使用的对象名称、方法和输入变量,就可以轻松的调用 COM 组件。JACOB 的准备工作非常简单,并且只需要非常有限的编码,就完成了现有业务系统和新应用的无缝集成。

1.4 JACOB操作邮箱

现在的项目中操作word文件比较多,word文件的加密解密,转换为各种格式,插入图片,添加水印、htmlword等等各种东西,大家也都知道,java语言是不能直接操作word或者excel的,不像C#,可以调用VBA的类来直接操作office

目前java也有一些操作office的开源框架,比如poi,这个没怎么研究过,估摸着它是通过解析officexml文档模型来操作office的,因为office03office07以及以上的文档模型结构不太一样,导致了poi处理不同版本office时要用不同的类,用的最多的是jacob,它对office03office07的操作基本一致,高版本的还没有试过

1.4.1 环境配置:

1、在工程中引入jar
2、将dll文件放在jdkpath目录下面,通过System.getProperty("java.library.path");可以看到path路径,或者直接放到jdk/bin、jre/bin、system32/system64下面都放一份,总能找得到,版本要急着对应好。
3、附件为使用的组件,配合jdk使用。

1.4.2 常用类以及方法

常用类以及方法:

Dispatch的几种静态方法:(这些方法就是要用来操作office的)

以上方法中有的有很多重载方法,调用不同的方法时需要放置不同的参数,至于哪些参数代表什么意思,具体放什么值,就需要参考vba代码了,仅靠jacob是无法进行变成的。

Variant对象的toDispatch()方法:将以上方法返回的Variant类型转换为Dispatch,进行下一次链式操作。

1.4.3 初始化com线程

使用jacob之前使用下面的语句来初始化com线程,大概意思是打开冰箱门,准备放大象。。。

ComThread.InitSTA();  

使用完成后使用下面的语句来关闭现场,大概意思是关上冰箱门。。。

ComThread.Release();  

1.4.4 创建应用程序对象

创建应用程序对象,设置参数,得到文档集合
操作一个文档之前,我们必须要创建一个应用对应,比如是word还是excel,设置一些文档应用的参数,得到文档集合对象,(大家应该知道wordDocumentsexcelWorkBooks

ActiveXComponent wordApp = new ActiveXComponent("Word.Application");//word  
ActiveXComponent wordApp = new ActiveXComponent("Excel.Application");//excel  
wordApp.setProperty("Visible", new Variant(false));//设置应用操作是文档不在明面上显示,只在后台静默处理。  

这个操作就是模仿vba的写法,Application.Visible = False;vba代码中的对application的设置这里都可以用

Dispatch document = wordApp.getProperty("Documents").toDispatch();  

1.4.5 打开文档

有了文档对象集合,我们就可以来操作文档了,链式操作就此开始:
call方法,调用open方法,传递一个参数,返回一个我们的word文档对象,

Dispatch doc = Dispatch.call(document, "Open",new Variant("D:\\my.doc")).toDispatch();  

1.4.6 保存文档

Dispatch.call(doc, "Save");  

doc是我们打开的文档的对象

1.4.7 退出wordapplication

参数有很多个,我们一个都不传,执行完后winword进程关闭

wordApp.invoke("Quit", new Variant[] {});  

1.4.8 释放com线程

ComThread.Release();  
上一篇下一篇

猜你喜欢

热点阅读