Copyright © 1998-2000 by Object Tools <info@object-tools.com>
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.
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.
Eiffel-COM bridge supports the following COM types:
Note: DISP interfaces are not supported. Dual interfaces may be used instead because they cover the functionality of DISP interfaces.
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.
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.
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.
Properties:
Implementation:
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
Properties:
Implementation:
Properties:
Implementation:
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
Properties:
Implementation:
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
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; |
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).
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 |
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; |
The Eiffel-COM bridge currently supports only INPROC servers and servers that implement connectable coclasses.
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:
The last step involving feature CreateObjectInstance of the class ECOM_CLASS is described in details below:
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.
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.