高效阉割良田高拍仪JavaSDK所依赖的Eclipse SWT OLE Win32自动化组件

简介: 良田高拍仪的java依赖虽然只有一个swt win32的jar包,但这一个依赖就挺折磨人的,包体积不小,调用起来还很不方便,真正用到的地方其实也只有一点点,本文将把良田高拍仪所使用到的ole自动化组件完全抽离出swt win32这个包,这样不需要任何依赖就能调用高拍仪设备了

良田高拍仪的SDK如官方所说,eloam windows sdk 其实是通用的,只与平台有关,不是针对语言的,语言不同只是调用方式不同罢了,标题所说的JavaSDK的其实也就是官方的一个小例子EloamViewJavaDemo,也就是用Eclipse SWTOLE组件去调用驱动的API,如果你不清楚EloamViewJavaDemo,可以看一下前文在Windows系统对接良田高拍仪驱动SDK,不过如果你手上没有那个demo,也没有关系,通过本文的学习你就不需要官方的demo(官方的SDK包也不提供这个demo了)

起步

无论你有没有EloamViewJavaDemo这个demo,都应该新建一个项目进行开发测试,不要修改原本的demo,新建了我们当然就可以使用现代化的工具了,起步自然是用gradle配置,但实际也没什么可配置,因为我们除了一个swt-win32-4430.dll之外将不需要任何的依赖!

简单配置一下gradle就可以将swt-win32-4430.dll放到项目根目录中了,不要放错放到resources目录里了,然后就可以找到它的依赖,org.eclipse.swt,接下来就是先分析分析EloamViewJavaDemo用了这个依赖的哪些东西了,其实就是UI控件和ole部分,一般来说java去调用它怎么都轮不到去用窗口UI界面的,所有关于UI控件的部分可以无视,下面就都是说这个ole的部分,也就是标题说的Eclipse SWT OLE Win32自动化组件:

那么ole到底是什么呢?简单的说就是Windows应用之间相互通讯的一种机制,对于高拍仪来说就是这个SDK与驱动程序的通讯,浏览EloamViewJavaDemo的几个操作按钮绑定的事件就可以发现,都是去调用eloamViewOCX类的方法去完成的,而eloamViewOCX类所有的方法操作都是先制作成Variant参数,然后发送给swtOleAutomationinvoke()具体执行,那么我们就可以把这个过程抽离出来优化一下,不再依赖整个org.eclipse.swt

所以接下来就是处理一下这个OLE,在这个过程中,我去除了大部分OLE协议,但是我保留了一部分,我觉得保留一部分OLE协议,你才知道你用的是OLE协议,是在阉割的部分中我留下的一部分(是故意的)

检查具体依赖的部分

ole部分其实很小一块:

image.png

一看就这么几个文件,就可以更加的放心了,只可惜eclipse不提供这种比较细致引入,所以只能直接把这些类复制到我们的项目里,但请先不要着急直接复制过来,因为这些类还是有相当多引用的,我们应该找找具体用了哪些东西,然后再贴过来,所以先找一找eloamViewOCX类具体importole那些部分:

import org.eclipse.swt.ole.win32.OLE;  
import org.eclipse.swt.ole.win32.OleAutomation;  
import org.eclipse.swt.ole.win32.OleControlSite;  
import org.eclipse.swt.ole.win32.OleFrame;  
import org.eclipse.swt.ole.win32.OleListener;  
import org.eclipse.swt.ole.win32.Variant;

首先是org.eclipse.swt.ole.win32.OLE这个类,只是用到了OLE.OLEIVERB_SHOW这个常量用于显示,所以可以去掉这个类,然后是OleAutomation,这个是核心。后面的OleControlSite完全是为了初始化OleAutomation用的,但仔细看OleAutomation初始化的过程,可以发现其实并不复杂,只需要一个ProgID(GUID),所以OleControlSite意义也不大,可以改造一下OleAutomation的初始化的部分,去掉其对OleControlSite的依赖。然后是OleFrame,只是用于显示,我们网络调用不需要UI自然也不需要这种东西,可以去掉,然后是OleListener,其实只有两个unusedaddEventListener()removeEventListener()方法在用,也是没有用处的,也是可以去掉的。最后是Variant,前文提到了这个是传输参数的,还是要保留的。

综上所述我们需要的就是两个类OleAutomationVariant,把这两个类就可以直接复制到新建的项目中来(但包结构最好还是保留),只需要找到他们的依赖就行。

解决Variant的依赖

首先解决Variant,它只是为解决传输参数类型而设计的类,看看它自己又有哪些依赖:

import org.eclipse.swt.internal.ole.win32.COM;
import org.eclipse.swt.internal.ole.win32.IDispatch;
import org.eclipse.swt.internal.ole.win32.IUnknown;
import org.eclipse.swt.internal.ole.win32.VARIANT;
import org.eclipse.swt.internal.win32.OS;

但仔细分析一下,这个EloamViewJavaDemo用到的参数只涉及COMOS的部分,所以可以其他引用统统删光,也就是像什么IDispatch dispatchDataIUnknown unknownData,包括那个Variant NULL,连同静态块一起删掉,然后就是把COMOS再复制过来了,但是他们也有很多引用,这个没有关系我已经整理好了EloamViewJavaDemo所需的部分,可以copy我整理后的这两个类使用:

整理OS类

package org.eclipse.swt.internal.win32;
public class OS {
   
   
    public static final boolean IsDBLocale;
    public static final int SM_IMMENABLED = 0x52;
    public static final int GMEM_FIXED = 0;
    public static final int GMEM_ZEROINIT = 64;
     static {
   
   
        OS.SetProcessDPIAware();
        IsDBLocale = OS.GetSystemMetrics(SM_IMMENABLED) != 0;
    }
    public static final int PTR_SIZEOF = 4;
     public static final native boolean SetProcessDPIAware();
     public static final native int GetSystemMetrics(int nIndex);
     public static final native int OleInitialize(int pvReserved);
     public static final native void OleUninitialize();
     public static final native int GlobalAlloc(int uFlags, int dwBytes);
     public static final native void MoveMemory(int Destination, int[] Source, int Length);
     public static final native void MoveMemory(int /*long*/ DestinationPtr, long[] Source, int Length);
     public static final native void MoveMemory(long[] Destination, int /*long*/ SourcePtr, int Length);
     public static final native void MoveMemory(int Destination, char[] Source, int Length);
     public static final native void MoveMemory(int DestinationPtr, short[] Source, int Length);
     public static final native void MoveMemory(int DestinationPtr, float[] Source, int Length);
     public static final native void MoveMemory(int DestinationPtr, double[] Source, int Length);
     public static final native void MoveMemory(int Destination, byte[] Source, int Length);
     public static final native void MoveMemory(int[] Destination, int SourcePtr, int Length);
     public static final native void MoveMemory(float[] Destination, int SourcePtr, int Length);
     public static final native void MoveMemory(double[] Destination, int SourcePtr, int Length);
     public static final native void MoveMemory(char[] Destination, int SourcePtr, int Length);
     public static final native void MoveMemory(short[] Destination, int SourcePtr, int Length);
     public static final native void MoveMemory(byte[] Destination, int Source, int Length);
     public static final native int GlobalFree(int hMem);
     public static final native int HeapFree(int hHeap, int dwFlags, int lpMem);
     public static final native int HeapAlloc(int hHeap, int dwFlags, int dwBytes);
     public static final native int GetProcessHeap();
}

整理COM类

package org.eclipse.swt.internal.ole.win32;
import org.eclipse.swt.internal.win32.OS;
public class COM extends OS {
   
   
  public static final native int GUID_sizeof();
  public static final short VT_EMPTY = 0;
  public static final short VT_BYREF = 16384;
  public static final short VT_I1 = 16;
  public static final short VT_I2 = 2;
  public static final short VT_I4 = 3;
  public static final short VT_I8 = 20;
  public static final short VT_NULL = 1;
  public static final short VT_R4 = 4;
  public static final short VT_R8 = 5;
  public static final int VT_BOOL = 11;
  public static final short VT_BSTR = 8;
  public static final short VT_UI2 = 18;
  public static final short VARIANT_TRUE = -1;
  public static final short VARIANT_FALSE = 0;
  public static final int S_OK = 0;
  public static final int GMEM_FIXED = 0;
  public static final int GMEM_ZEROINIT = 64;
  public static final short DISPATCH_METHOD = 0x1;
  public static final int LOCALE_USER_DEFAULT = 2048;
  public static final int CLSCTX_INPROC_SERVER = 1;
  public static final int CLSCTX_LOCAL_SERVER = 4;
  public static final native int SysAllocString(char[] sz);
  public static final native int SysStringByteLen(int bstr);
  public static final native int CLSIDFromProgID(char[] lpszProgID, GUID pclsid);
  public static final native int CLSIDFromString(char[] lpsz, GUID pclsid);
  public static final native int IIDFromString(char[] var0, GUID var1);
  public static final native int DISPPARAMS_sizeof();
  public static final native int VARIANT_sizeof();
  public static final native int EXCEPINFO_sizeof();
  public static final native void VariantInit(int pvarg);
  public static final native int VariantClear(int pvarg);
  public static final native int CoCreateInstance(GUID var0, int var1, int var2, GUID var3, int[] var4);
  public static final native int VtblCall(int fnNumber, int /*long*/ ppVtbl, GUID arg0, int[] arg1);
  public static final native int VtblCall(int var0, int var1, GUID var2, int var3, int var4, int var5, int[] var6);
  public static final native int VtblCall(int fnNumber, int /*long*/ ppVtbl, int arg0, int arg1, int[] arg2);
  public static final native int VtblCall(int fnNumber, int /*long*/ ppVtbl, int arg0, GUID arg1, int arg2, int arg3, DISPPARAMS arg4, int arg5, EXCEPINFO arg6, int[] arg7);
  public static final GUID IIDIUnknown = IIDFromString("{00000000-0000-0000-C000-000000000046}");
  public static final GUID IIDIDispatch = IIDFromString("{00020400-0000-0000-C000-000000000046}"); //$NON-NLS-1$
  private static GUID IIDFromString(String lpsz) {
   
   
    int length = lpsz.length();
    char[] buffer = new char[length + 1];
    lpsz.getChars(0, length, buffer, 0);
    GUID lpiid = new GUID();
    return IIDFromString(buffer, lpiid) == 0 ? lpiid : null;
  }
}

这两个类贴完就可以可以看到OS是没有任何的引用的,但是后面的COM有用到一个GUID,但这个GUID非常简单啦,没有任何其他的引用,直接把这个类复制来就可以,然后关于Variant的部分就收官了。

解决OleAutomation的依赖

下面只要解决OleAutomation的部分就完成了,但它引用非常之多,也略有些复杂,我也是很整理了一会,其实也就是各种阉割了,首先是把org.eclipse.swt.internal.ole.win32下面的DISPPARAMSEXCEPINFO复制过来,这两个类型没有引用而且用的地方太多可以完全保留,然后就是IDispatch这个关键类,它是OLE协议的核心接口,不过我们前面的都阉割了大部分协议了,也不用太在意了:

整理IDispatch类

package org.eclipse.swt.internal.ole.win32;
import org.eclipse.swt.internal.win32.OS;
public class IDispatch {
   
   
  private int address;
  public EXCEPINFO excepInfo = new EXCEPINFO();
  public IDispatch(int address) {
   
   
    this.address = address;
  }
  public int GetIDsOfNames(String rgszNames, int[] rgDispId) {
   
   
    int hHeap = OS.GetProcessHeap();
    int ppNames = OS.HeapAlloc(hHeap, 8, OS.PTR_SIZEOF);
    int memTracker = 0;
    try {
   
   
      int nameSize = rgszNames.length();
      char[] buffer = new char[nameSize + 1];
      rgszNames.getChars(0, nameSize, buffer, 0);
      int pName = OS.HeapAlloc(hHeap, 8, buffer.length * 2);
      OS.MoveMemory(pName, buffer, buffer.length * 2);
      COM.MoveMemory(ppNames, new int[]{
   
   pName}, OS.PTR_SIZEOF);
      memTracker = pName;
      return COM.VtblCall(5, address, new GUID(), ppNames, 1, COM.LOCALE_USER_DEFAULT, rgDispId);
    } finally {
   
   
      OS.HeapFree(hHeap, 0, memTracker);
      OS.HeapFree(hHeap, 0, ppNames);
    }
  }
  public int GetTypeInfo(int iTInfo, int lcid, int /*long*/[] ppTInfo) {
   
   
    return COM.VtblCall(4, address, iTInfo, lcid, ppTInfo);
  }
  public int Invoke(int dispIdMember, DISPPARAMS pDispParams, int pVarResult, int[] pArgErr) {
   
   
    return COM.VtblCall(6, address, dispIdMember, new GUID(), COM.LOCALE_USER_DEFAULT, COM.DISPATCH_METHOD, pDispParams, pVarResult, excepInfo, pArgErr);
  }
}

其实也没有很大的改动,就是eloamViewOCX类那边固定的参数去掉方便下面的OleAutomation进行调用,然后就是最关键的部分OleAutomation了,由它来加载设备、调用接口:

整理OleAutomation类

由于这个改动较大,又是操作流程的入口,所以我将这个类重新更名为SimpleOleAutomation,也可以消除误解:

import org.eclipse.swt.internal.ole.win32.COM;
import org.eclipse.swt.internal.ole.win32.DISPPARAMS;
import org.eclipse.swt.internal.ole.win32.GUID;
import org.eclipse.swt.internal.ole.win32.IDispatch;
import org.eclipse.swt.internal.win32.OS;

public class SimpleOleAutomation {
   
   
  private IDispatch objIDispatch;
  private Variant invokeResult = new Variant();
  private String disName;
  public OleAutomation(String pId) {
   
   
    OS.OleInitialize(0);
    GUID appId = getClassID(pId);
    if (appId == null) {
   
   
      OS.OleUninitialize();
      throw new RuntimeException("GUID not find:" + pId);
    }
    int flags = COM.CLSCTX_INPROC_SERVER;
    if (pId.startsWith("Excel")) flags |= COM.CLSCTX_LOCAL_SERVER; //$NON-NLS-1$
    int[] ppvObject = new int[1];
    int result = COM.CoCreateInstance(appId, 0, flags, COM.IIDIUnknown, ppvObject);
    if (result != COM.S_OK) {
   
   
      OS.OleUninitialize();
      throw new RuntimeException("CoCreateInstance failure");
    }
    int p = ppvObject[0];
    ppvObject[0] = 0;
    COM.VtblCall(0, p, COM.IIDIDispatch, ppvObject);
    objIDispatch = new IDispatch(ppvObject[0]);
    ppvObject[0] = 0;
    objIDispatch.GetTypeInfo(0, COM.LOCALE_USER_DEFAULT, ppvObject);
  }
  private GUID getClassID(String clientName) {
   
   
    GUID guid = new GUID();
    char[] buffer = null;
    if (clientName != null) {
   
   
      int count = clientName.length();
      buffer = new char[count + 1];
      clientName.getChars(0, count, buffer, 0);
    }
    if (COM.CLSIDFromProgID(buffer, guid) != COM.S_OK) {
   
   
      int result = COM.CLSIDFromString(buffer, guid);
      if (result != COM.S_OK) return null;
    }
    return guid;
  }
  public OleAutomation invoke(String name, Variant[] rgvArg) {
   
   
    DISPPARAMS pDispParams = new DISPPARAMS();
    if (rgvArg != null) {
   
   
      pDispParams.cArgs = rgvArg.length;
      pDispParams.rgvarg = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, COM.VARIANT_sizeof() * rgvArg.length);
      int offset = 0;
      for (int i = rgvArg.length - 1; i >= 0; i--) {
   
   
        rgvArg[i].getData(pDispParams.rgvarg + offset);
        offset += COM.VARIANT_sizeof();
      }
    }
    int[] pArgErr = new int[1];
    int pVarResultAddress = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, COM.VARIANT_sizeof());
    int result = objIDispatch.Invoke(getIDsOfNames(disName = name), pDispParams, pVarResultAddress, pArgErr);
    if (pVarResultAddress != 0) {
   
   
      invokeResult.setData(pVarResultAddress);
      COM.VariantClear(pVarResultAddress);
      OS.GlobalFree(pVarResultAddress);
    }
    // free the Dispparams resources
    if (pDispParams.rgdispidNamedArgs != 0) {
   
   
      OS.GlobalFree(pDispParams.rgdispidNamedArgs);
    }
    if (pDispParams.rgvarg != 0) {
   
   
      int offset = 0;
      for (int i = 0, length = rgvArg.length; i < length; i++) {
   
   
        COM.VariantClear(pDispParams.rgvarg + offset);
        offset += COM.VARIANT_sizeof();
      }
      OS.GlobalFree(pDispParams.rgvarg);
    }
    if (result != 0) {
   
   
      System.out.println("[invoke] [" + name + "] result is " + result);
    }
    return this;
  }
  public int getIDsOfNames(String name) {
   
   
    int[] disId = new int[1];
    int result = objIDispatch.GetIDsOfNames(name, disId);
    if (result != COM.S_OK) return 0;
    return disId[0];
  }
  public void printResult() {
   
   
    System.out.println("Invoke [" + disName + "]:" + invokeResult);
  }
  public int resValue() {
   
   
    return Integer.parseInt(invokeResult.str());
  }
}

到此就算是结束了,他们的依赖一共就是下面几个包与类:

  • org.eclipse.swt.internal
    • ole.win32
      • COM
      • DISPPARAMS
      • EXCEPINFO
      • GUID
      • IDispatch
    • win32
      • OS

其中OSCOMGUID都是Variant的依赖,其余都是OleAutomation的依赖,当然其实OleAutomation也直接引用了前面Variant的3个依赖类,然后就可以用OleAutomation改造后的SimpleOleAutomation,对良田高拍仪进行调用了。

测试用例与总结

经过的“轻微”的改动,用起来其实就很简单了,就是看官方文档eloam windows sdk然后跟着下面的方式调用就行了:

public static void main(String[] args) throws Exception {
   
   
    //这个guid就是良田高拍仪的设备号
    SimpleOleAutomation auto = new SimpleOleAutomation("{CA2184F0-5A78-4D81-80F2-B0A7BFC74FBF}");
    auto.invoke("InitDev", null).printResult();
    //打开摄像头
    auto.invoke("OpenVideoEx", new Variant[]{
   
   Variant.ZERO, new Variant(1), Variant.ZERO}).printResult();
    //SetVideoProcAmp设置调节视频数据
    //0x1表示亮度,0x2表示对比度,0x3表示饱和度,0x4表示色调, 0x5表示清晰度,0x6表示伽马,0x7表示白平衡,0x8表示逆光对比,0x9表示启用颜色, 0xA表示增益
    //此处设亮度为180
    auto.invoke("SetVideoProcAmp", new Variant[]{
   
   Variant.ZERO, new Variant(1), new Variant(180), new Variant(true)}).printResult();
    //Deskew自动裁减
    auto.invoke("Deskew", new Variant[]{
   
   Variant.ZERO, new Variant(true)}).printResult();
    //此处如果没有延时有时会拍不出来,可能是我的设备有问题
    Thread.sleep(1000);
    //摄像并将文件保存到temp.jpg
    auto.invoke("Scan", new Variant[]{
   
   Variant.ZERO, new Variant("temp.jpg"), new Variant(0x0100)}).printResult();
    auto.invoke("DeInitDev", null).printResult();
}

用例就这么几行代码就可以了,非常简明呀!将这个的程序打包之后只有140kb,其中120kb还是因为swt-win32-4430.dll。如果不是这样做,直接用官方给的EloamViewJavaDemo所依赖的org.eclipse.swt.win32那个jar包,它本身大小就有2.57mb,还有很多依赖的dll也有832kb,还没开始写呢,依赖就3mb起步了。而经过本文对ole的抽离阉割,加上用例总共也就140kb,可以说能节省相当多资源了。

本文依照作者在2021年的一些开发经验,于2023年7月14日同时写作并发布在lyrieek的稀土掘金社区与阿里云开发者社区。

目录
相关文章
|
2月前
|
移动开发 安全 数据安全/隐私保护
iOS 全局自动化代码混淆工具!支持 cocoapod 组件代码一并混淆
iOS 全局自动化代码混淆工具!支持 cocoapod 组件代码一并混淆
|
10天前
|
Web App开发 IDE 测试技术
【专栏】深入理解自动化测试框架 Selenium 的核心组件
【4月更文挑战第27天】Selenium 是一款广泛使用的自动化测试框架,核心组件包括 WebDriver(与浏览器交互的接口,支持多浏览器测试),IDE(可视化的测试脚本录制和编辑工具)和 Grid(分布式测试,实现多机器并行测试)。通过这些组件,开发者能高效、稳定地进行自动化测试,但需注意浏览器兼容性、脚本维护和性能问题。理解并掌握这些组件的使用,能提升测试效率和质量。
|
2月前
|
设计模式 IDE 测试技术
深入理解自动化测试框架Selenium的核心组件
【2月更文挑战第30天】 在快速迭代的软件开发过程中,自动化测试已成为确保产品质量和加快上市速度的关键。本文将深入探讨Selenium这一广泛使用的自动化测试框架,剖析其核心组件以及它们如何协同工作以提供高效、灵活的测试解决方案。我们将从Selenium架构的基础出发,详细解读WebDriver API、Selenium Grid、以及各种语言绑定等关键部分,并讨论如何通过这些组件进行有效的UI测试。
|
9月前
|
Java Android开发
【Eclipse】创建SWT项目
【Eclipse】创建SWT项目
126 0
|
11月前
|
JavaScript 测试技术 开发工具
从0搭建vue3组件库:自动化发布、管理版本号、生成 changelog、tag
从0搭建vue3组件库:自动化发布、管理版本号、生成 changelog、tag
247 0
|
11月前
|
JavaScript 前端开发 开发工具
从0搭建Vue3组件库:使用gulp自动化处理打包与发布
从0搭建Vue3组件库:使用gulp自动化处理打包与发布
320 0
|
Android开发
《Android内存泄漏自动化链路分析组件Probe》电子版地址
Android内存泄漏自动化链路分析组件Probe
66 0
《Android内存泄漏自动化链路分析组件Probe》电子版地址
|
Android开发
《Android内存泄漏自动化链路分析组件--Probe》电子版地址
Android内存泄漏自动化链路分析组件--Probe
58 0
《Android内存泄漏自动化链路分析组件--Probe》电子版地址
|
JavaScript 前端开发
vue中实现组件自动化全局注册
vue中实现组件自动化全局注册
122 0
vue中实现组件自动化全局注册
|
4月前
|
Java Maven Android开发
在Eclipse里配置Maven插件
Maven是一款比较常用的Java开发拓展包,它相当于一个全自动jar包管理器,会导入用户开发时需要使用的相应jar包。使用Maven开发Java程序,可以极大提升开发者的开发效率。下面我就跟大家介绍一下如何在Eclipse里安装和配置Maven插件。
51 0

热门文章

最新文章

推荐镜像

更多