主要内容

本页采用了机器翻译。点击此处可查看最新英文版本。

使用 VBA 集成组件

何时使用公式函数或子程序

VBA 提供两种基本过程类型:函数和子程序。

您可以直接从工作表中的单元格访问 VBA 函数作为公式函数。当原始 MATLAB® 函数返回一个或不返回任何输出时,使用函数过程。

您可以将子程序作为通用宏来访问。当原始 MATLAB 函数返回值数组或多个输出时,使用子程序,因为您需要将这些输出映射到工作表中的多个单元格/范围。

当您创建组件时,MATLAB Compiler™ 会生成一个 VBA 模块(.bas 文件)。该文件包含简单的调用包装器,每个调用包装器都作为类的每个方法的函数过程实现。有关详细信息,请参阅Excel 加载项编译的工作原理

使用 Microsoft Excel 初始化 MATLAB Compiler

在使用任何 MATLAB Compiler 组件之前,请使用当前的 Microsoft® Excel® 实例初始化支持库。对使用 MATLAB Compiler 组件的 Excel 会话执行一次此操作。

要进行此初始化,请调用实用工具库函数 MWInitApplication,它是 MWUtil 类的成员。此类是 MWComUtil 库的一部分。有关详细信息,请参阅类 MWUtil

将此初始化代码添加到 VBA 模块的一种方法是提供一个子例程,该子例程只执行一次初始化,然后在所有后续调用中退出。以下 Microsoft Visual Basic® 代码示例使用 Excel 的当前实例初始化库。一个名为 ObjectMCLUtil 类型的全局变量保存了 MWUtil 类的实例,另一个名为 BooleanbModuleInitialized 类型的全局变量存储了初始化过程的状态。私有子程序 InitModule() 创建 MWComUtil 类的实例,并使用 MWInitApplication 参量调用 Application 方法。一旦该函数成功执行,所有后续调用都会退出而无需重新初始化。

Dim MCLUtil As Object
Dim bModuleInitialized As Boolean

Private Sub InitModule()
   If Not bModuleInitialized Then
      On Error GoTo Handle_Error
      If MCLUtil Is Nothing Then
         Set MCLUtil = CreateObject("MWComUtil.MWUtil")
      End If
      Call MCLUtil.MWInitApplication(Application)
      bModuleInitialized = True
      Exit Sub
Handle_Error:
      bModuleInitialized = False
   End If
End Sub

此代码类似于构建组件时创建的 VBA 模块中生成的默认初始化代码。每个使用 MATLAB Compiler 组件的函数可以在开始时包含对 InitModule 调用,以确保始终根据需要执行初始化。

创建类的实例

在调用类方法(编译的 MATLAB 函数)之前,必须创建包含该方法的类的实例。VBA 提供了两种方法来实现这一点:

CreateObject 函数

此方法使用 Microsoft Microsoft Visual Basic 应用程序编程接口 (API) CreateObject 函数创建类的实例。Microsoft 将调用 CreateObject 称为后期绑定,将使用 new 称为早期绑定

要使用此方法,请使用 Object 声明一个 Dim 类型的变量来保存对类实例的引用,并使用类编程标识符 (CreateObject) 作为参量调用 ProgID,如下例所示:

Function foo(x1 As Variant, x2 As Variant) As Variant
       Dim aClass As Object
	 
       On Error Goto Handle_Error
       Set aClass = CreateObject("mycomponent.myclass.1_0") 
       ' (call some methods on aClass)
       Exit Function
Handle_Error:
   foo = Err.Description
End Function 

新运算符

此方法对明确指定为要创建的类的变量使用 Visual Basic New 运算符。在使用此方法之前,必须在当前 VBA 工程中引用包含该类的类型库。为此,请从 Visual Basic 编辑器中选择工具菜单,然后选择引用以显示可用引用列表。从此列表中,选择必要的类型库。

下面的示例说明如何使用 New 运算符创建类实例。假定在调用此函数之前,您已经从可用引用列表中选择了 mycomponent 1.0 类型库

Function foo(x1 As Variant, x2 As Variant) As Variant
   Dim aClass As mycomponent.myclass

   On Error Goto Handle_Error
   Set aClass = New mycomponent.myclass
   ' (call some methods on aClass)
   Exit Function
Handle_Error:
   foo = Err.Description
End Function 

在此示例中,类实例可以简单地被定义为 myclass。如果当前工程中的其他库包含名为 <component-name>.<class-name> 类型,则可以使用 . 形式的完整声明来防止发生名称冲突 myclass

同时使用 CreateObjectNew 来生成有维度的类实例。第一种方法不需要引用 VBA 工程中的类型库;第二种方法可以加快代码执行速度。第二种方法还有一个额外的好处,就是可以使用 Microsoft Visual Basic 编辑器的自动列出成员自动快速信息功能来处理您的类。每个构建组件创建的默认函数包装器都使用第一种方法来创建对象。

在前面的两个示例中,用于进行方法调用的类实例是过程的局部变量。这会为每次调用创建并销毁一个新的类实例。另一种方法是声明一个可由所有函数调用重用的单个模块范围的类实例,如上一个示例的初始化代码所示。

下面的示例用第二种方法说明了这种技术:

Dim aClass As mycomponent.myclass

Function foo(x1 As Variant, x2 As Variant) As Variant
   On Error Goto Handle_Error
   If aClass Is Nothing Then
      Set aClass = New mycomponent.myclass
   End If
   ' (call some methods on aClass)
   Exit Function
Handle_Error:
   foo = Err.Description
End Function

如何在类之间共享 MATLAB Runtime

当应用程序中第一个 MATLAB COM 类被实例化时,MATLAB Compiler 会创建一个 Microsoft Runtime 实例。该 MATLAB Runtime 在组件内的所有后续类实例之间重复使用和共享,从而提高了内存使用效率并消除了每个后续类实例中的 MATLAB Runtime 启动成本。

所有类实例共享单个 MATLAB 工作区,并共享用于构建组件的 MATLAB 文件中的全局变量。这使得 COM 类的属性表现为静态属性而不是实例属性。

调用类实例的方法

创建类实例后,您可以调用类方法来访问已编译的 MATLAB 函数。MATLAB Compiler 将原始 MATLAB 函数语法的标准映射应用于方法的参量列表。有关从 MATLAB 函数到 COM 类方法调用的映射的详细描述,请参阅引用实用工具类

当方法具有输出参量时,第一个参量始终是 nargout,其类型为 Long。该输入参数将正常的 MATLAB nargout 参数传递给编译的函数并指定请求多少个输出。没有输出参量的方法不会传递 nargout 参量。nargout 之后是输出参数,其列出的顺序与它们在原始 MATLAB 函数左侧的出现顺序相同。接下来是输入参数,其列出的顺序与原始 MATLAB 函数右侧的顺序相同。所有输入和输出参量均被输入为 Variant,即默认的 Visual Basic 数据类型。

Variant 类型可以保存任何基本 VBA 类型、任何类型的数组和对象引用。有关如何将任何基本类型的 Variant 类型与 MATLAB 数据类型相互转换的详细说明,请参阅数据转换规则。一般来说,可以将任何 Visual Basic 类型作为参量提供给类方法,但 Visual Basic UDT 除外。您还可以将 Microsoft Excel Range 对象直接作为输入和输出参量传递。

当您将简单的 Variant 类型作为输出参数传递时,被调用的方法会分配接收到的数据并释放 Variant 的原始内容。在这种情况下,将每个输出参量设置为单个 Variant 就足够了。当对象类型(如 Excel Range)作为输出参数传递时,对象引用会在两个方向上传递,并且对象的 Value 属性会接收数据。

下面的示例说明了将输入和输出参数从 VBA 传递到 MATLAB Compiler 组件类方法的过程。

第一个示例是一个接受两个输入并返回一个输出的公式函数。此函数将调用分派到与形式为 function y = foo(x1,x2) 的 MATLAB 函数相对应的类方法。

Function foo(x1 As Variant, x2 As Variant) As Variant
   Dim aClass As Object
   Dim y As Variant
   
   On Error Goto Handle_Error
   Set aClass = New mycomponent.myclass
   aClass = CreateObject("mycomponent.myclass.1_0")
   Call aClass.foo(1,y,x1,x2)
   foo = y
   Exit Function
Handle_Error:
   foo = Err.Description
End Function

第二个示例将相同的函数重写为子程序,并使用 Excel 范围进行输入和输出。

Sub foo(Rout As Range, Rin1 As Range, Rin2 As Range)
   Dim aClass As Object

   On Error Goto Handle_Error
   aClass = CreateObject("mycomponent.myclass.1_0")
   Call aClass.foo(1,Rout,Rin1,Rin2)
   Exit Sub
Handle_Error:
   MsgBox(Err.Description)
End Sub

具有可变参量的程序

处理 varargin 和 varargout 参量

当您用于 MATLAB 组件的 Excel 函数中存在 varargin 和/或 varargout 时,这些参数将作为列表中的最后一个输入/输出参数添加到类方法的参量列表中。您可以通过创建 varargin 数组将多个参量作为 Variant 数组传递,并将数组的每个元素分配给相应的输入参量。

下面的示例创建一个 varargin 数组来调用由 y = foo(varargin) 形式的 MATLAB 函数产生的方法:

Function foo(x1 As Variant, x2 As Variant, x3 As Variant, _ 
             x4 As Variant, x5 As Variant) As Variant 
   Dim aClass As Object 
   Dim v As Variant 
   Dim y As Variant 
   Dim MCLUtil As Object 
    
   On Error GoTo Handle_Error 
   set aClass = CreateObject("mycomponent.myclass.1_0") 
   Set MCLUtil = CreateObject("MWComUtil.MWUtil") 
   Call MCLUtil.MWPack(v, x1, x2, x3, x4, x5) 
   Call aClass.foo(1, y, v) 
   foo = y 
   Exit Function 
Handle_Error: 
   foo = Err.Description 
End Function 

MWUtil 实用工具库中包含的 MWComUtil 类提供了 MWPack 辅助函数来创建 varargin 参数。有关更多详细信息,请参阅类 MWUtil

下一个示例将 varargout 参数处理为三个单独的 Excel Range。该函数使用实用工具库中的 MWUnpack 函数。使用的 MATLAB 函数是 varargout = foo(x1,x2)

Sub foo(Rout1 As Range, Rout2 As Range, Rout3 As Range, _
        Rin1 As Range, Rin2 As Range)
   Dim aClass As Object
   Dim aUtil As Object
   Dim v As Variant
    
   On Error Goto Handle_Error
   aUtil = CreateObject("MWComUtil.MWUtil")
   aClass = CreateObject("mycomponent.myclass.1_0")
   Call aClass.foo(3,v,Rin1,Rin2)
   Call aUtil.MWUnpack(v,0,True,Rout1,Rout2,Rout3)
   Exit Sub
Handle_Error:
   MsgBox(Err.Description)
End Sub

Microsoft Visual Basic 代码传递空的 varargin

在 MATLAB 中,函数的 varargin 输入是可选的,可以在函数调用中出现或省略。但是,从 Microsoft Visual Basic 开始,函数签名更加严格 - 如果 MATLAB 函数输入中存在 varargin,则 VBA 调用必须包含 varargin,即使您希望它为空。要传入一个空的 varargin,请传递 Null 变量,该变量在传递时会转换为空的 MATLAB 元胞数组。

从 VBA 代码传递一个空的变量参数.  下面的示例说明了如何传递 null 变量以传递空的 varargin

Function foo(x1 As Variant, x2 As Variant, x3 As Variant, _ 
             x4 As Variant, x5 As Variant) As Variant 
   Dim aClass As Object 
   Dim v(1 To 5) As Variant 
   Dim y As Variant 
    
   On Error Goto Handle_Error 
   v(1) = x1 
   v(2) = x2 
   v(3) = x3 
   v(4) = x4 
   v(5) = x5 
   aClass = CreateObject("mycomponent.myclass.1_0") 

   'Call aClass.foo(1,y,v) 
   Call aClass.foo(1,y,Null) 

   foo = y 
   Exit Function 
Handle_Error: 
   foo = Err.Description 
End Function

更多详细信息

有关使用可变长度参量的详细信息,请参阅使用多个 MATLAB 函数创建宏

修改标志

每个 MATLAB Compiler 组件都公开一个名为 MWFlags 类型为 MWFlags 的读/写属性。MWFlags 属性由两组常量组成:数组格式标志数据转换标志数组格式标志会影响数组的转换,而数据转换标志则处理单个数组元素的类型转换。

数据转换标志将数据转换过程的选定行为从 Variant 更改为 MATLAB 类型,反之亦然。默认情况下,MATLAB Compiler 组件允许通过 MWFlags 类属性在类级别设置数据转换标志。这适用于所有 Visual Basic 类型,但 MATLAB CompilerMWStructMWFieldMWComplexMWSparseMWArg 类型除外。这些类型中的每一种都公开其自己的 MWFlags 属性,并忽略正在调用其方法的类的属性。MWArg 类专为特定参量需要与默认类属性不同的设置的情况而提供。

本节概述了如何设置这些标志以及它们的作用。有关 MWFlags 类型的详细讨论以及其他代码示例,请参阅类 MWFlags (MATLAB Compiler SDK)

数组格式化标志

数组格式化标志指导数据转换,从输入的一般 Variant 数据生成 MATLAB 元胞数组或矩阵,或者在输出时生成 Variant 数组或包含基本类型数组的单个 Variant

以下示例假设您已通过选择工具 > 引用并从列表中选择 MWComUtil 7.5 类型库来引用当前工程中的 MWComUtil 库:

Sub foo( )
   Dim aClass As mycomponent.myclass
   Dim var1(1 To 2, 1 To 2), var2 As Variant
   Dim x(1 To 2, 1 To 2) As Double
   Dim y1,y2 As Variant
   
   On Error Goto Handle_Error
   var1(1,1) = 11#
   var1(1,2) = 12#
   var1(2,1) = 21#
   var1(2,2) = 22#
   x(1,1) = 11
   x(1,2) = 12
   x(2,1) = 21
   x(2,2) = 22
   var2 = x
   Set aClass = New mycomponent.myclass
   Call aClass.foo(1,y1,var1)
   Call aClass.foo(1,y2,var2)
   Exit Sub
Handle_Error:
   MsgBox(Err.Description)
End Sub

此外,这些示例假设您已引用使用 MATLAB Compiler (mycomponent) 创建的 COM 对象(DLL 文件),如新运算符

这里,两个 Variant 变量 var1var2 由相同的数值数据构造,但它们的内部结构不同:var1 是一个 2×2 的 Variant 数组,其中每个元素包含一个 1×1 的 Double,而 var2 是一个 1×1 的 Variant 包含一个 2×2 的 Double 数组。

MATLAB Compiler 中,使用默认设置时,这两个数组都将转换为 2×2 的 double 数组。这不符合 COM VARIANT 中列出的 MATLAB 转换规则的一般约定。根据这些规则,var1 转换为一个 2×2 元胞数组,每个单元格由一个 1×1 双精度数占据,而 var2 直接转换为一个 2×2 双精度矩阵。

这两个数组都转换为双精度矩阵,因为 InputArrayFormat 标志的默认值是 mwArrayFormatMatrixInputArrayFormat 标志控制如何处理这两种类型的数组。使用这个默认值是因为来自 Excel 范围的数组数据总是采用 Variant 数组的形式(如上例中的 var1),而 MATLAB 函数最常处理矩阵参量。

但是,如果您需要元胞数组该怎么办?在这种情况下,应将 InputArrayFormat 标志设置为 mwArrayFormatCell。通过在创建类之后和方法调用之前添加以下行来执行此操作:

aClass.MWFlags.ArrayFormatFlags.InputArrayFormat = 
mwArrayFormatCell

设置此标志会将所有数组输入作为元胞数组呈现到编译的 MATLAB 函数中。

类似地,您可以使用 OutputArrayFormat 标志来操作输出参量的格式。您还可以使用 AutoResizeOutputTransposeOutput 标志修改数组输出。

AutoResizeOutput 用于直接作为输出参数传递的 Excel Range 对象。当设置此标志时,目标范围会自动调整大小以适合结果数组。如果未设置此标志,则目标范围必须至少与输出数组一样大,否则数据将被截断。

TransposeOutput 标志转置所有数组输出。处理输出一维数组的 MATLAB 函数时此标志很有用。默认情况下,MATLAB 将一维数组实现为 1×n 矩阵(行向量),这些矩阵成为 Excel 工作表中的行。

提示

例如,如果您的 MATLAB 函数专门返回行向量,请确保在 Excel 中分配类似的单元格行向量。

您可能更喜欢行向量输出的工作表列。此示例自动调整大小并转置输出范围:

Sub foo(Rout As Range, Rin As Range )
   Dim aClass As mycomponent.myclass

   On Error Goto Handle_Error
   Set aClass = New mycomponent.myclass
   aClass.MWFlags.ArrayFormatFlags.AutoResizeOutput = True
   aClass.MWFlags.ArrayFormatFlags.TransposeOutput = True
   Call aClass.foo(1,Rout,Rin)
   Exit Sub
Handle_Error:
   MsgBox(Err.Description)
End Sub

数据转换标志

数据转换标志处理单个数组元素的类型转换。两个数据转换标志 CoerceNumericToTypeInputDateFormat 控制如何将数字和日期类型从 VBA 转换为 MATLAB。假设有以下示例:

Sub foo( )
   Dim aClass As mycomponent.myclass
   Dim var1, var2 As Variant
   Dim y As Variant
   
   On Error Goto Handle_Error
   var1 = 1
   var2 = 2#
   Set aClass = New mycomponent.myclass
   Call aClass.foo(1,y,var1,var2)
   Exit Sub
Handle_Error:
   MsgBox(Err.Description)
End Sub

此示例将 var1 类型的 Variant/Integer 转换为 int16,将 var2 类型的 Variant/Double 转换为 double

如果原始 MATLAB 函数需要两个参量都是 double,则此代码可能会导致错误。一种解决方案是将 double 分配给 var1,但这可能不可行或不可取。在这种情况下,将 CoerceNumericToType 标志设置为 mwTypeDouble,导致数据转换器将所有数字输入转换为 double。在上面的示例中,在创建类之后和调用方法之前放置以下行:

aClass.MWFlags.DataConversionFlags.CoerceNumericToType = 
mwTypeDouble

InputDateFormat 标志控制如何转换 VBADate 类型。此示例将当前日期和时间作为输入参量发送并将其转换为字符串:

Sub foo( )
   Dim aClass As mycomponent.myclass
   Dim today As Date
   Dim y As Variant
   
   On Error Goto Handle_Error
   today = Now
   Set aClass = New mycomponent.myclass
   aClass. MWFlags.DataConversionFlags.InputDateFormat = 
mwDateFormatString
   Call aClass.foo(1,y,today)
   Exit Sub
Handle_Error:
   MsgBox(Err.Description)
End Sub

处理方法调用过程中的错误

创建类实例或类方法期间发生的错误会在当前过程中产生异常。Microsoft Microsoft Visual Basic 通过 On Error Goto <label> 语句提供异常处理功能,在该语句中程序执行跳转到 <label> 发生错误时。(<label> 必须与 On Error Goto 语句位于同一过程中)。所有错误都以这种方式处理,包括原始 MATLAB 代码中的错误。异常在当前上下文中名为 ErrObject 的变量中创建一个 Visual Basic Err 对象。(有关 VBA 错误处理的详细讨论,请参阅 Visual Basic for Applications 文档。)本节中的所有示例说明了 MATLAB Compiler 组件的函数调用包装器中使用的典型错误捕获逻辑。