7 扩展UiBot命令

说起 “插件” ,很多人脑海中都会浮现出IE/Chrome/Firefox浏览器插件、Eclipse、Visual Studio、Sublime Text等各种编程工具的插件,这些应用工具层面上的插件,依托于原平台运行,但又扩展了原平台的功能,极大丰富了原有工具和平台。基于插件,用户甚至可以定制化地打造个性化的浏览器和编程工具。

其实,绝大部分编程语言也提供这样一种插件机制,我们一般称之为 “类库”。比如Java语言,除了提供最基本的语言特性之外,还额外提供了内容极为丰富的核心库,涵盖了网络通信、文件处理、加解密、序列化、日志等方方面面,几乎无所不包。但是,即便是如此完善、如此强大的Java官方核心库,仍有很多用户还是觉得不够用,或者说在自己特定的应用场景中不好用。因此,一部分具备编程能力的用户,根据自己的应用需求和场景特点,将某一部分的功能打造得非常强大,弥补或者超越了官方核心库。这些用户将这部分功能抽取和贡献出来,这就形成了公认的第三方类库,这些第三方类库和官方核心库一起,共同构成了繁荣的Java生态圈。JavaScript和Python语言同样也是如此。

我们再回到UiBot上来。前文提到过,UiBot本质上是一个平台工具,这个平台有几个特点,第一个特点就是 “强大” ,UiBot提供用以搭建RPA流程的零部件数量非常丰富,大大小小一整套的功能模块,从基础的键盘鼠标操作、各种界面元素操作,到常见办公软件、浏览器的自动化操作,从各种各样的数据处理,到文件处理、网络和系统操作等,一应俱全。但是这个平台还有第二个特点,那就是 “简单”,UiBot将最通用、最常用、最基本、最核心的功能抽取出来,集成到平台中,形成一套简明、精干的核心库。如果一味地堆砌功能,将大大小小的所有功能一股脑地集成到UiBot的框架中,那UiBot的框架就会变得非常的臃肿,学习难度也会大幅上升。

那么,如果用户遇到了一个UiBot框架不能直接解决的问题,那么应该怎么办呢?类似于Java或JavaScript,UiBot也提供了插件机制,如果您擅长市面上的通用编程语言,那么您可以利用这些编程语言实现特定的功能,然后在UiBot中调用这个功能。

更有意思的是,UiBot还支持用多种不同的编程语言来编写插件。包括Python语言、Java语言、C#语言和C/C++语言。您可以在此范围内任意选择喜欢的语言,无论哪种语言编写的插件,在UiBot里面使用起来几乎没有差异。

当然,由于不同的编程语言之间有比较大的差异,使用不同的编程语言为UiBot编写插件的方法也是不一样的。本文分别介绍使用Python语言、Java语言、C#语言为UiBot编写插件的方法。考虑到C/C++语言比较难学,鉴于篇幅,本文就不对这两种语言的插件机制进行介绍了。

在以下描述中,经常会涉及到文件目录,如无特殊说明,均指相对于UiBot安装目录的相对路径。为了便于书写,我们采用/符号来作为路径的分隔符,而不是Windows习惯的\符号。

7.1 用Python编写插件

7.1.1 编写方式

用Python编写UiBot插件是最简单的,只需要用任意文本编辑器书写扩展名为.py的文件(下文简称py文件),并且保存为UTF-8格式,放置在extend/python目录下,即可直接以插件名.函数名的形式,调用这个py文件里面定义的函数。

注意,这里的插件名是指文件名去掉扩展名.py以后的部分,例如文件名为test.py,则插件名为test。

我们来看一个完整的例子:

  1. 编写插件源代码。打开extend/python目录,在这个目录下创建test.py文件,使用记事本打开test.py文件,写入如下内容:
  1. 将test.py文件另存为UTF-8编码格式,如下图:
Python插件编写
Python插件编写
  1. 调用插件功能。打开UiBot,新建一个流程,在源代码视图写入代码:
  1. 验证插件功能是否正确。运行此流程,结果如下所示,代表插件调用正常。
Python插件运行结果
Python插件运行结果

7.1.2 插件API

在用Python编写插件的时候,除了可以调用Python本身的功能之外,插件还可以反过来调用UiBot的一部分功能。我们称之这些被调用的功能为插件API。

插件API的调用方法如下:

  1. 在Python插件中写入如下代码:
  1. 直接调用插件API,例如:

目前Python插件中能使用的插件API包括:

  • UiBot.IsStop()

这个函数用于检测当前流程是否需要马上停下来(比如用户按下了“停止”按钮)。当需要停下来时,返回True,否则返回False

当某个插件函数需要执行比较长时间的时候,在执行过程中,如果用户决定停止流程,但插件函数还没有执行完成,流程将无法立即停下来。 因此,建议在编写插件时,考虑到插件函数执行时间比较长的情况,并且在函数执行过程中定期的调用这个插件API,来确定流程是否要停下来。如果要停,应该立即退出插件函数。

  • UiBot.GetString(string_path)

这个函数用于获得当前语言版本的某个字符串,参数是一个字符串路径(下面解释),返回值是获得的字符串。

我们在插件中可能会用到字符串,有的字符串内容是需要区分语言版本的。比如我们在插件中提示一个报错信息,这个报错信息应该包含中文版、英文版或其他语言版本。如果用户使用的是中文版的UiBot,那么就报中文的错误;如果用户使用的是英文版的UiBot,就报英文的错误。

如何做到这一点呢?我们可以在UiBot的安装目录下看到lang/en-us/extend.jsonlang/zh-cn/extend.json这两个文件(其他语言版本也有类似的路径,不再赘述),分别表示插件中要用到的英文版和中文版的字符串。可以把我们要用的字符串的不同语言版本分别写到这些文件中去,然后在插件中用UiBot.GetString()来获得需要的字符串即可。

当然,UiBot的插件有很多,每个插件中也有很多字符串,这么多字符串都放在一个文件中,如何保证不冲突呢?很容易看到,这个文件是JSON格式的,其中用多个嵌套的JSON Object来区分不同的字符串。当我们需要使用一个字符串的时候,只需要在UiBot.GetString()的参数中填入字符串路径即可。所谓字符串路径,是指这个字符串所在的Object及其往上各级Object的Key的组合,其中用/分隔。比如UiBot.GetString('App/Prefix')获得的就是这个文件中,Key为’App’的JSON Objet中的Key为’Prefix’的字符串。

  • UiBot.GetCommanderInfo()

当UiBot Worker在运行流程时,和UiBot Commander建立了连接,则可以通过这个API获得Commander的一些信息,如URL等。除了UiBot官方之外,一般用户的插件不会用到UiBot Commander,所以并不需要使用这个API。

7.1.3 插件的导入模块

单纯的一个py文件,功能往往比较有限。只有在py文件中通过import语句,导入其他的一些Python模块,其功能才更加丰富。

实际上,在UiBot安装目录的lib/site-packages路径下,已经预置了很多的Python模块(或者Python包。Python包和模块的定义和差异请查阅相关说明,本文不作解释)。这些模块都是在Python插件中可以直接使用的。 如果我们在插件中还需要导入其他的模块,一种方式是将其放置在lib/site-packages路径下,还有一种方式是将其放置在extend/python/<插件名>.lib路径下。注意这里的<插件名>.lib也是一个目录,如果我们有个Python插件,文件名是test.py,则这个目录就是test.lib

在编写插件时,我们更推荐把插件中导入的模块(假设这些模块是UiBot本身没有预置的)放在extend/python/<插件名>.lib路径下,而不是lib/site-packages路径下。因为lib/site-packages是一个公用目录,当我们删除掉一个插件的时候,很难从中分辨出到底哪个模块是被这个插件所使用的,而现在已经不再需要了。但如果把这些模块放在extend/python/<插件名>.lib路径下,就很清晰了,因为在删除插件时,只需要把和插件同名,且扩展名为.lib的目录一并删掉,就可保证不错不漏。

另外,值得注意的是:有的py文件会导入一些扩展名为pyd的模块,这些模块实际上是二进制格式的动态链接库。请注意动态链接库区分32位版本和64位版本,如果您使用的UiBot是32位版本,那么这些pyd模块也应该是32位版本的;否则,pyd模块就应该是64位版本的。目前,社区版的UiBot都是32位版本的。

7.1.4 隐藏源代码

对于py文件来说,其源代码是完全公开的。如果我们既要让其他人使用我们编写的Python插件,又不希望被其他人看到插件的源代码,该怎么办呢?

我们只需要在UiBot Creator中至少调用一次这个插件,就会看到有一个extend/python/__pycache__目录被创建出来了。到这个目录里面去看一看,里面有一些以插件名开头,中间是诸如.cpython-37这样的内容,以扩展名.pyc结束的文件。例如,我们的py文件为test.py,那么会自动创建这样的一个文件:extend/python/__pycache/test.cpython-37.pyc

把这个文件改名为test.pyc,并且放在extend/python目录下,同时删除掉原来的test.py(删除前请自行备份),我们仍然可以在UiBot中使用test这个插件,且用法不变。因为它的代码已经以二进制的格式保存在test.pyc中了。我们只需要把这个文件发送给其他人去使用,就可以避免被人直接读到源代码。

当然,test.pyc实际上并不是加密的,仍然有可能被人反编译得到一部分源代码。如果要做比较彻底的加密,还需要配合其他手段,本文不再赘述。

7.1.5 其他注意事项

  1. 如果Python插件的函数中定义了N个参数,那么在UiBot中调用的时候,可以传入少于N个参数,多余的参数会自动补为None。但不可以传入多于N个参数。

  2. 可以把UiBot中的数组或者字典类型作为参数,传入Python插件中,对应为Python中的list或dict类型。也可以把Python中的list, tuple或dict类型作为返回值,传回到UiBot,前两者都被转换为数组类型,后者被转换为字典类型。 无论传入参数,还是返回值,这些复合类型在Python插件和UiBot之间都采用值传递的方式,而不是引用传递的方式。

  3. 可以在Python插件的函数中抛出异常,异常可以由Python插件自行捕获,也可以不捕获。如果Python插件不捕获,那么异常会自动被传到UiBot中,UiBot可以捕获。 如果UiBot也不捕获,那么流程的运行会出错退出,并且会在出错信息中说明是由于Python插件中的异常导致的,以便排查问题。

  4. UiBot中已经内置了Python的运行环境,无需额外安装Python。即使安装了,UiBot也不会使用您安装的Python。目前UiBot内置的Python是3.7.1版本。

  5. Python中的变量、函数都是区分大小写的,但在UiBot中使用Python插件时,仍然可以不区分大小写的调用其中的函数。比如,在前面的例子中,可以在UiBot中写test.add(1,1),也可以写Test.ADD(1,1),其效果完全一样。

  6. 可以在Python中使用全局变量,比如可以把变量写到函数之外。全局变量的值被Python插件中的所有函数所共享,但不同的插件不共享全局变量。

  7. 使用Python编写UiBot插件很容易,但Python本身是一门独立的编程语言,使用文本编辑器开发和调试都很不方便,因此建议使用集成开发环境,例如Visual Studio Code进行Python插件开发。

7.2 用Java编写插件

7.2.1 编写方式

从UiBot 5.0版开始,支持用Java语言写UiBot的插件。用过Java的读者都知道,Java的源代码文件一般以.java扩展名结尾,需要先用JDK(Java Development Kit)编译成扩展名为.class的字节码(Byte Code)文件,然后才能运行。运行的时候不一定要安装JDK,也可以只安装JRE(Java Runtime Environment)。

由于版权的限制,UiBot中没有内置JDK,但内置了由Oracle公司发布的JRE 1.7版本。所以,为了用Java编写插件,需要您自行下载和安装Oracle JDK 1.7版本。下载和安装的方法在互联网上有大量资料讲述,本文不再重复。

为了方便您用Java语言写UiBot的插件,我们设计了一个插件的例子并将其源码放在GitHub上,点击这里即可获取。如果您习惯使用git,也可以从这个URL拉取:https://github.com/Laiye-UiBot/extend-example。后续内容将围绕这个例子展开。

按照Java语言的规范,首先我们需要设计一个插件名,然后将源代码文件命名为<插件名>.java,并在文件中写一个Java类,这个类的名字也必须是插件名。在例子中,我们可以看到插件名为JavaPlugin,所以源代码文件名必须是JavaPlugin.java,而在这个文件中会定义一个名为JavaPlugin的类:

为了让UiBot能够正常使用这个类,这个类必须是public的,也不能包含在任何包里。类里面可以定义public、private或protected的函数,但只有public函数是UiBot可以直接调用的。 比如,我们在例子中定义了一个叫Add的函数,这个函数是public的,所以,可以在UiBot中调用它。

怎么调用呢?需要先用JDK中的javac程序,编译这个源码文件,在命令行输入:

当然,这里需要javac程序在当前的搜索路径下。另外,例子中的JavaPlugin.java是UTF-8编码的,且里面有中文字符,所以需要加上-encoding utf8的选项。如果没有中文字符,则此选项可以省略。

如果编译成功,会自动生成名为JavaPlugin.class的文件,把这个文件放到extend/java目录下,然后就可以像使用Python插件一样的使用它。例如,我们可以打开UiBot,新建一个流程,在源代码视图写入代码:

运行此流程,结果如下所示,代表插件调用正常。

Java插件运行结果
Java插件运行结果

7.2.2 插件API

和Python插件类似,在Java插件中,也可以使用插件API,反过来调用UiBot的一部分功能。如果要调用插件API,无需import任何包,只需要在编译Java插件的时候,把插件例子中的UiBot目录复制到Java插件源代码所在目录下即可。

目前Java插件中能使用的插件API包括:

  • UiBot.API.IsStop()

用于检测当前流程是否需要马上停下来(比如用户按下了“停止”按钮)。当需要停下来时,返回True,否则返回False

其具体作用请参考Python插件中使用的UiBot.IsStop()函数。

  • UiBot.API.GetString(string_path)

用于获得当前语言版本的某个字符串,参数是一个字符串路径(下面解释),返回值是获得的字符串。

其具体作用请参考Python插件中使用的UiBot.GetString()函数。 另外,在插件例子中,我们也使用到了这个API,来获得字符串路径为’Excel/SaveBook’的字符串,即extend.json文件中,名为’Excel’的JSON Object中的名为’SaveBook’的字符串。

下面这段UiBot源代码会先调用Java插件中的GetString()函数,再反过来调用UiBot中的UiBot.API.GetString()。您可以在UiBot中输入这段代码,运行试试,看能得到什么结果。

  • UiBot.API.GetCommanderInfo()

当UiBot Worker在运行流程时,和UiBot Commander建立了连接,则可以通过这个API获得Commander的一些信息。除UiBot官方之外,一般用户的插件不会用到UiBot Commander,所以并不需要使用这个API。

7.2.3 变量的传递

Java是静态类型的编程语言,也就是说,变量在使用之前需要先定义,且定义时必须指定变量的类型(整数、浮点数、字符串等),在运行的时候,变量也只能是指定的这种类型。而且,数组中通常只能包含同一种类型的数据。

但这与UiBot有很大的不同,UiBot的变量是动态类型的,可以不指定类型,运行的时候还可以随意更换类型,数组中也可以包含各种不同类型的数据。

所以,为了在UiBot中顺利使用Java插件,需要符合以下规定:

  • 如果Java插件的参数是整数、浮点数、字符串、布尔类型,UiBot传入的参数也必须是同样的类型(除了下面几条所述的例外情况),否则会出错
  • 如果Java插件的参数是浮点数,可以传入整数,不会出错。但反之不成立,也就是说,如果Java插件的参数是整数,不能传入浮点数
  • 如果Java插件的参数是长整数型(long),可以传入小于 2^31 的整数,不会出错。但反之不成立,也就是说,如果Java插件的参数是整数型(int),不能传入大于等于 2^31 的整数
  • 如果需要把字典或数组类型从UiBot中传到Java插件中,Java插件中的参数类型只能使用org.json.JSONArray(对应数组)或者org.json.JSONObject(对应字典)
  • 如果需要把字典或数组类型从Java插件中传到UiBot中,Java插件中的返回值类型只能使用org.json.JSONArray或者org.json.JSONObject。UiBot会自动把org.json.JSONArray类型的返回值转换成UiBot中的数组,而把org.json.JSONObject类型的返回值转换成UiBot中的字典
  • 无论传入参数,还是返回值,这些复合类型在Java插件和UiBot之间都采用值传递的方式,而不是引用传递的方式
  • 可以在Java源代码中写import org.json.*;,这样就可以直接使用JSONArray或者JSONObject类型,避免org.json的前缀。在插件例子中就是这样写的。另外,org.json这个包已经被UiBot包含在运行环境中了,无需额外下载和安装。

在插件例子中,有一个Concat函数,用于演示如何把两个数组从UiBot传到Java插件中,又如何把两个数组连接后的结果返回到UiBot中。建议读者仔细阅读。

7.2.4 插件的引用模块

和Python类似,单纯的一个Java文件,功能往往比较有限。只有在源代码中通过import语句,导入其他的一些Java包,其功能才更加丰富。

我们在UiBot中已经内置了Oracle JRE 1.7,当然也包含了JRE中自带的包,比如com.sun.javafx等。此外,我们还在UiBot中内置了要用到的org.json包。除了上述内容以外,其他第三方的Java包可以以.class格式的文件存在,也可以以.jar格式的文件存在。熟悉Java语言的读者,对以上两种文件应该有足够的了解,前者是Java代码的Byte Code,后者是多个Byte Code打包而成的压缩文件。

在启动UiBot的时候,会自动把extend/java目录加入到Java的classpath中。此外,当加载一个Java插件的时候,还会把extend/java/<插件名>.lib这个目录,以及这个目录下所有扩展名为.jar的文件,都自动加入到Java的classpath中。比如,我们有个Java插件,名为A.class,且放置在extend/java目录下。那么,extend/java目录、extend/java/A.lib目录、以及extend/java/A.lib/*.jar,都会加入classpath中。我们的插件中如果需要引用任何第三方的Java包,只要把包放置在这些路径下,并且符合Java的classpath规范,即可使用。

7.2.5 其他注意事项

  1. Java插件中的函数不支持可变参数或默认参数,在调用时必须传入指定数量、指定类型的参数。

  2. 可以在Java插件的函数中抛出异常,异常可以由Java插件自行捕获,也可以不捕获。如果Java插件不捕获,那么异常会自动被传到UiBot中,UiBot可以捕获。 如果UiBot也不捕获,那么流程的运行会出错退出,并且会在出错信息中说明是由于Java插件中的异常导致的,以便排查问题。

  3. Java中的变量、函数都是区分大小写的,但在UiBot中使用Java插件时,仍然可以不区分大小写的调用其中的函数。比如,在前面的例子中,可以在UiBot中写javaPlugin.add(1,1),也可以写JavaPlugin.ADD(1,1),其效果完全一样。

  4. 在写Java插件的时候,实际上是定义了一个Java类,并且把类里面的public函数给UiBot去调用。这个类可以有构造函数,也可以有成员变量,它们的初始化都会在流程刚刚开始运行的时候自动完成。

  5. UiBot中内置了Oracle JRE 1.7版本,您需要自行下载Oracle JDK 1.7版本去编译Java插件。虽然有时JDK和JRE的版本不一致也可以工作,但为了减少麻烦,还是推荐用同一版本。另外,也推荐使用集成开发环境,来进行Java插件的开发,例如Eclipse或IntelliJ IDEA。

7.3 用C#.Net编写插件

7.3.1 编写方式

UiBot本身的部分代码就是基于微软的.Net框架,用C#语言编写的。所以,也可以用C#语言编写UiBot的插件(以下简称为.Net插件)。实际上,微软的.Net框架支持多种编程语言,包括VB.Net、C++/CLI等等,这些编程语言都遵循.Net框架的规范,它们都可以用来编写.Net插件,但因为C#是微软主推的编程语言,所以本文用C#举例,有经验的读者亦可将其移植到.Net框架上的其他语言。另外,UiBot对.Net插件的支持也是在不断升级的,本文以UiBot 5.1版为例,如果在老版本的UiBot上,一些例子可能无法正常运行,请及时升级。

为了方便您用C#语言写.Net插件,我们设计了一个插件的模板,并将其源码放在GitHub上,点击这里即可获取。如果您习惯使用git,也可以从这个URL拉取:https://github.com/Laiye-UiBot/extend-example。建议您在写.Net插件的时候,直接在这个模板的基础上写,而无需从头开始。后续讲述的内容,也将围绕这个模板中的例子展开。

和Java插件类似,.Net插件也需要编译成扩展名为.dll的文件,才能被UiBot使用。微软的集成开发环境Visual Studio兼具编写和编译的功能,并且也提供了免费的社区版,推荐下载使用。我们提供的模板是基于Visual Studio 2015版本的,您可以选择这个版本,也可以选更高版本的Visual Studio,但不建议使用低于2015版本的Visual Studio。

安装了Visual Studio,并下载了我们的.Net插件模板后,可以双击UiBotPlugin.sln文件,这是一个“解决方案”,名字起得很唬人,实际上就是多个相关联的文件的集合。用Visual Studio打开这个解决方案后。可以看到,里面包含了很多内容,其中唯一需要我们动手修改的是UiBotPlugin.cs文件,其他的文件、引用、Properties等都可以不去动。如下图:

.Net插件的模板
.Net插件的模板

UiBotPlugin.cs文件里,有一个叫UiBotPlugin的命名空间,其中包含了一个接口(interface)和一个类(class)。为了避免混淆,我们推荐把这个命名空间的名字改为您的插件名。比如最终的插件文件是DotNetPlugin.dll,那么插件名就是DotNetPlugin,这个命名空间的名字也改为DotNetPlugin为宜。

从模板中可以看出:在接口里面声明了三个函数,在类里面写了这三个函数的实现。这三个函数都是例子,您随时可以把它们的声明和实现都删掉,加入您自己的插件函数。但请特别注意:在加入函数的时候,也要保持类似的写法,需要在接口中声明,在类中实现,否则,UiBot不能正常识别这个插件函数。

我们用例子中的Add函数为例,尝试编译插件,并在UiBot中调用这个函数:

  1. 选择Visual Studio的“生成”(Build)菜单项,编译这个解决方案之后,会看到在插件的目录下有个叫Release的目录,里面产生了一个叫UiBotPlugin.dll的文件。
  2. 把这个文件手动改名为您的插件名,并保留.dll的扩展名。如改名为DotNetPlugin.dll
  3. 把这个文件放到UiBot的extend/DotNet目录下。
  4. 打开UiBot,新建一个流程,在源代码视图写入代码:

运行此流程,结果如下所示,代表插件调用正常。

.Net插件运行结果
.Net插件运行结果

您可能注意到了,在前面的Python插件、Java插件的例子中,都有Add这个例子函数,而除了插件名之外,UiBot调用它们的方式和运行结果都没有区别。实际上,不同的插件内部实现是有很大差异的,比如在Python语言里,默认用UTF-8编码来保存字符串,而在.Net里默认用UTF-16保存。但UiBot已经帮您抹平了这些差异,让您在使用的过程中不必关心这些细节。

7.3.2 插件API

和Python、Java插件类似,在.Net插件中,也可以使用插件API,反过来调用UiBot的一部分功能。如果要调用插件API,只需要基于UiBot提供的模板编写插件即可,无需做其他任何设置。

.Net插件中能使用的插件API的名字、参数和含义都和Java插件完全一致,例如,可以用UiBot.API.IsStop()来检测当前流程是否需要马上停下来,等等。请参考Java插件的中关于插件API的讲解,不再赘述。

在模板中,您可能会看到一个名叫DotNetAdapter.dll的文件。实际上,这个文件是UiBot每个版本都包含的。从UiBot 5.1版开始,您调用的.Net版的插件API,实际上都在这个文件里面实现。因此,当您的插件发布的时候,并不需要包含这个文件,因为UiBot已经自带了。

同时,如果您的UiBot更新到了更高的版本,DotNetAdapter.dll中也可能会包含了更多的插件API。您可以自行从UiBot中拿到新版本的DotNetAdapter.dll文件,并放在您编写的插件的源代码所在的目录下,即可使用到新版的插件API。

7.3.3 变量的传递

和Java类似,C#.Net也是静态类型的编程语言,变量在使用之前需要先定义,且定义时必须指定变量的类型。而且,数组中通常只能包含同一种类型的数据。这与UiBot的动态类型有很大的不同。

因此,在编写和使用.Net插件的时候,需要符合以下规定:

  • 对于整数、浮点数、字符串、布尔类型等基本类型的参数,UiBot对.Net插件的类型检查不是很严格,它会尽量进行转换,即使转换不成功,也不会报错。所以,请在使用时特别留意每个参数的类型,避免传入了不正确的值,而没有及时发现。
  • 如果需要把字典或数组类型从UiBot中传到.Net插件中,.Net插件中的参数类型只能使用Newtonsoft.Json.Linq.JArray(对应数组)或者Newtonsoft.Json.Linq.JObject(对应字典)。在模板中,由于我们已经写了using Newtonsoft.Json.Linq;,所以可以省略前缀,简写为JArray(对应数组)或JObject(对应字典),下文亦使用此简化写法。
  • 如果需要把字典或数组类型从.Net插件中传到UiBot中,.Net插件中的返回值类型只能使用JArray(对应数组)或JObject(对应数组)。UiBot会自动把JArray类型的返回值转换成UiBot中的数组,而把 JObject类型的返回值转换成UiBot中的字典。
  • 无论传入参数,还是返回值,这些复合类型在.Net插件和UiBot之间都采用值传递的方式,而不是引用传递的方式。

在插件模板中,有一个作为例子的Concat函数,用于演示如何把两个数组从UiBot传到.Net插件中,又如何把两个数组连接后的结果返回到UiBot中。建议读者仔细阅读。

7.3.4 插件的引用模块

UiBot本身是依赖于.Net Framework的,并且假设用户已经安装了.Net Framework 4.5.2(含)以上的版本。如果没有安装.Net Framework,或者版本不对,UiBot本身都不能运行,当然就更不能使用您编写的插件了。所以,在编写插件的时候,只要您的插件依赖的也是.Net Framework 4.5.2版本,就不必担心环境不匹配的问题。

微软已经在.Net Framework里面内置了非常丰富的功能,但难免有的功能仍然没有包含,需要引用第三方的.Net dll文件。

和Java插件类似,UiBot在加载一个.Net插件的时候,如果这个.Net插件引用了其他第三方的.Net dll文件,UiBot首先会试图到.Net插件所在的目录下去搜索被引用的dll文件。如果没有找到,还会再到<插件名>.lib这个目录下去找一次。比如,我们有个.Net插件,名为A.dll,放置在extend/DotNet目录中,且引用了B.dll。那么UiBot会先尝试找extend/DotNet/B.dll,再尝试找extend/DotNet/A.lib/B.dll。如果这两个目录下都没有找到,会抛出异常。

7.3.5 其他注意事项

  1. JArray和JObject并不是.Net Framework里面自带的,而是使用了开源的Json.Net。在编译和运行的时候,都需要依赖一个名为Newtonsoft.Json.dll的文件。 在UiBot提供的模板中,已经包含了这个文件。同时,在每个版本的UiBot中,也会自带这个文件。因此,您可以直接使用JArray和JObject,而并不需要把这个文件包含在插件当中。

  2. 在编译插件的时候,编译器可能会警告“DotNetAdapter的处理器架构不匹配”之类的信息。实际上没有影响,无需理睬这个警告。

  3. .Net插件中的函数支持默认参数。在调用时,如果某些参数有默认值,则可以不传值,此参数会自动取默认值。

  4. 可以在.Net插件的函数中抛出异常,异常可以由.Net插件自行捕获,也可以不捕获。如果.Net插件不捕获,那么异常会自动被传到UiBot中,UiBot可以捕获。 如果UiBot也不捕获,那么流程的运行会出错退出,并且会在出错信息中说明是由于.Net插件中的异常导致的,以便排查问题。

  5. .Net中的变量、函数都是区分大小写的,但在UiBot中使用.Net插件时,仍然可以不区分大小写的调用其中的函数。比如,在前面的例子中,可以在UiBot中写DotNet.add(1,1),也可以写dotnet.ADD(1,1),其效果完全一样。

7.4 扩展编译机制

Creator5.1.0版本之前,扩展插件需安装至UiBot的安装路径才会编译。从Creator5.1.0版本之后,扩展插件都会安装到对应流程项目路径下的extend的目录下编译。主要影响发布流程,Creator5.1.0版本之后只会打包流程目录下的扩展插件至流程,不会打包安装目录下的扩展插件,为避免遗漏扩展,建议通过命令中心添加扩展插件,按需安装至流程。

以下所有的文件路径,均以UiBot的流程项目目录存放路径为基准。

当我们使用UiBot,在源码中显式的加载一个扩展,例如:

Import Test

或者隐式的加载一个扩展,例如:

Test.MyFunction()

的时候,UiBot在编译阶段,会做如下动作:

  • 判断这个名字是否已经被变量、函数名等占用了,如果有,则报错。

  • 判断是否已加载过这个名字(不区分大小写)的扩展,如果没有,则尝试加载。

  • 尝试加载插件,按以下目录顺序尝试加载:

    1、 目录:extend/lua_mod,扩展名so

    2、 目录:extend/DotNet,扩展名dll

    3、 目录:extend/python,扩展名先尝试py,再尝试pyc

    4、 目录:extend/java,扩展名class

    5、 目录:extend/lua,扩展名lua

  • 如果经过以上尝试,都没有找到此插件,则尝试加载UB语言的task文件。

  • 如果以上尝试均告失败,报错。

7.4.1 其它注意事项

1、对于扩展名为so、dll的插件,如果还引用了其他dll文件,UiBot没有做任何特殊处理,只能在插件所在目录下去查找。

2、对于扩展名为py、pyc的插件(即python插件),如果还import了其他的py或pyc文件,则UiBot会在加载插件之前,临时为其增加一个import路径,即将插件扩展名替换为 .lib 的目录。

例如:

插件是 extend/python/Test.py,则会临时增加一个import路径搜索 extend/python/Test.lib。在import的时候,仍然会优先在python默认的路径中进行搜索,然后才会在临时增加的路径中搜索。当插件加载完成后,这个临时新增的import路径也会被去掉。

3、对于扩展名为class的插件(即java插件),如果还import了其他的java类,则按照java的classpath的规则,先在java默认的目录中搜索(一般是jre7/lib/ext目录),然后在当前class文件所在的目录下,按照import的包名的规则进行搜索。

例如: 插件是extend/java/Test.class,且在插件中写了 Import MyDep.MyClass 则会尝试加载extend/java/MyDep/MyClass.class文件。