3D PLM Enterprise Architecture |
Middleware Abstraction - Object Modeler |
Creating Interfaces for AutomationMaking your interfaces available from scripting languages |
Technical Article |
AbstractAutomation Interfaces are special interfaces that allow CAA V5 programming from simple scripting languages and macro recording and replay. In this article, we describe what these interfaces require from a technical standpoint, as well as the proper methodology to create them.. |
Declaring objects by means of interfaces is nice and the benefits that we could get from this are the following: objects are accessed by client applications through their interfaces, and actual implementations are never directly manipulated. Interface inheritance, implementation inheritance, and extensions are key features that make the CAA V5 model powerful, versatile, incremental, which enable to customize and extend the objects it provides, as well as enable data distribution.
Another key topic is to enable them for Automation and for journalling. Automation means that interfaces can be accessed from a scripting language and thus be used by user scripts. Journalling means recording a script from an interactive user scenario. This script can be replayed as is, or after modifications, to automate repetitive tasks. Interfaces making this possible are called CAA V5 interfaces for Automation. This is possible thanks to the interface exposition, which makes it possible to access CAA V5 interface and their methods from macros written in interpreted languages such as Visual Basic, VB Script, JScript, and Basic Script. The CAA V5 interfaces for Automation have this ability because the signatures of their methods have specific limitations to match scripting language calling possibilities.
CAA V5 runs on Windows and hosts the scripting engine for VBScript. It runs also on UNIX and hosts the scripting engine for Basic Script. Other scripting engines can be used out-process for Automation with Windows. But for journalling, only the VB syntax is currently available, and it is Basic Script that is generated in journalized macros. These scripting languages are not fully compatible. In order to make interfaces that can be called in both environments, and to create macros on Windows that can be run on UNIX or the reverse, the rules stated in this article must strictly be observed. Otherwise, macro portability cannot be guaranteed.
[Top]
Up to now, we have declared interfaces in such a way that they can take advantage of the CAA V5 model features, but we did nothing for exposition. Let's call these interfaces standard interfaces. Two other kinds of interfaces deal with interface exposition, explained below in capability growing order:
From the client application programmer standpoint, there is no difference between a standard interface and an interface belonging to one of the two kinds above. However, exposed and journalizable interface IDL files include CAA V5 pragma directives that request to generate a type library used by scripting languages to retrieve these interfaces and execute the code of their methods to which they point.
[Top]
The base interface for exposed and journalizable interfaces is IDispatch,
supplied by CAA V5 for UNIX and by the Component Object Model (COM) for Windows.
IDispatch derives from IUnknown and provides the following methods, as shown in
the IDispatch.idl
file:
#include "IUnknown.idl" #pragma ID IDispatch "DCE:00020400-0000-0000-C000000000000046" interface IDispatch { HRESULT GetTypeInfoCount(out unsigned long oNumberOfTypeInfo); HRESULT GetTypeInfo(in unsigned long iindex, in unsigned long ilangId, out unsigned long opTypeInfo); HRESULT GetIDsOfNames(in IID forFuture, in WCHAR iArrayOfNames, in unsigned long iNbNames, in unsigned long iLangId, out long oArrayOflong); HRESULT Invoke(in long ilongMember, in IID forFuture, in unsigned long iLangId, in unsigned short iFlags, in DISPPARAMS iPdisparams, in unsigned long oPvaresult, in EXCEPINFO oPexcepinfo, in unsigned long oPuArgErr); }; |
The IDispatch interface supplied by CAA V5 is exactly the same that the one
of COM, making interfaces portable from UNIX to Windows for OLE and the reverse.
The CATBaseDispatch interface derives from IDispatch, since both CAA V5 and
Windows use a #define
statement to see this interface as a
structure to gain inheritance capabilities.
#define interface struct
With Windows, the CATBaseDispatch class provides an implementation for GetTypeInfoCount
,
GetTypeInfo
, GetIdsOfNames
and Invoke
,
exposed as pure virtual functions in the IDispatch.h
header file,
avoiding to implement them whenever you implement an exposable, exposed or
journalizable interface and thus contributing to code factorization.
Let's have a look at GetIdsOfNames
and Invoke
.
Given a method name, GetIdsOfNames
returns its dispatch ID, or
DISPID, which is a long integer (and not a GUID [1]).
The DISPID is the identifier of the method inside the interface. Invoke
uses then the DISPID as first argument to call the concerned method. This is
called late binding, and is a run time binding, because the
actual method to call is determined at run time using a call to GetIdsOfNames
and not thanks to a build time mechanism. This matches the need of interpreted
languages.
It is quite different with a build time mechanism, such as the one used with C++. Interfaces are represented using abstract classes. An abstract class is represented in memory as an area accessible using a pointer, which is the interface pointer. This pointer gives access to a table storing pointers to the methods of the TIE object that ties the interface to its current implementation. This table is called vtbl, short for class virtual function table. When the client calls a method using an interface pointer, this pointer is the pointer to the vtbl of the interface base class, and the appropriate method is called thanks to a shift of the appropriate offset to locate the pointer to this method. The vtbl is created when the abstract class is built, and the call in the client application is transformed into the appropriate call instruction when the client is compiled. So everything is created at build time. This is known as the vtbl binding, and is a build time binding.
vtbl binding is much faster than late binding in common situations, namely when objects are not distributed.
The CATIABase interface derives from the CATBaseDispatch interface and is the base interface from which all the interfaces you will publish must derive if you want to make them exposed or journalizable, except your collection interfaces that you must derive from CATIACollection.
The CATIABase interface is implemented by the CATBaseObject class, for two purposes:
ObjFromId
method to
determine reference to objects the first time these objects are metget_Application
to retrieve the application objectget_Parent
to retrieve the object's parent objectget_Name
to retrieve the object's nameGetItem
to manage unresolved objects.The CATIACollection interface is implemented by the CATBaseCollection class and provides the following method implementations:
get_Application
to retrieve the application objectget_Parent
to retrieve the object's parent objectGetItem
to manage unresolved objects.[Top]
To provide type information at run time for the scripts, COM offers the type library. This makes it possible for the interpreted language to access the methods by means of the virtual function table of the object and to ensure run time type checking like C++ run time does.
The type library is a compiled version of a set of IDL files. It contains the description of all the interfaces, all the method prototypes, properties, and all the parameters they require along with their types. It is built using the CAA V5 IDL compiler [2].
You can scan a type library thanks to the OLE/COM Object Viewer with Windows.
[Top]
All the CAA V5 interfaces are coded using the IDL, acronym for the Interface Definition Language. Some CAA V5 pragmas enable to declare in the IDL file the kind of the interface described and the objects to build.
[Top]
They derive from CATIABase and include in their IDL file a ID declaration.
#pragma ID CATIAExposed "DCE:7c1b4ba8-5c25-0000-0280020bcb000000" #pragma DUAL CATIAExposed #pragma ID AccessName "DCE:73a1e08a-d8c4-11d0-a59f00a0c9575177" #pragma ALIAS CATIAExposed AccessName interface CATIAExposed : CATIABase { ... }; |
An exposed interface must be declared in the type library.
[Top]
To make a journalizable interface from an exposed interface, just add the
comment /*IDLREP*/
in the IDL file, preferably as the first line of
the file.
/*IDLREP*/ #pragma ID CATIJournalizable "DCE:7c1b4ba8-5c25-0000-0280020bcb000000" #pragma DUAL CATIJournalizable #pragma ID AccessName "DCE:73a1e08a-d8c4-11d0-a59f00a0c9575177" #pragma ALIAS CATIJournalizable AccessName interface CATIJournalizable : CATIABase ... }; |
As an exposed interface, a journalizable interface must be declared in the type library.
[Top]
If you want to take advantage of Automation capabilities, you need to declare
the interfaces in a type library source file, suffixed by tplib
.
This is an example of such a file:
/*IDLREP*/ #pragma REPID INFITF "DCE:14f197b2-0771-11d1-a5b100a0c9575177" #pragma REPBEGIN INFITF #pragma REPREQ PreReqTypeLib #include "CATIApplication.idl" #include "CATIPageSetup.idl" #include "CATIWindow.idl" #pragma REPEND INFITF |
where:
INFITF
is the type library nameCATIApplication.idl
, CATIPageSetup.idl
and CATIWindow.idl
the files containing the interfacesREPID
, REPBEGIN
and REPEND
are
keywords to declare respectively the type library name and its GUID, the
beginning and the end of the interfaces to include in the type libraryREPREQ
declares the prerequisite type libraries.The IDL compiler [2] builds the run time type library from such a source file and stores it in a shared library with UNIX or a DLL with Windows.
[Top]
[Top]
A property is equivalent to a data member in C++. A property is accessed from scripting languages using the property name, but the IDL file includes two functions with the reserved prefixes get_ and put_ to get the property value and to set it respectively. These functions have one and only one argument.
[Top]
These rules deal with the types, the way complex types are handled, the interface inheritance, and the signature types.
[Top]
Compared to C++, scripting languages have parameter type support restrictions. The parameters of the method signatures must be of the following types.
Exposed Type Name | Type Description | CAA V5 IDL Syntax for that Type |
---|---|---|
Boolean | Can take the two values True and False | boolean |
Integer | Signed integer coded using 16 bits | short |
Long | Signed integer coded using 32 bits | long |
Single | Floating number coded using 32 bits | float |
Double | Floating number coded using 64 bits | double |
BSTR | Character string coded using the Unicode | CATBSTR |
Variant | Can contain any of the above types | CATVariant |
SafeArray(Variant) | Variant array. This type can be used as an input, or as an input/output parameter only | CATSafeArrayVariant |
Object | OLE Automation interface | <interface name> |
Error code | Error code coded on 32 bits | HRESULT |
Enum | Enumeration | Enum |
The following OLE Automation types are not supported
Exposed Type Name | Type Description |
Byte | Non signed integer coded using 32 bits |
Currency | Amount expressed in a given currency |
SafeArray(<type>) | Array of <type>, where type is not Variant |
Decimal | High precision decimal number coded using 16 bytes |
Date | Date |
[Top]
The complex types, that is BSTR, SafeArray(Variant), and Variant are never
handled directly, but only using the CAA V5 types, that is CATUnicodeString,
any[], and any respectively. Functions to convert from/to these types are
provided. For example, the CATUnicodeString class provides the BuildFroBSTR
method to build a CATUnicodeString class instance from a BSTR, and ConvertToBSTR
to convert a CATUnicodeString class instance into a BSTR. The
CATAutoConversions.h file provides global functions to convert Variants.
[Top]
All the exposed interfaces must derive from one out of the two following interfaces:
These interfaces expose methods to match some of the scripting engine requirements. This is explained in IDispatch, CATBaseDispatch and CATIABase.
[Top]
The 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, inout <type> ioparameter); |
MyExposedMethod
has an in parameter and an inout parameter.
[Top]
The methods exposed in an interface fall among one of the four following types:
get_
.
This prefix is dedicated to property reading functions and must not be used
for any other function. Property reading functions have a single parameter
preceded by out /*IDLRETVAL*/
which returns the read property
value. Their signatures is as follows:
HRESULT get_<propertyName> ( (out /*IDLRETVAL*/) <type> <parameterName> ); |
For example:
HRESULT get_FileName(out/*IDLRETVAL*/ CATBSTR oString); |
put_
.
This prefix is dedicated to property writing functions and must not be used
for any other function. Property writing functions have a single parameter
preceded by in
which passes the property value. Their
signatures is as follows:
HRESULT put_<propertyName> ( (in) <type> <parameterName> ); |
For example:
HRESULT put_FileName(in CATBSTR iString); |
HRESULT <subProcName> ( ((in|out|inout) <type> <parameterName>) * ); |
For example:
HRESULT Update(in iTimeStamp); |
out /*IDLRETVAL*/
and
must be the last parameter in the signature.
HRESULT <functionProcName> ( ((in|out|inout) <type> <parameterName>)* , out /*IDLRETVAL*/ <type> <returnedParameterName>); |
For example:
HRESULT GetLastItemInList(in CATIACollection iList, out /*IDLRETVAL*/ CATIABase oItem); |
You cannot put two methods with the same name and different signatures in the same interface: method overloading is not supported.
[Top]
A read/write property is created by declaring the property using a #pragma, and a couple of get_/put_ methods. For example:
#pragma PROPERTY Name HRESULT get_Name (out /*IDLRETVAL*/ CATBSTR oNameValue); HRESULT put_Name (in CATBSTR iNameValue); |
The Name property is declared using the #pragma PROPERTY. The two methods show that is is a read/write property. The get_Name method parameter is declared as out and /*IDLRETVAL*/ forces the parameter as a return value for the scripting function.
A read/only property is created by declaring the property using a #pragma, and a single get_ method. For example:
#pragma PROPERTY ActiveDocument HRESULT get_ActiveDocument (out /*IDLRETVAL*/ CATIADocument oDocument); |
The Name property is declared using the #pragma PROPERTY. The get_ method shows that it is a read/only property. As for a read/write property, the get_Name method parameter is declared as out and /*IDLRETVAL*/ forces the parameter as a return value for the scripting function.
[Top]
Refer to the following check-list to make sure that the interfaces you write will match IDL, COM, CORBA, and CAA V5 rules. This check-list includes Visual Basic compatibility requirements.
idl
. Example: MyInterface.idl
/*IDLREP*/ |
HRESULT
in
, out
or inout
parameter
in <type>
means for C++ <type> *
if
type is an interface
out <type>
means for C++ <type> * &
if type is an interface or <type> &
if type is a base
type.
put_
or get_
,
except for propertiesint
and void *
types are not available
#pragma PROPERTY PropertyName HRESULT get_PropertyName (out /*IDLRETVAL*/ <type> oPropertyValue); HRESULT put_PropertyName (in <type> iPropertyValue); |
CATSafeArray
type used as method parameters can not have more
than one dimensionCATBSTR
type must always be used for character stringsenum
can be defined if it meets the three conditions
below:
enum
using the typedef
statementenum
element is not initialized. It is valued
to 0 by the IDL compiler [2].Visual Basic | CAA V5 IDL | CAA V5 C++ |
---|---|---|
Integer | short | short |
Long | long | long |
Single | float | float |
Double | double | double |
Byte | octet | octet |
Boolean | boolean | boolean |
HRESULT | HRESULT | |
String | CATBSTR | CATBSTR |
Object | CATBaseDispatch * | CATBaseDispatch * |
Array | CATSafeArray | CATSafeArray |
Variant | CATVariant | CATVariant |
Visual Basic | CAA V5 IDL | CAA V5 C++ |
---|---|---|
Integer | short | short |
Long | long | long |
Single | float | float |
Double | double | double |
Byte | octet | octet |
Boolean | boolean | boolean |
HRESULT | HRESULT | |
String | CATBSTR | CATBSTR |
Object | CATBaseDispatch * | CATBaseDispatch * |
Array | CATSafeArray | CATSafeArray |
Variant | CATVariant | CATVariant |
#pragma ID CATIAExposed "DCE:7c1b4ba8-5c25-0000-0280020bcb000000" #pragma DUAL CATIAExposed #pragma ID AccessName "DCE:73a1e08a-d8c4-11d0-a59f00a0c9575177" #pragma ALIAS CATIAExposed AccessName |
The CATVariant stands for the Variant in Visual Basic, and for the any type of IDL, but you must use CATVariant in your IDL file. You should use it whenever you don't know the type of the parameter that Visual Basic will use, or if this type may vary.
[Top]
To be available form a scripting language, an interface must derive from CATIABase, or CATIACollection, and the class that implements it must derive from CATBaseDispatch, or CATBaseCollection, which provides an implementation of the methods declared by IDispatch. Its methods must also comply with the specific Automation signature restrictions.
Depending on the statements you put in the interface's IDL file, your interface can be exposable, exposed, or journalizable. It can include properties and methods the client applications can use on instances of objects implementing the interface.
The Interface Coding Check-List helps you to be right the first time.
[Top]
[1] | About Globally Unique IDentifiers |
[2] | The CAA V5 IDL Compiler |
[Top] |
Version: 1 [May 2000] | Document created |
[Top] |
Copyright © 2000, Dassault Systèmes. All rights reserved.