ITPub博客

首页 > Linux操作系统 > Linux操作系统 > [转载]弥补和 COM 的缝隙

[转载]弥补和 COM 的缝隙

原创 Linux操作系统 作者:dinner1007 时间:2019-06-17 22:15:05 0 删除 编辑
弥补和 COM 的缝隙
Java 平台的有力特征之一就是它面向对象的本质;面向对象语言的好处之一就是它们有助于代码重用。但如果您是一个 Java 程序员想重用不是用 Java 语言写的代码,那怎么办?有了 Bridge2Java ― IBM 的一种 alphaWorks 技术,Java 开发者可以将 COM 对象集成到他们的应用程序。William Phillips 解释它如何做到这一点。请到 讨论论坛和其他读者和作者分享您关于这篇文章的想法。

实际上,任何编写计算机程序的人都熟悉对象概念。用对象编程的一个主要的好处是一个特别有用的对象,如一个字符格式化程序,能被一个应用程序的很多不同部分利用。如果您把这种哲学用到逻辑结论,就没有理由问为什么不能把应用程序本身看作是对象或被其它应用程序作为对象来使用。这就是设计“组件对象模型”(Component Object Model)或者说 COM 来帮助创建的那种环境。

软件开发者可以使用 COM 的框架来快速、简单地把独立的应用程序改造成大应用程序的工作部分。但一些高级语言,象 Visual Basic 和 Java 语言,不能与 COM 简单建立接口。一个称为 自动控制器的层必需介入 COM 对象和用这些语言写的对象之间。鉴于越来越多的大的应用程序是用 Java 语言写的,需要一种便于集成的工具。Microsoft 所采用的 Java 平台 ― Visual J++,有内置的自动控制器,但是标准 Java 用户没有这个好处。

Bridge2Java 通过在 Java 程序中允许 COM 对象象其它 Java 对象一样使用来解决这个 Windows 问题。在这篇文章里,我将给您一个 Bridge2Java 的高级概述;我同时也提供一个展示如何使用它的示例。如果您愿意了解更多关于 COM 的知识,请查看下面的 参考资料部分。

忧愁河上的金桥

一个支持 COM 的程序(也称为 COM 服务器)允许其它程序(也称为 COM 客户机)访问自己的方法和属性的过程称为 自动化。每一个支持自动化的 COM 服务器必须公开一个称为 IDispatch 的特殊接口。通过使用 IDispatch 接口和 Invoke() 方法,一个 COM 客户机可以调用 COM 服务器想公开的任何函数。Bridge2Java 通过用 Invoke() 方法来允许 Java 程序和 COM 服务器通信;这个方法也把值返回给 Java 程序。从 Java 程序员的角度来说,Bridge2Java 允许 COM 对象就象其它 Java 对象一样被 Java 代码"看到"。可以从对象调用函数;数据类型全是 Java 的数据类型;甚至连 COM 事件也被捕获并发送到 Java 程序。

什么是 JNI?

Java 语言的设计者认识到创建 100% 的纯 Java 应用程序是个宏伟的目标。他们也知道事实上,这个目标通常很难达到。随着开发新的 Java 应用程序,开发者经常不情愿丢弃以前写的可能很昂贵或者很难的原码。 Java 的本机接口或者说 JNI,提供了一个由 Java 虚拟机(JVM)出来并进入 JVM 在运行的本机计算机世界的通道(doorway)。通过使用这个通道,Java 程序员可以使用先前昂贵的或者不可能的原码来重写成 Java 代码。JNI 允许本机代码调用 JVM,创建并访问对象,调用对象方法并访问实例变量。这种折中为开发者丢弃了 Java 平台的“写一次,在任何地方运行”这个好处,因为调用的任何本机代码将会被捆绑到特殊的平台。了解更多关于 JNI 的知识,请查看 参考资料获得后续信息。




回页首


JNI:基本原理

Java 世界和 COM 世界的边界被用“Java 本机接口”(Java Native Interface(JNI))桥接起来了。只有两个 JNI 调用是必需的:一个是创建 COM 服务器,另一个是处理 Invoke() 方法。只要发生了一次 Java 函数调用就调用 Invoke() ;它事实上是整个“Java 到 COM”过程的要核。然而,如果连接 Java 和 COM 对象和通过 Invoke() 传送一个从 Java 代码到 COM 的调用一样简单,那么 Bridge2Java 将会是一个非常简单的程序。Bridge2Java 的实际工作是在把 Java 变量转变成 COM 能理解的变量类型来完成的。变量称为 VARIANT 并且是包含所有可能类型和指出存储的数据实际类型值的结构。大多数 Java 变量巧妙映射到 VARIANT ― 例如,一个 int 很容易地映射到 VARIANT 中的 VT_I4 类型。实际的困难是在于将象 SAFEARRAYBYREF 值这样的 COM 类型映射到 Java 的等价类型。

转换变量的过程由一个 Java 代理调用开始,该调用创建并批量装入一个自定义建立的 Java 版的 VARIANT ,称为 Jvariant 。这个 Jvariant 能包含所有的 Java 变量类型,再加上一个域,该域指出包含的数据应该被转换成 COM 端的哪种类型。Bridge2Java 中的 SAFEARRAYS 被以 Jvariant 数组形式从 Java 代码传入。Bridge2Java 然后在 COM 端构建适当类型的数组并用输入数组的值批量装入它。如果此数组(或任何其它值)是 BYTEREF ,那么 Bridge2Java 必须能用更新的值替代传入的值。它通过使用数个来自 COM 端的允许更新 Java 数组元素的 JNI 调用之一做到这一点。

图 1 展示 Bridge2Java 如何把 Java 命令传送到 COM 服务器的纵览。


图 1. Bridge2Java 纵览
Bridge2Java 纵览



回页首


一个样本应用程序:建桥

使用 Bridge2Java 的第一步是把 COM 服务器的接口、方法和属性转换成 Java 代理对象和方法。这是由代理生成器(包括 Bridge2Java)完成的,并且要求用户为 COM 服务器定位 typelib。( typelib 仅仅是 COM 服务器程序员提供的便利;它为 COM 服务器提供了所有接口、方法和属性的有序清单。)Bridge2Java 代理生成器遍历 typelib,并为它所发现的每一个 IDispatch 接口生成 Java 对象。这样创建的每一个 Java 对象包含来自自 IDispatch 接口的所有方法和属性。属性将被生成为 get_set_ 方法,例如 get_Visible()set_Visible(BOOL visible)

我们的样本应用程序将展示 Java 开发者如何能使用 Bridge2Java 和 COM 调用 Lotus 1-2-3 功能。清单 1 展示了来自代理生成器的片段。这个示例类是用于我们应用程序指向 Lotus 1-2-3 的 IDispatch 接口。(1-2-3 的 typelib 称为 L123TYP.TLB )。

这个示例中有几个重要的事情值得注意。一个是列在文件上部的 cslid ;这是 IDispatch 接口的唯一标识并是 COM 访问注册表找到 Lotus 1-2-3 位置时使用的值。当 Bridge2Java 启动 1-2-3 时,COM 传回一个指向 IDispatch 接口的指针,它将被随后的 Invoke() 调用所利用来和 COM 服务器通信。

“Java 到 COM”过程的另一个重要部分是创建并初始化包含传入值和类型的 Jvariant 类。 invoke_method() 调用通常创建几个 Jvariant ,用于每一个传入的参数。在只返回一个值的情况下, Jvariant 就被隐式地创建并且只传回返回值。

Bridge2Java 也使用所有存在于 invoke_method() 调用的十六进制值。这些值称为 dispids 并且它们唯一标识 _Application 接口中的每一个方法。 Invoke() 使用这些值,连同上面提到的 IDispatch 接口指针和任何必需的 VARIANT ,来唯一定位和调用由这个接口提供的所有方法。


列表 1. Application.java
// Dispatch Proxies 
package Lot123;
import com.ibm.bridge2java.*;
import java.util.Date;
public class Application extends Dispatch implements COMconstants
{
   public static final String clsid = "{29130071-2EED-1069-BF5D-00DD011186B7}";
   public Application() 
   {
      super(clsid);
   }
   public Application(String clsidin) 
   {
      super(clsidin);
   }
   public Application(int IDispatch) 
   {
      super(IDispatch);
   }
   public Application(Object theObject) 
   {
      super(theObject);
   }
   public Application(int canvasHWND, int nullval) 
   {
      super(clsid, canvasHWND);
   }
   public Application(String clsidin, int canvasHWND, int nullval) 
   {
      super(clsidin, canvasHWND);
   }
   public String ExtendedName(Object nametype)
   {
      Jvariant args[] = {new Jvariant(nametype,VT_VARIANT)};
      return invoke_method(args, 0x1000300, DISPATCH_METHOD).StringVal();
   }
   public void Goto()
   {
      invoke_method_void(null, 0x1000304, DISPATCH_METHOD);
   }
   .
   .
   .
   public Object get_Run()
   {
      return invoke_method(null, 0x100ea00, DISPATCH_PROPERTYGET).ObjectVal();
   }
   public void set_Run(Object value)
   {
      Jvariant args[] = {new Jvariant(value, VT_VARIANT)};
      invoke_method_void(args, 0x100ea00, DISPATCH_PROPERTYPUT);
   }
   public String get_ProductVersion()
   {
      return invoke_method(null, 0x14c0211, DISPATCH_PROPERTYGET).StringVal();
   }
   public void set_ProductVersion(String value)
   {
      Jvariant args[] = {new Jvariant(value, VT_BSTR)};
      invoke_method_void(args, 0x14c0211, DISPATCH_PROPERTYPUT);
   }
} 

生成代理之后,现在用户可以写 Java 应用程序来和 COM 服务器会话了。清单 2 展示了一个小的程序,它将启动 1-2-3,把一行写到一个单元格,复制那一行,等待一秒钟,然后不保存地关闭 1-2-3。这是个简单的示例,但是它说明了构建一个更复杂的程序所必需的每一件事。很多示例包括在 Bridge2Java 包中,和其它的一起,该包展示了如何捕获事件并在 Java 画布中描绘 COM 服务器。


列表 2. Quick123.java
// Description: This is a Bridge2Java Lotus 1-2-3 demo.  This demo brings up 
//              1-2-3, writes "This is a test" in the first cell, 
//              copies the first cell to the cell below it, waits 1 second,
//              then quits.
//
// Setup:  None
//
// Author:  Bill Phillips
//
import Lot123.*;
public class Quick123
{
    public static void main(java.lang.String[] args) {
        Application app;
        Ranges ranges;
        Document doc;
        Range rangeA1, rangeA2;
        try
        {
            // Set up the com environment.
            com.ibm.bridge2java.OleEnvironment.Initialize();
            // Create a new document (which starts 123).
            doc = new Document();
            // Get the application object.
            app = new Application(doc.get_Parent());
            // Make the application visible.
            app.set_Visible(new Boolean("true"));
            // Open the Range workbook.
            doc = new Document(app.NewDocument());
            // Get all of the ranges.
            ranges = new Ranges(doc.get_Ranges());
            // Get cell A1.
            rangeA1 = new Range(ranges.Item(new String("A1")) );
            // Set the contents of cell A1
            rangeA1.set_Contents("This is a test");
            // Get cell A2.
            rangeA2 = new Range(ranges.Item(new String("A2")) );
            // Copy the contents of cell A1 into cell A2
            rangeA1.QuickCopy(rangeA2);
            // Wait one second
            Thread.sleep(1000);
            // Quit 123 without saving
            app.Quit("false");
        } catch (com.ibm.bridge2java.ComException e)
        {
            System.out.println( "COM Exception:" );
            System.out.println( Long.toHexString((e.getHResult())) );
            System.out.println( e.getMessage() );
        } catch (Exception e)
        {
            System.out.println("message: " + e.getMessage());
        } finally
        {
            app = null;
            com.ibm.bridge2java.OleEnvironment.UnInitialize();
        }
    }
}




回页首


总结

COM 提供了一种体系结构,该体系结构允许在真实组件级别使用程序而不用关心它们的版本、写程序所用的语言甚至它们运行的位置。Java 平台提供了一个易于临时用户学习和使用的语言。Bridge2Java 跨越了复杂仍强大的 COM 世界和 Java 语言的简单性之间的鸿沟。

Bridge2Java 当前是 IBM alphaWorks 技术,这意味着可以下载并评估它,与开发小组分享您的想法。您也可以获取许可把它用到您自己的产品中。单击下面 参考资料中关于 alphaWorks 的 Bridge2Java 页的链接。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-130323/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-08-23

  • 博文量
    1714
  • 访问量
    1290399