Integrate Components Using VBA
When to Use Formula Functions or Subroutines
VBA provides two basic procedure types: functions and subroutines.
You access a VBA function directly from a cell in a worksheet as a formula function. Use function procedures when the original MATLAB® function returns one or no outputs.
You access a subroutine as a general macro. Use a subroutine procedure when the original MATLAB function returns an array of values or multiple outputs, because you need to map these outputs into multiple cells/ranges in the worksheet.
When you create a component, MATLAB
Compiler™ produces a VBA module (.bas
file). This file contains
simple call wrappers, each implemented as a function procedure for each method of the
class. For more information, see How Excel Add-In Compilation Works.
Initialize MATLAB Compiler Libraries with Microsoft Excel
Before you use any MATLAB Compiler component, initialize the supporting libraries with the current instance of Microsoft® Excel®. Do this once for an Excel session that uses the MATLAB Compiler components.
To do this initialization, call the utility library function
MWInitApplication
, which is a member of the MWUtil
class. This class is part of the MWComUtil
library. For details, see
Class MWUtil.
One way to add this initialization code into a VBA module is to provide a subroutine
that does the initialization once, and simply exits for all subsequent calls. The following
Microsoft
Visual Basic® code sample initializes the libraries with the current instance of
Excel. A global variable of type Object
named
MCLUtil
holds an instance of the MWUtil
class, and
another global variable of type Boolean
named
bModuleInitialized
stores the status of the initialization process.
The private subroutine InitModule()
creates an instance of the
MWComUtil
class and calls the MWInitApplication
method with an argument of Application
. Once this function succeeds, all
subsequent calls exit without reinitializing.
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
This code is similar to the default initialization code generated in the VBA module
created when the component is built. Each function that uses MATLAB
Compiler components can include a call to InitModule
at the
beginning to ensure that the initialization always gets performed as needed.
Create an Instance of a Class
Before calling a class method (compiled MATLAB function), you must create an instance of the class that contains the method. VBA provides two techniques for doing this:
CreateObject
functionNew
operator
CreateObject Function
This method uses the Microsoft
Visual Basic application programming interface (API) CreateObject
function to create an instance of the class. Microsoft refers to calling CreateObject as late binding and
using new
as early binding.
To use this method, declare a variable of type Object
using
Dim
to hold a reference to the class instance and call
CreateObject
using the class programmatic identifier
(ProgID
) as an argument, as shown in the next example:
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
New Operator
This method uses the Visual Basic
New
operator on a variable explicitly dimensioned as the class to be
created. Before using this method, you must reference the type library containing the
class in the current VBA project. Do this by selecting the Tools menu from the Visual Basic Editor, and then selecting References to
display the Available References list. From this
list, select the necessary type library.
The following example illustrates using the New
operator to create
a class instance. It assumes that you have selected mycomponent
1.0 Type Library from the Available References
list before calling this function.
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
In this example, the class instance can be dimensioned as simply
myclass
. The full declaration in the form
<component-name>.<class-name>
guards against name
collisions that can occur if other libraries in the current project contain types named
myclass
.
Using both CreateObject
and New
produce a
dimensioned class instance. The first method does not require a reference to the type
library in the VBA project; the second results in faster code execution. The second
method has the added advantage of enabling the Auto-List-Members and Auto-Quick-Info
capabilities of the Microsoft
Visual Basic editor to work with your classes. The default function wrappers created
with each built component all use the first method for object creation.
In the previous two examples, the class instance used to make the method call was a local variable of the procedure. This creates and destroys a new class instance for each call. An alternative approach is to declare one single module-scoped class instance that is reused by all function calls, as in the initialization code of the previous example.
The following example illustrates this technique with the second method:
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
How MATLAB Runtime Is Shared Among Classes
MATLAB Compiler creates a single MATLAB Runtime instance when the first Microsoft COM class is instantiated in an application. This MATLAB Runtime is reused and shared among all subsequent class instances within the component, resulting in more efficient memory usage and eliminating the MATLAB Runtime startup cost in each subsequent class instantiation.
All class instances share a single MATLAB workspace and share global variables in the MATLAB files used to build the component. This makes properties of a COM class behave as static properties instead of instance-wise properties.
Call the Methods of a Class Instance
After you have created a class instance, you can call the class methods to access the compiled MATLAB functions. MATLAB Compiler applies a standard mapping from the original MATLAB function syntax to the method's argument list. For a detailed description of the mapping from MATLAB functions to COM class method calls, see Reference Utility Classes.
When a method has output arguments, the first argument is always
nargout
, which is of type Long
. This input
parameter passes the normal MATLAB
nargout
parameter to the compiled function and specifies how many
outputs are requested. Methods that do not have output arguments do not pass a
nargout
argument. Following nargout
are the output
parameters listed in the same order as they appear on the left side of the original
MATLAB function. Next come the input parameters listed in the same order as they
appear on the right side of the original MATLAB function. All input and output arguments are typed as
Variant
, the default Visual Basic data type.
The Variant
type can hold any of the basic VBA types, arrays of any
type, and object references. For a detailed description of how to convert
Variant
types of any basic type to and from MATLAB data types, see Data Conversion Rules. In general, you can supply any
Visual Basic type as an argument to a class method, with the exception of Visual Basic
UDT
s. You can also pass Microsoft
Excel
Range
objects directly as input and output arguments.
When you pass a simple Variant
type as an output parameter, the
called method allocates the received data and frees the original contents of the
Variant
. In this case it is sufficient to dimension each output
argument as a single Variant
. When an object type (like an Excel
Range
) is passed as an output parameter, the object reference is passed
in both directions, and the object's Value
property receives the data.
The following examples illustrate the process of passing input and output parameters from VBA to the MATLAB Compiler component class methods.
The first example is a formula function that takes two inputs and returns one output.
This function dispatches the call to a class method that corresponds to a MATLAB function of the form function
y = foo(x1,x2)
.
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
The second example rewrites the same function as a subroutine and uses Excel ranges for input and output.
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
Program with Variable Arguments
Process varargin and varargout Arguments
When varargin
and/or varargout
are present in
the MATLAB function that you are using for the Excel component, these parameters are added to the argument list of the class
method as the last input/output parameters in the list. You can pass multiple arguments
as a varargin
array by creating a Variant
array,
assigning each element of the array to the respective input argument.
The following example creates a varargin
array to call a method
resulting from a MATLAB function of the form
y = foo(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 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
The MWUtil
class included in the MWComUtil
utility library provides the MWPack
helper function to create
varargin
parameters. See Class MWUtil for more details.
The next example processes a varargout
parameter into three
separate Excel
Range
s. This function uses the MWUnpack
function
in the utility library. The MATLAB function used is
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
Pass an Empty varargin from Microsoft Visual Basic Code
In MATLAB, varargin
inputs to functions are optional, and may be
present or omitted from the function call. However, from Microsoft
Visual Basic, function signatures are more strict—if varargin
is present among the MATLAB function inputs, the VBA call must include varargin
,
even if you want it to be empty. To pass in an empty varargin
, pass
the Null
variant, which is converted to an empty MATLAB cell array when passed.
Pass an Empty varargin from VBA Code. The following example illustrates how to pass the null variant in order to pass an
empty 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
For More Information
For more information about working with variable-length arguments, see Create Macro Using Multiple MATLAB Functions.
Modify Flags
Each MATLAB
Compiler component exposes a single read/write property named
MWFlags
of type MWFlags
. The
MWFlags
property consists of two sets of constants: array formatting flags and data conversion flags. Array formatting flags
affect the transformation of arrays, whereas data conversion flags
deal with type conversions of individual array elements.
The data conversion flags change selected behaviors of the data conversion process from
Variant
s to MATLAB types and vice versa. By default, the MATLAB
Compiler components allow setting data conversion flags at the class level through the
MWFlags
class property. This holds true for all Visual Basic types, with the exception of the MATLAB
Compiler
MWStruct
, MWField
, MWComplex
,
MWSparse
, and MWArg
types. Each of these types
exposes its own MWFlags
property and ignores the properties of the class
whose method is being called. The MWArg
class is supplied specifically
for the case when a particular argument needs different settings from the default class
properties.
This section provides a general discussion of how to set these flags and what they do.
For a detailed discussion of the MWFlags
type, as well as additional
code samples, see Class MWFlags (MATLAB Compiler SDK).
Array Formatting Flags
Array formatting flags guide the data conversion to produce either a MATLAB cell array or matrix from general Variant
data on input
or to produce an array of Variant
s or a single
Variant
containing an array of a basic type on output.
The following examples assume that you have referenced the
MWComUtil
library in the current project by selecting Tools > References and selecting MWComUtil 7.5 Type Library from the list:
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
In addition, these examples assume you have referenced the COM object (DLL file)
created with MATLAB
Compiler (mycomponent
) as mentioned in New Operator.
Here, two Variant
variables, var1
and
var2
are constructed with the same numerical data, but internally
they are structured differently: var1
is a 2-by-2 array of
Variant
s with each element containing a 1-by-1
Double
, while var2
is a 1-by-1
Variant
containing a 2-by-2 array of Double
s.
In MATLAB
Compiler, when using the default settings, both of these arrays will be converted
to 2-by-2 arrays of double
s. This does not follow the general
convention listed in COM VARIANT to the MATLAB Conversion Rules. According to these rules, var1
converts to a 2-by-2 cell array with each cell occupied by a 1-by-1 double, and
var2
converts directly to a 2-by-2 double matrix.
The two arrays both convert to double matrices because the default value for the
InputArrayFormat
flag is mwArrayFormatMatrix
.
The InputArrayFormat
flag controls how arrays of these two types are
handled. This default is used because array data originating from Excel ranges is always in the form of an array of Variant
s
(like var1
of the previous example), and MATLAB functions most often deal with matrix arguments.
But what if you want a cell array? In this case, you set the
InputArrayFormat
flag to mwArrayFormatCell
. Do
this by adding the following line after creating the class and before the method
call:
aClass.MWFlags.ArrayFormatFlags.InputArrayFormat = mwArrayFormatCell
Setting this flag presents all array input to the compiled MATLAB function as cell arrays.
Similarly, you can manipulate the format of output arguments using the
OutputArrayFormat
flag. You can also modify array output with the
AutoResizeOutput
and TransposeOutput
flags.
AutoResizeOutput
is used for Excel
Range
objects passed directly as output parameters. When this flag is
set, the target range automatically resizes to fit the resulting array. If this flag is
not set, the target range must be at least as large as the output array or the data is
truncated.
The TransposeOutput
flag transposes all array output. This flag is
useful when dealing with MATLAB functions that output one-dimensional arrays. By default, MATLAB realizes one-dimensional arrays as 1-by-n matrices (row vectors) that
become rows in an Excel worksheet.
Tip
If your MATLAB function is specifically returning a row vector, for example, ensure you assign a similar row vector of cells in Excel.
You may prefer worksheet columns from row vector output. This example auto-resizes and transposes an output range:
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
Data Conversion Flags
Data conversion flags deal with type conversions of individual array elements. The
two data conversion flags, CoerceNumericToType
and
InputDateFormat
, govern how numeric and date types are converted
from VBA to MATLAB. Consider the example:
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
This example converts var1
of type
Variant/Integer
to an int16
and
var2
of type Variant/Double
to a
double
.
If the original MATLAB function expects double
s for both arguments, this code
might cause an error. One solution is to assign a double
to
var1
, but this may not be possible or desirable. In such a case
set the CoerceNumericToType
flag to mwTypeDouble
,
causing the data converter to convert all numeric input to double
. In
the previous example, place the following line after creating the class and before
calling the methods:
aClass.MWFlags.DataConversionFlags.CoerceNumericToType = mwTypeDouble
The InputDateFormat
flag controls how the VBA
Date
type is converted. This example sends the current date and
time as an input argument and converts it to a string:
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
Handle Errors During a Method Call
Errors that occur while creating a class instance or during a class method create an
exception in the current procedure. Microsoft
Visual Basic provides an exception handling capability through the On Error Goto
<label>
statement, in which the program execution jumps to
<label>
when an error occurs. (<label>
must be located in the same procedure as the On Error Goto
statement).
All errors are handled this way, including errors within the original MATLAB code. An exception creates a Visual Basic
ErrObject
object in the current context in a variable called
Err
. (See the Visual Basic for Applications documentation for a detailed discussion on VBA error
handling.) All of the examples in this section illustrate the typical error trapping logic
used in function call wrappers for MATLAB
Compiler components.