Eiffel-COM bridge v3.2

Last modified: 1.02.00 18:51

Table of Contents
1. Introduction
2. Mapping COM to Eiffel
2.1. Identifiers
2.2. Types
2.2.1. Primitive type
2.2.2. Pointer type
2.2.3. Coclass
2.2.3.1. General non-aggregated coclass
2.2.3.2. General aggregated coclass
2.2.3.3. Coclass with automation support
2.2.3.4. Connectable coclass
2.2.3.5. OLE control
2.2.4. Enumeration
2.2.5. Interface
2.2.6. Structure
2.2.7. Union
3. COM server
3.1. INPROC server
3.2. Connectable server
Glossary

1. Introduction

Eiffel-COM bridge allows to create COM servers under Visual Eiffel. There is no special utility that does it. Visual Eiffel compiler has built-in support for this. The Eiffel classes should follow the guidelines given here to be assembled as a COM server.


2. Mapping COM to Eiffel

2.1. Identifiers

The following Eiffel identifiers are translated into the COM identifiers by removing the trailing underscore:

adapt_ expanded_ prefix_
alias_ export_ print_
all_ external_ real_
and_ false_ redefine_
area_ feature_ rename_
as_ from_ require_
as_pointer_ frozen_ rescue_
bit_ generating_type_ result_
boolean_ generator_ retry_
character_ id_object_ same_dynamic_type_
check_ if_ same_type_
class_ implies_ select_
clone_ indexing_ separate_
command_line_arguments_ infix_ set_
conforms_to_ inherit_ size_
copy_ inspect_ standard_clone_
creation_ integer_ standard_copy_
current_ invariant_ standard_deep_clone_
debug_ io_ standard_deep_copy_
deep_clone_ is_ standard_equal_
deep_copy_ is_deep_equal_ standard_is_deep_equal_
deep_equal_ is_equal_ standard_is_equal_
default_ like_ string_
default_pointer_ local_ strip_
default_rescue_ loop_ tagged_out_
deferred_ none_ then_
double_ not_ true_
do_ object_id_ undefine_
do_nothing_ obsolete_ unique_
elseif_ old_ until_
else_ once_ variant_
end_ or_ void_
ensure_ out_ when_
equal_ pointer_ xor_

All other identifiers remain unchanged.


2.2. Types

Eiffel-COM bridge supports the following COM types:

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

Note: DISP interfaces are not supported. Dual interfaces may be used instead because they cover the functionality of DISP interfaces.


2.2.1. Primitive type

The following table shows how Eiffel classes are mapped into the primitive COM types.

Table 1. Mapping Eiffel types to primitive COM types

COM type IDL type Eiffel type
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_DECIMAL DECIMAL ECOM_DECIMAL [b]
VT_HRESULT HRESULT ECOM_HRESULT
VT_I2 short RTS_SHORT_INTEGER [b]
VT_I4 int INTEGER
VT_I8 - RTS_UINTEGER_8 [b]
VT_PTR ... * see Pointer type
VT_R8 double DOUBLE
VT_UI1 unsigned char CHARACTER
VT_VARIANT VARIANT expanded ECOM_VARIANT [b]
Notes:
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.

Other primitive COM types are not supported.


2.2.2. Pointer type

The mapping of pointer types is described in the documentation on COM-Eiffel bridge. Eiffel-COM bridge supports only those pointer types that are binded to the types, supported by Eiffel-COM bridge.

Important: Pointers to interfaces are supported in the same way as it is done in the COM-Eiffel bridge. They correspond to expanded Eiffel classes objects of which occupy 4 bytes in the memory and which inherit the proper classes representing COM interfaces. These are not Eiffel-COM bridge specific classes. They are the same ECOM_CLIENT_ classes used in the COM-Eiffel bridge.


2.2.3. Coclass

The class describing a coclass should be an effective class being a direct or indirect descendant of the class ECOM_COCLASS and it should inherit all the interfaces it implements (except interface IUnknown which is already inherited by ECOM_COCLASS). In addition to implementation of the features of interfaces (including IUnknown), the class should implement a feature CreateObjectInstance which implements a creation of COM object. All the other required information to support run-time COM structures is automatically created and saved in the executable module.

Example 1. General coclass implementation

class MY_COCLASS
inherit
   ECOM_CLASS
      end
   ECOM_IMyInterface
      end
feature -- implementation of IUnknown
   ...
feature -- Implementation
   CreateObjectInstance ...
feature -- implementation of IMyInterface
   ...
end -- class MY_COCLASS

There are several library classes that provide basic implementation for different cases. They are described in the sections below. In all these cases a class implementing a coclass should inherit classes that describe required COM interfaces and implement their features.


2.2.3.1. General non-aggregated coclass

Properties:

Implementation:

  • inherit ECOM_NON_AGGREGATED_CLASS

Example 2. Non aggregated coclass implementation

class CMyCoClass
inherit
   ECOM_NON_AGGREGATED_CLASS end
   ECOM_MY_INTERFACE end
feature -- ECOM_MY_INTERFACE implementation
   ...
end -- class CMyCoClass

2.2.3.2. General aggregated coclass

Properties:

Implementation:

  • inherit ECOM_AGGREGATED_CLASS

2.2.3.3. Coclass with automation support

Properties:

Implementation:

  • inherit ECOM_STANDARD_DISPATCHED_CLASS
  • define an attribute default_interface of the type that corresponds to the default interface of the coclass; the methods of the default interface would be available for invocation via IDispatch
  • redefine the feature type_lib_path that returns the path to the type library that is created by the Eiffel-COM bridge unless the type library is linked into the executable module

Example 3. Implementation of a coclass, supporting IDispatch

class CMyCoClass
inherit
   ECOM_STANDARD_DISPATCHED_CLASS end
   ECOM_I_MY_DUAL_INTERFACE end
feature -- Default interface
   default_interface: ECOM_I_MY_DUAL_INTERFACE
feature -- TLB path
   type_lib_path: STRING is do ... end;
feature -- ECOM_I_MY_DUAL_INTERFACE implementation
   ...
end -- class CMyCoClass

2.2.3.4. Connectable coclass

Properties:

  • aggregation
  • automation
  • connection (via an interface IConnectionPointContainer)

Implementation:

  • inherit ECOM_CONNECTABLE_CLASS
  • define an attribute default_interface of the type that corresponds to the default interface of the coclass; the methods of the default interface would be available for invocation via IDispatch
  • define an attribute default_outgoing_interface of the type that corresponds to the default outgoing interface of the coclass
  • redefine the feature type_lib_path that returns the path to the type library that is created by the Eiffel-COM bridge unless the type library is linked into the executable module

Example 4. Implementation of a coclass, supporting an outgoing interface

class CMyCoClass
inherit
   ECOM_CONNECTABLE_CLASS end
   ...
feature -- Default outgoing interface
   default_outgoing_interface: ECOM_I_MY_OUTGOING_INTERFACE
   ...
end -- class CMyCoClass

2.2.3.5. OLE control

Not implemented yet.


2.2.4. Enumeration

The name of the class describing a COM enumeration should be formed as ECOM_ENUM_enum_name, where enum_name is the name of the enumeration. The members of the enumeration should be represented by the integer constants. If their names have suffix _value, this suffix is removed from the names. The class should be expanded and the object size should be 4 bytes.

Example 5. Mapping an enumeration

Eiffel class IDL enumeration
expanded class ECOM_ENUM_AB
inherit
   INTEGER
feature
   A_VALUE: INTEGER is 65
   B_VALUE: INTEGER is 66
end -- class ECOM_ENUM_AB
typedef enum _AB
{
   A = 65,
   B = 66
} AB;

2.2.5. Interface

The name of the class describing a COM interface should be formed as ECOM_interface_name, where interface_name is the name of the interface. The class should be a direct descendant of ECOM_IUNKNOWN or it should be a direct descendant of another class that represents a COM interface. All immediate features of the class should be deferred. They are mapped into the interface methods with the same names.

Example 6. Mapping an interface

Eiffel class IDL interface
deferred class ECOM_IMYINTERFACE
inherit
   ECOM_IUNKNOWN
feature
   meth1 (p1: INTEGER): INTEGER
   meth2: ECOM_BSTR
end -- class ECOM_IMYINTERFACE
interface IMYINTERFACE: IUnknown
{
   int meth1 (int p1);
   BSTR meth2;
};

The type of the interface method and the types of its arguments should be types, supported by the Eiffel-COM bridge. There is also a limitation on the supported results of the functions (see Mapping Eiffel types to primitive COM types).


2.2.6. Structure

The name of the class describing a COM structure should be formed as ECOM_struct_name, where struct_name is the name of the structure. The members of the structure should be represented by the class attributes. The types of the members should be types, suppported by the Eiffel-COM bridge.

Example 7. Structure name, structure members

class ECOM_MYSTRUCT
feature
   i: INTEGER
   bstr: expanded ECOM_BSTR
end -- class ECOM_MYSTRUCT

The class should be written in a way that allows to have the corresponding expanded class. The objects of that expanded class should occupy the same space in the memory. The offsets of the class attributes should correspond to the offsets of the structure members The structure members should be aligned to the 4-byte boundary. This can be done using padding attributes which occupy required number of bytes in the memory. These attributes should have prefix pad in their names. The Eiffel-COM bridge automatically recognizes the padding attributes and discards them when the mapping from Eiffel to COM is performed. It reports an error if non-padding attributes of the class are misaligned.

Example 8. Alignment of structure members

Eiffel class IDL structure
class 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 ECOM_A           -- size = 12
typedef struct _A
{                
   char  c;  // offset: 0
   int   i;  // offset: 4
   short s;  // offset: 8
} A; // size = 12

2.2.7. Union

The name of the class describing a COM union should be formed as ECOM_UNION_union_name, where union_name is the name of the union. Members of the union should be represented by functions without arguments. They should return an object of the required type, suppported by the Eiffel-COM bridge. Moreover, the Eiffel class should have an attribute or attributes so that Eiffel object size is equal to the size of the union. These attributes are used only for valid mapping into the memory and are not considered as defining the union members. The class should be written in a way that allows declaration of the corresponding expanded class.

Example 9. Mapping a union

Eiffel class IDL union
class ECOM_UNION_A
feature {NONE} -- Internal representation
   data: BIT 32
feature -- Access
   c: CHARACTER is do ... end
   i: INTEGER is do ... end
   s: RTS_SHORT_INTEGER is do ... end
end -- class ECOM_UNION_A
typedef union _A
{
   char  c;
   int   i;
   short s;
} A;

3. COM server

The Eiffel-COM bridge currently supports only INPROC servers and servers that implement connectable coclasses.


3.1. INPROC server

To create an INPROC server, the project settings should specify COM as a value of the target option. The server would be placed in a DLL.

The root class of the Eiffel system should inherit the class ECOM_DLL This class implements features DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer, which become additional entry points in the DLL and are required to use the system as a COM server.

One COM server can support any number of coclasses. The Eiffel-COM bridge discovers which coclasses are supported by the COM server using attributes of the root class. The root class should declare one attribute per one coclass. A creation instruction for the attribute in should be executed in the root creation procedure.

The process of discovery of the required coclass is described below:

  1. The client requests a coclass, e.g. by calling CoCreateInstance
  2. The standard COM library looks in the registry for the record with the corresponding CLSID, loads the DLL specified in the parameter InprocServer32 and calls DllGetClassObject
  3. The feature DllGetClassObject of the class ECOM_DLL looks for the coclass with the given CLSID among the attributes of the root class, using run-time information generated by the Eiffel-COM bridge and returns the corresponding class factory interface
  4. The standard COM library uses the returned interface IClassFactory and calls CreateInstance
  5. The feature CreateInstance of the class ECOM_CLASS_FACTORY calls the feature CreateObjectInstance for the corresponsing attribute of the root class; this feature creates new instance of the object and returns interface IUnknown

The last step involving feature CreateObjectInstance of the class ECOM_CLASS is described in details below:

  1. The corresponding object is duplicated using feature deep_clone
  2. The object is initialized by the feature init, which must call the feature mandatory_init of the class ECOM_CLASS; the latter creates virtual method tables for the interfaces of the coclass
  3. A pointer to the interface IUnknown is obtained
  4. An object is added to the table of active coclass objects in the class ECOM_SERVER

3.2. Connectable server

If only outgoing interfaces should be supported, there is no need to specify a specific value for the compiler option target. It means, in particular, that outgoing interfaces are supported not only for the DLL, but also for the EXE modules.

Glossary

Automation

Automation denotes a possibility of dynamic discovery of the methods supported by the coclass. To achieve this the coclass should implement the interface IDispatch. Then the client can call the methods of the the default interface via IDispatch.Invoke.