3D PLM Enterprise Architecture |
Middleware Abstraction - Object Modeler |
Implementing Interfaces for AutomationMaking your components available from scripting languages |
Technical Article |
AbstractThis article explains how to make your interfaces available from scripting languages, such as Visual Basic or JScript. It is a programmer's guide (how to) to show which statements to include for Automation, and which gives you a check-list of the rules to observe. |
We define how to code operations to implement interfaces [1]. IDL operations are turned into methods (C++ purists name them functions.) Nevertheless, we do deal neither with parameters nor with data members. Some additional information is given here to make good dispatch interfaces and how the IDL language is related with the C++ abstract classes generated.
The implementation classes you'll create should derive from CATBaseObject, except collections which should derive from CATBaseCollection.
[Top]
All methods must return an HRESULT
value [2].
[Top]
Method parameter types must be coded with IDL in the method signature with a
directional attribute. An input parameter is declared with the in
directional attribute, while an output parameter is declared with the out
directional attribute. A parameter passed in both directions is declared with inout
.
For example:
HRESULT MyExposedMethod(in <type> iparameter, out <type> oparameter, inout /*IDLRETVAL*/ <type> ioparameter); |
The supported types and their C++ and VBScript correspondences are shown in the following tables:
Note:
The IDL file doesn't include the *
pointer declarator or the &
reference declarator.
Optional parameters cannot be used.
Several output parameters can be used, but only one output parameter can be used
with /*IDLRETVAL*/
with Visual Basic. It must be the last parameter
in the signature. A method declared like this, with oArg4
declared
using /*IDLRETVAL*/
in the IDL file:
HRESULT DoThis(in short iArg1, in short iArg2, in short iArg3, out /*IDLRETVAL*/ short oArg4); |
has the following signature in C++:
HRESULT DoThis(short iArg1, short iArg2, short iArg3, short & oArg4); |
It is called like this in C++
HRESULT rc = Obj->DoThis(iArg1, iArg2, iArg3, & oArg4); |
but like this in Visual Basic or Basic Script:
oArg4 = Obj.DoThis(iArg1, iArg2, iArg3) |
[Top]
Some methods are often used as accessors (also named setter/getter) to set
and get the value of a data. For example, the SetName
method of the
CATCommand class sets the name of the MyCommand instance, while the GetName
method retrieves this name from the MyCommand instance data members. This can be
used as follows:
// C++ if (MyCommand->GetName() == "") { MyCommand->SetName(CATString("Nice Command Name")); } |
This makes it possible to encapsulate data members and protects client applications from implementation changes. With Visual Basic, the goal is quite different. Everything must be as easy as possible for the client application programmer, and data is manipulated through properties, equivalent to the Set/Get methods, that Visual Basic handles as variables.
// VBScript If MyCommand.Name = "" Then MyCommand.Name = "Nice Command Name" End If |
Such a property is declared using the #pragma PROPERTY
statement
in the IDL file:
// IDL #pragma PROPERTY PropertyName HRESULT get_PropertyName (out /*IDLRETVAL*/ <type> oPropertyValue); HRESULT put_PropertyName (in <type> iPropertyValue); |
The #pragma PROPERTY
statement declares the property name, that
is the name dedicated to Visual Basic to handle this property, and the two
following statements declare the methods to retrieve this property value (get_PropertyName
)
and to set it (put_PropertyName
). If the property type is CATBSTR,
the parameter of the get_PropertyName
method must be set as inout.
To declare a read/only property, simply remove the put_PropertyName
method. The example of the CATCommand class for its Name property becomes:
// IDL #pragma PROPERTY Name HRESULT get_Name (inout /*IDLRETVAL*/ CATBSTR oName); HRESULT put_Name (in CATBSTR iName); |
This implies that no identifier must begin with get_
or with put_
.
With C++, such methods become:
HRESULT get_Name (CATBSTR & oName); HRESULT put_Name (CATBSTR iName); |
The supported types and their C++ and VBScript correspondences are shown in the following tables:
Note:
Note the lack of CATSafeArrayVariant support.
[Top]
Visual Basic uses BSTR
character strings, which stand for basic
string, or binary string. CAA V5 uses the CATUnicodeString class to handle
character strings without language dependency, and the CATString class for non
translatable character strings. The use of char * is prohibited, but you can
nevertheless find some char * as method return values. To enable the use of BSTR
with CATUnicodeString instances, CAA V5 supplies also the CATBSTR class, and the
CATUnicodeString class offers the BuildFromBSTR
and ConvertToBSTR
methods.
Declare character string types as CATBSTR in the IDL files, and use these methods as follows in your C++ source files:
CATBSTR StringForVisualBasic = ...; CATUnicodeString * StringForCAAV5; int rc = StringForCAAV5->BuildFromBSTR(StringForVisualBasic); ... StringForCAAV5->ConvertToBSTR(&StringForVisualBasic); |
In addition, the CATFreeString
function deallocates a CATBSTR
instance:
CATFreeString(StringForVisualBasic); |
Let's take the example of the command name we saw above in Properties. We assume here that the command methods GetName and PutName manage the access to the command name data member, this name being a CATUnicodeString class instance. The accessor methods could be coded in C++ as follows:
HRESULT CATCommand::get_Name(CATBSTR & oName) { CATUnicodeString name = this->GetName(); name.ConvertToBSTR(& oName); return S_OK; } HRESULT CATCommand::put_Name(CATBSTR iName) { CATUnicodeString name; name.BuildFromBSTR(iName); this->PutName(name); return S_OK; } |
The name could be stored as a char *. Even if you know this never should happen, the methods become in this case:
HRESULT CATCommand::get_Name(CATBSTR & oName) { CATUnicodeString name = (const char *) this->GetName(); name.ConvertToBSTR(& oName); return S_OK; } HRESULT CATCommand::put_Name(CATBSTR iName) { CATUnicodeString name; name.BuildFromBSTR(iName); this->PutName(name.ConvertToChar()); return S_OK; } |
If the name is stored as a CATString class instance, which is more common, the methods become:
HRESULT CATCommand::get_Name(CATBSTR & oName) { const CATString * string = (const CATString) this->GetName(); CATUnicodeString name = (const char *) string.CastToCharPtr(); name.ConvertToBSTR(& oName); return S_OK; } HRESULT CATCommand::put_Name(CATBSTR iName) { CATUnicodeString name; name.BuildFromBSTR(iName); CATString * string = (CATString *) name.ConvertToChar()); this->PutName(string); return S_OK; } |
This is a bit more difficult, since there is no direct conversion between CATString and CATUnicodeString, and we need to use char * between them.
[Top]
A CATVariant parameter should be used whenever you don't know the type of the parameter that Visual Basic will use, or if this type may vary. For example, the Item method of most collection objects takes a CATVariant as input parameter to locate and retrieve a given object in the collection item. This parameter may be either the rank of the object in the collection passed as an integer, or the name of this object, and then passed as a CATBSTR. The same type CATVariant accommodates these two types.
A CATVariant parameter is a VARIANT parameter for Visual Basic, and corresponds to the any type of the OMG IDL, but must be created as CATVariant in your IDL file.
With C++, you must use a CATVariant variable for method parameters in order to be compliant with the C++ abstract classes generated by the IDL compiler, but you need to convert them to and from the any type to process them in your code. Two global functions are dedicated to convert an any variable to a CATVariant and the reverse.
HRESULT ConvertAnyToVariant(const any & iAnyToConvert, CATVariant * oVariantToCreateFromAny); any * BuildAnyFromVariant(const CATVariant & iVariantToConvert); |
Use these functions as follows:
... HRESULT rc; CATVariant * oVariantToCreateFromAny; rc = ::ConvertAnyToVariant(const any & iAnyToConvert, CATVariant * oVariantToCreateFromAny); if (SUCCEEDED(rc)) { ... } ... any * anyToCreateFromVariant; anyToCreateFromVariant = ::BuildAnyFromVariant(iVariantToConvert); ... |
Assume the following scenario. The CATVariant is an input parameter of your
IDL method, and thus an input parameter of the corresponding C++ method. You
expect that this CATVariant holds either an integer or a character string. You
can use the ConvertVariant
global functions
... HRESULT MyClass:MyMethod(CATVariant iValueWithUnknownType) { HRESULT rc = E_FAIL; // try to convert the input CATVariant into a short short IntValue; rc = ::ConvertVariant(iValueWithUnknownType, IntValue); if SUCCEEDED(rc) { // go on, conversion succeeds } else { // try to convert the input CATVariant into a CATUnicodeString CATUnicodeString StringValue; rc = ::ConvertVariant(iValueWithUnknownType, StringValue); if SUCCEEDED(rc) { // go on, conversion succeeds } else { // process the error } } ... return rc; } |
[Top]
If you use a table as a parameter of an interface method, you must use the CATSafeArray class. There is one CATSafeArray set of Build, Convert and Free methods per type. For example, the following methods exist for long integers:
HRESULT FreeVariantSafeArray(CATSafeArrayVariant * iArray); CATSafeArray * BuildSafeArrayVariant(const long *iLongArray, long iSize); HRESULT ConvertSafeArrayVariant(CATSafeArrayVariant * iSafeArray, long * oongArray, short iSize); |
Such methods exist for long
, float
, short
,
double
, CATBSTR, CATBaseDispatch, char
, octet
and boolean
.
[Top]
Implementing your interfaces for Automation implies that your methods return a HRESULT, that your method parameters are qualified as in, out, or inout in the IDL file, that properties are declared using a #pragma, and that you correctly declare and use the character strings, the tables, and the CATVariants.
[Top]
[1] | Creating Interfaces for Automation |
[2] | What Is HRESULT? |
[3] | The CAA V5 IDL Compiler |
[Top] |
[Top]
Version: 1 [May 2000] | Document created |
[Top] |
Copyright © 2000, Dassault Systèmes. All rights reserved.