COM-Eiffel bridge v3.2

Last modified: $Date: 2005/02/02 11:51:44 $


Table of Contents

1. Introduction
2. Synopsis
2.1. Options
2.2. Command file
3. Generation strategy
3.1. Information sources
3.2. Output files
4. Mapping COM to Eiffel
4.1. Identifiers
4.2. Types
A. Name mapping summary
Glossary

1. Introduction

The utility is used as a bridge between COM and Eiffel code to access COM objects from programs written in Eiffel.

COM-Eiffel bridge is intended to automatically generate Eiffel classes - wrappers for the COM objects. The information for this is taken from the type libraries. The following COM types are supported:

  • primitive type
  • pointer type
  • coclass
  • enumeration
  • interface (general, DISP, dual)
  • structure
  • union

The GUI version of COM-Eiffel bridge is also available.

2. Synopsis

2.1. Options

An option can start either from "-" or from "/".

-clsid:clsid,...

generate Eiffel classes that wrap COM class(es) with the given CLSIDs

Example 1. Coclass selection

-clsid:{0002DF01-0000-0000-C000-000000000046}

Note

These coclasses must be registered in the registry. They must also support interface ITypeLib or have the corresponding type library registered in the registry (see Registration in registry).

-dir:output_dir
set the path for the output directory to the given one (current directory is used by default)
-gen:[da|en|vh|wp],...

set generation options:

daallow generation of classes that wrap DISP interfaces of dual interfaces; they are not generated by default
en

change names of structure, enumeration or union items to improve readability by removing the prefixes "_", "tag", "wire", "user" from the names; for example, struct _tagMYSTRUCT will be mapped into Eiffel class named ECOM_MYSTRUCT_ instead of ECOM__TAGMYSTRUCT

vhforce Eiffel functions to return HRESULT if the corresponding COM method has return type VT_VOID: many type libraries mark methods as returning VT_VOID though factual implementation return HRESULT; the option explicitly states whether it should change the return type accordingly to this observation
wpdo not seek for already generated classes in the "predefined" directory (see -pd:predefined_dir)

Example 2. Generation options

-gen:da,en
-help
show brief help on command line arguments
-int:interface_name,...

generate Eiffel classes that wrap COM interface(s) with the specified name(s)

Example 3. Interface selection

-int:Installer

Note

These interfaces and the corresponding type library must be registered in the registry (see Registration in registry).

-nologo
suppress logo message
-pd:predefined_dir
set the path for the "predefined" directory. The "predefined" directory contains all already generated wrappers. COM-Eiffel Bridge does not generate Eiffel classes for COM types that exist in the "predefined" directory. By default this is a directory containing the ECOM cluster.
-tlb:tlb_path,...

generate Eiffel classes for all items from the type libraries located in the files with the specified names

Example 4. Type library selection

-tlb:C:\Windows\System\mshtml.tlb
-verbose
display detailed information about generation process

2.2. Command file

Command file may be used to specify options for COM-Eiffel bridge. This is a text file every line of which contains exactly one option starting from the first position.

3. Generation strategy

3.1. Information sources

All the information required to generate wrappers is obtained from type libraries. The table below shows where the type library is taken from depending on the specified options.

Table 1. Sources of type information

OptionType library
-clsid:clsid,...

The type library is searched in the following order:

  • The registry entry pointing to the type library file name specified under the given clsid

  • The type library obtained via IDispatch interface if the specified coclass supports this interface

-int:interface_name,...The name of the type library file obtained from the registry under the given interface_name
-tlb:tlb_path,...The name of the type library file specified in the option (tlb_path)

Note

If several sources are specified when running COM-Eiffel bridge, they are processed in the following order:

3.2. Output files

The generated classes are stored in the files accordingly to the specified options. The code is produced not only for the selected COM types but also for all COM types they rely on. This procedure is recursive and results in a closed set of the generated classes.

The generated classes are put into the subdirectory of the output directory accordingly to their COM type. The subdirectories are Coclass, Enum, Interface and Struct. The names of the generated files correspond to the names of the Eiffel classes which are derived from the type information. Eiffel classes that are already included in the "predefined" directory are not generated.

4. Mapping COM to Eiffel

All COM types are mapped into the corresponding Eiffel types.

4.1. Identifiers

All names of the Eiffel classes generated by the COM-Eiffel bridge have prefix ECOM_ and depend on the actual COM type. The summary of the mapping rules is given in User-defined type name. Most feature names of the generated classes correspond to the names of COM type members. The summary of mapping rules is given in User-defined type member name.

Mapping of COM identifiers into Eiffel indentifiers may result in collisions. The following collisions are checked by the COM-Eiffel bridge:

  • usage of the leading underscores in the identifiers
  • usage of the reserved Eiffel words as the identifiers
  • usage of names corresponding to the features of class GENERAL and other parent classes, required by the converter
  • coincidence between names of locals and names of the interface methods

These collisions are resolved by appending an underscore at the end of the identifier.

4.2. Types

4.2.1. Primitive type

Primitive COM types are mapped into the basic Eiffel classes or into the ECOM classes and are not generated by COM-Eiffel bridge.

Table 2. Mapping primitive COM types to Eiffel types

COM typeIDL typeEiffel type
- IDispatch * expanded ECOM_CLIENT_IDISPATCH
- IUnknown * expanded ECOM_CLIENT_IUNKNOWN
VT_BOOL VARIANT_BOOL expanded ECOM_VARIANT_BOOL
VT_BSTR BSTR expanded ECOM_BSTR
VT_CARRAY CARRAY BIT N (N = size of CARRAY in bytes * 8) [a]
VT_CY CY expanded ECOM_CURRENCY [b]
VT_DATE double DOUBLE
VT_DECIMAL DECIMAL ECOM_DECIMAL [b]
VT_ERROR SCODE INTEGER
VT_HRESULT HRESULT ECOM_HRESULT
VT_I1 char CHARACTER
VT_I2 short RTS_SHORT_INTEGER [b]
VT_I4 int INTEGER
VT_I8 - RTS_UINTEGER_8 [b]
VT_INT int INTEGER
VT_LPSTR LPSTR POINTER
VT_LPWSTR LPWSTR POINTER
VT_PTR ... * see Pointer type
VT_R4 float - [b] [c]
VT_R8 double DOUBLE
VT_SAFEARRAYSAFEARRAY - [c]
VT_UI1 unsigned char CHARACTER
VT_UI2 unsigned shortRTS_SHORT_INTEGER [b]
VT_UI4 unsigned int INTEGER
VT_UI8 - RTS_UINTEGER_8 [b]
VT_UINT unsigned int INTEGER
VT_VARIANT VARIANT expanded ECOM_VARIANT [b]
VT_VOID void POINTER or nothing (for void return type)

[a] The methods returning this type are not supported unless its size is 1 or 4 bytes.

[b] The methods returning this type are not supported.

[c] The mapping is not currently supported.

All these Eiffel classes are expanded and the corresponding objects have the same size as their counterpart COM types.

4.2.1.1. ECOM_BSTR

Class ECOM_BSTR is expanded and corresponds to the COM type BSTR.

Warning

Class ECOM_BSTR does not support the automatic memory reclamation for BSTRs. There is an agreement in the COM that the caller should free the received BSTR, though you should check the documentation for the concrete interface to be sure in this.

Example 5. Usage of ECOM_BSTR

local
   bstr: ECOM_BSTR
do
   ...
   bstr.from_string ("Hello!")
   ... -- using of the bstr
   bstr.free

4.2.2. Pointer type

A pointer type allows to contruct a new type using a primitive or user-defined type. Nested pointer types are also allowed. The pointer type is mapped into the corresponding expanded generically derived type of the class ECOM_PTR. The table below summarizes the mapping rules for the pointer types.

Table 3. Mapping pointer types

IDL typeEiffel type
non_interface_type *expanded ECOM_PTR [mapping of non_interface_type]
interface_name *expanded ECOM_CLIENT_interface_name

Example 6. Mapping pointer types

IDL typeEiffel type
void * expanded ECOM_PTR [POINTER]
int * expanded ECOM_PTR [INTEGER]
BSTR * expanded ECOM_PTR [ECOM_BSTR]
GUID * expanded ECOM_PTR [expanded ECOM_GUID]
IUnknown * expanded ECOM_CLIENT_IUNKNOWN
IDispatch * expanded ECOM_CLIENT_IDISPATCH
IMyInterf * expanded ECOM_CLIENT_IMYINTERF
void ** expanded ECOM_PTR [expanded ECOM_PTR [POINTER]]
int ** expanded ECOM_PTR [expanded ECOM_PTR [INTEGER]]
BSTR ** expanded ECOM_PTR [expanded ECOM_PTR [ECOM_BSTR]]
IUnknown ** expanded ECOM_PTR [expanded ECOM_CLIENT_IUNKNOWN]
IMyInterf **expanded ECOM_PTR [expanded ECOM_CLIENT_IMYINTERF]

Class ECOM_PTR [G] represents a pointer to the expanded object of the type G.

Every generated class corresponding to some COM type has feature get_ptr, which returns a typed pointer to Current. This allows to manipulate pointers in Eiffel like in C though they remain type-safe.

Example 7. Using ECOM_PTR in the program

local
   iid_myint: expanded ECOM_GUID                          -- guid
   piid: expanded ECOM_PTR [expanded ECOM_GUID]           -- guid *
   int: INTEGER                                           -- int
   pint: expanded ECOM_PTR [INTEGER]                      -- int *
   ppint: expanded ECOM_PTR [expanded ECOM_PTR [INTEGER]] -- int **
do
   ...
   piid := iid_myint.get_ptr     -- get address of the guid
   pint.set_item ($int)          -- set pint to the address of int
   ppint.set_item (pint.get_ptr) -- set ppint to the address of pint
   ...

4.2.3. Coclass

The Eiffel class generated for the coclass has name ECOM_COCLASS_coclass_name, where coclass_name is a name of the coclass.

Note

If a coclass is used as an argument in some method, it is mapped into the base interface of this coclass.

Coclass creation procedures

create (pIUnknownOuter: POINTER; cls_ctx: WAPI_CLSCTX)

creates a single uninitialized object of the class

pIUnknownOuter
Pointer to whether object is or isn't part of an aggregate
cls_ctx
Context for running executable code
create_std_server

creates non-aggregated inproc server (the procedure has no arguments)

The generated class inherits ECOM_CLIENT_COCLASS and has features that return all interfaces supported by the coclass.

Example 8. Usage of coclass client class

	local
		CMyCls: ECOM_COCLASS_CMYCOCLASS
		iMyInterface: expanded ECOM_CLIENT_IMYINTERFACE;
	do
		!!CMyCls.create_std_server
		iMyInterface := CMyCls.get_IMyInterface
		... -- operations with iMyInterface
		ret := iMyInterface.Release
	end

To create multiple objects based on the single CLSID, a class factory for the coclass is used. COM-Eiffel bridge generates the class factory for each coclass. Its class name is ECOM_COCLASS_FACTORY_coclass_name, where coclass_name is the name of the corresponding coclass. This class inherits the library class ECOM_CLIENT_COCLASS_FACTORY.

Coclass factory creation procedures

create (pIUnknownOuter: WAPI_CLSCTX)

creates a class factrory for the class with the corressponding server context

pIUnknownOuter
Context for running executable code
create_std_server

creates a class factrory, inproc server (the procedure has no arguments)

The coclass factory has features that return all interfaces supported by the coclass.

Example 9. Usage of coclass factory class

	local
		CFMyCls: ECOM_COCLASS_FACTORY_CMYCOCLASS
		iMyInterface: expanded ECOM_CLIENT_IMYINTERFACE;
		pUnkOuter: expanded ECOM_CLIENT_IUNKNOWN
	do
		!!CFMyCls.create_std_server
		iMyInterface := CFMyCls.get_IMyInterface (pUnkOuter)
		...  -- operations with iMyInterface
		ret := iMyInterface.Release
	end
          

4.2.4. Enumeration

The COM enumeration is mapped to the Eiffel expanded class which inherits from INTEGER. It is binary equal to C-style enum and has the same size. The name of the generated class is ECOM_ENUM_enum_name, where enum_name is the name of the enumeartion.

Example 10. Mapping enumeration to Eiffel

IDL codeEiffel class interface
typedef enum _AB
   {
      A='A',
      B='B'
   } AB;
expanded class interface ECOM_ENUM__AB
inherit
   INTEGER
feature
   A: ECOM_ENUM__AB
   B: ECOM_ENUM__AB
-- Integer Values
   A_VALUE: INTEGER
   B_VALUE: INTEGER
end -- class interface ECOM_ENUM__AB

4.2.5. Interface

4.2.5.1. General interface

General interfaces implement those COM interfaces that do not support automation, i.e. do not have standard COM interface IDispatch as a direct or indirect ancestor.

Important

Interfaces are not directly used in the COM. They are always accessed via Pointer type. Therefore, the following description of interfaces is a description of pointers to interfaces.

COM-Eiffel bridge generates two classes per interface: a common deferred class which describes the methods of the interface and a client class which allows the client to use the interface. The common class has name ECOM_interface_name and the client class has name ECOM_CLIENT_interface_name, where interface_name is the name of the interface. The client should use an expanded version of the class representing the interface.

Note

The client class should be initialized by the actual pointer to the COM interface before it can be used. The feature get_qu_ptr is used to do it by passing its result as an argument to the feature QueryInterface of ECOM_CLIENT_IUNKNOWN. When the interface is no longer required, it should be released by calling feature Release (the feature also comes from ECOM_CLIENT_IUNKNOWN).

Example 11. Obtaining and releasing COM interface

local
     iid_myint: expanded ECOM_GUID
     iMyInterface: expanded ECOM_CLIENT_IMYINTERFACE
  do
     ...
     hresult := iUnknown.QueryInterface (iid_myint.get_ptr, iMyInterface.get_qu_ptr)
     ... -- operations with iMyInterface
     ret := iMyInterface.Release
  end

All the methods of the interface are mapped into the corresponding of the generated class. The primitive types of arguments and a result are mapped accordingly to Mapping primitive COM types to Eiffel types.

The inheritance structure of the COM interfaces is preserved in the generated classes. The top class in the hierarchy of generated classes inherits ECOM_CLIENT_IUNKNOWN.

4.2.5.2. DISP interface

DISP interface represents standard COM interface IDispatch. The generated class inherits ECOM_DISP_ADAPTOR and includes all the features of the interface obtained via IDispatch with the corresponding signatures.

4.2.5.3. Dual interface

Dual interfaces inherit interface IDispatch and add new methods. COM-Eiffel bridge handles these interfaces by generating two client classes instead of one: the first relies on the implementation of general interface classes, while the second relies on the implementation of DISP interface classes. To avoid class name clashes, the second class has name ECOM_DISP_ADAPTOR_interface_name, where interface_name is the name of the interface.

4.2.6. Structure

COM structures are standard C-style structs. The Eiffel representation of the structure contains all its members as attributes. The expanded structure class is binary equal to COM structure in the memory and has the same size. The name of the class is ECOM_struct_name, where struct_name is the name of the COM structure. The client should use an expanded version of the class.

Example 12. Mapping structure to Eiffel

IDL codeEiffel class interface
typedef struct _tagMYSTRUCT
   {
      int   i;
      char  c;
   } MYSTRUCT;
class interface ECOM__TAGMYSTRUCT
feature -- Access
   i: INTEGER
   c: CHARACTER
feature -- Modification
   adapt (other: like Current)
   set (i_: INTEGER; c_: CHARACTER)
end -- class interface ECOM__TAGMYSTRUCT

The structure members are automatically aligned accordingly to the COM conventions.

Example 13. Alignment inside COM structures (4-byte alignment)

IDL codeEiffel class interface
typedef struct A     
   {
      char  c;  // offset:  0
      int   i;  // offset:  4
      short s;  // offset:  8
   } _A;        // size:   12
class interface ECOM_A
feature
   c:       CHARACTER         -- offset:  0
   pad1_2B: RTS_SHORT_INTEGER -- offset:  1
   pad2_1B: CHARACTER         -- offset:  3
   i:       INTEGER           -- offset:  4
   s:       RTS_SHORT_INTEGER -- offset:  8
   pad3:    RTS_SHORT_INTEGER -- offset: 10
end -- class interface ECOM_A -- size:   12

4.2.7. Union

For the unions the Eiffel class is generated with argumentless functions returning objects corresponding to the union member type. The name of the generated class is ECOM_UNION_union_name, where union_name is the name of the union. The client should use an expanded version of the class.

Example 14. Mapping union to Eiffel

IDL codeEiffel class interface
typedef union _A
   {
      char  c;
      int   i;
      short s;
   } A;
class interface ECOM_UNION_A
feature
   c: CHARACTER
   i: INTEGER
   s: RTS_SHORT_INTEGER
end -- class interface ECOM_UNION_A

The expanded union Eiffel class is binary equal to C union when considering object layout in the memory, including the object size.

A. Name mapping summary

Table A.2. User-defined type member name

IDL typeIDL member nameEiffel feature name
enummember_name
  • member_name of enum type
  • member_name_VALUE of INTEGER type
unionmember_namemember_name
structmember_namemember_name
coclassinterface_nameget_interface_name
interfacemethod_namemethod_name
interfaceproperty_name
  • get_property_name
  • put_property_name

Glossary

Base interface of a coclass

A coclass may implement one or more interfaces. One of them, usually the first, is called a base interface. The base interface is used whenever a coclass is specified in the place where interface is required.

Registration in registry

Most of the information required to support COM is kept in the system registry. The process of storing such an information is known as registration and the corresponding software externalies are known as registered (e.g., registered COM interface). There are many ways the concrete application can be registered. Some of them are: installation program, registering COM classes via standard registration mechanism (regsvr32 uses this method), import regitry file, manual modification of the registry.