# JaCOB与Variant与SafeArray与多维数组

- JACOB

事情的起因是这样的,项目需要从Java中调用DLL函数库,而函数库又是VB编写的,因此我们只能求助于第三方插件。然后就找到了JACOB - Java COM Bridge。调用过程:
1. new ActiveXComponent(“dll_name”)生成对象OBJ_ACTIVE
2. Dispatch.call(OBJ_ACTIVE, “function_name”, param_list[])调用指定方法

在调用过程中产生了一个问题,就是VB语言中的传引用调用。Java当中并没有传引用调用和传值调用的明显区分,只是对于基本数据类型,其指向一个固定对象,因此为传值;而对于其它数据类型,调用时会指向原对象,可以认为是传引用。
但是我们发现在进行call调用时,传入的参数内容并不会随之更改。对于VB源码中明确指明ByRef调用的参数,此时会出现错误。为了解决向dll中的方法进行传引用操作,我们需要对参数进行一层封装,即Variant。

- Variant

根据网络上的定义, Variant 数据类型是所有没被显式声明(例如 Dim、Private、Public 或 Static等语句)为其他类型变量的数据类型。Variant 数据类型并没有类型声明字符。
这种数据类型可以对我们要传递的参数进行封装,我暂且认为它其实是一个容器。一个Variant变量提供了对基础数据类型“传引用”的方法,包括

  • putBooleanRef
  • putByteRef
  • putBooleanRef
  • putCurrencyRef
  • putDateRef
  • putDecimalRef
  • putDispatchRef
  • putDoubleRef
  • putErrorRef
  • putFloatRef
  • putIntRef
  • putLongRef
  • putObjectRef
  • putSafeArrayRef
  • putShortRef
  • putStringRef
  • putVariantArrayRef

同时还提供对应的get*Ref()方法。因此我们只要将参数封装如一个Variant变量中,并将Variant变量作为参数传入DLL的方法,调用完成后,从Variant读取已经修改的数据即可。
通常来说,对于单个的参数就足够了,但是如果一个参数是数组的话,我们就要引入一个新的数据类型——SafeArray了。(不过方法列表里还有putVariantArrayRef,这个方法就没有进行测试了)

- SafeArray

Variant的封装函数里没有提供针对基础数据类型数组的方法,但是提供了putSafeArrayRef(SafeArray in),因此,当我们需要进行数组封装时,我们需要进行两层封装:将数组参数放入SafeArray中,将SafeArray封装至Variant中。
以浮点型数组为例。SafeArray提供了setDouble(int, double)和setDouble(int, int, double)的写入数据的形式,int参数代表坐标。本身SafeArray是支持二维数组定义的,因此可以直接与二维数组进行对应数据的传递。但是在定义的时候要注意,由于最终要封装在Variant变量中,SafeArray中的元素类型并不是基础数据类型,而是与Variant相关的类型。从变量定义可以看出:

new SafeArray( Variant.VariantDouble, 10, 10 );

这是一个10×10的浮点数二维SafeArray数组。
顺带一提,运算结束后SafeArray并不需要进行get操作从Variant中获取数据,其本身存储的已经是运算修改后的数据了。(但是如果直接把SafeArray当做参数传进去……结果是失败的……)

- 多维数组

二维数组的问题解决了,然而在项目里我又碰到了三维数组。这次翻阅了一下方法列表,找到了一个SafeArray.setDoubles(int, int, double[], int)。
看起来似乎是可以直接对二维数组中的某一元素赋值为double数组的样子,那么再不考虑更高维度的情况下,三维数组的问题就解决了。
当然,事实证明我还是太年轻。因为没有文档手册,所以不可避免的我的理解出现了偏差。
SafeArray.setDoubles(int arg0, int arg1, double[] arg2, int arg3)的意思完全不是我想的那样。这个方法的真正用法是,将一个数组写入SafeArray。而SafeArray在这里(我们可以认为)其实是以一维数组的形式表达的。由于多维数组依旧是连续的存储空间,我们直接访问一维下标index和三维下标i,j,k指向的对象是相同的(index = i * 二维维度 * 三维维度 + j * 三维维度 + k )。因此在这个方法中,arg0与arg1代表连续空间的起始和终止坐标,arg2为数据来源数组,arg3为数据来源数组的起始坐标。
我们应当认为这其实是两个一维数组之间的数值传递(其对应的getDoubles同理)。如果需要进行三维坐标转化,那就只能自己遍历了。我采用的方法是现将SafeArray写入一个一维数组tmpArr,然后根据坐标对应关系,将tmpArr回写到原来的三维数组中。

至此遇到的问题基本解决了。不过还遗留下两个小疑问。一个是对于更高维的数组是不是也要用这种思路解决,还是有更加方便的途径呢?另一个是,源码要怎么看啊,搜到一个SafeArray.java但是内容依旧看不懂……

你可能感兴趣的:(# JaCOB与Variant与SafeArray与多维数组)