3D PLM Enterprise Architecture

Middleware Abstraction - Object Modeler

Implementing Interfaces for Automation

Making your components available from scripting languages
Technical Article

Abstract

This 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.


Implementing Your Interfaces for Automation

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]

Method Returned Value

All methods must return an HRESULT value [2].

[Top]

Method Parameters

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:

Table 1: Supported Types for in Parameters of Non-Property Methods
CAA V5 IDL C++ VBScript
in float const float Single
in double const double Double
in short const short Integer
in long const long Long
in char const char Byte
in boolean const boolean Boolean
in CatYYY CATYYY Long (enumerated value)
in CATBSTR const CATBSTR& String
in CATIAXXX CATIAXXX* VB object
in CATVariant const CATVariant& any of the above types
in CATSafeArrayVariant const CATSafeArrayVariant& VB unidimensional array

 

Table 2: Supported Types for out Parameters of Non-Property Methods (Note the lack of CATSafeArrayVariant support)
CAA V5 IDL C++ VBScript
out float float& Single
out double double& Double
out short short& Integer
out long long& Long
out char char& Byte
out boolean boolean& Boolean
out CatYYY CatYYY& Long (enumerated value)
out CATIAXXX CATIAXXX*& VB object
out CATSafeArrayVariant CATSafeArrayVariant*& VB unidimensional array

Note:

Table 3: Supported Types for inout Parameters of Non-Property Methods
CAA V5 IDL C++ VBScript
inout float float& Single
inout double double& Double
inout short short& Integer
inout long long& Long
inout char char& Byte
inout boolean boolean& Boolean
inout CatYYY CatYYY& Long (enumerated value)
inout CATBSTR CATBSTR& String
inout CATVariant CATIAXXX* VB object
inout CATIAXXX CATVariant& any of the above types
inout CATSafeArrayVariant CATSafeArrayVariant& VB unidimensional array

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]

Properties

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:

 

Table 4: Supported Types for Property get_ functions
CAA V5 IDL C++ VBScript
out float float& Single
out double double& Double
out short short& Integer
out long long& Long
out char char& Byte
out boolean boolean& Boolean
out CatYYY CatYYY& Long (enumerated value)
inout CATBSTR CATBSTR& String
out CATIAXXX CATIAXXX*& VB object
inout CATVariant CATVariant& any of the above types

Note:

Table 5: Supported types for property put_ functions
CAA V5 IDL C++ VBScript
in float float Single
in double double Double
in short short Integer
in long long Long
in char char Byte
in boolean boolean Boolean
in CatYYY CATYYY Long (enumerated value)
in CATBSTR const CATBSTR& String
in CATIAXXX CATIAXXX* VB object
in CATVariant const CATVariant& any of the above types

Note the lack of CATSafeArrayVariant support.

[Top]

Using Character Strings

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]

Using CATVariants

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]

Using Arrays

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]


In Short

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]


References

[1] Creating Interfaces for Automation
[2] What Is HRESULT?
[3] The CAA V5 IDL Compiler
[Top]

[Top]


History

Version: 1 [May 2000] Document created
[Top]

Copyright © 2000, Dassault Systèmes. All rights reserved.