Last modified: $Date: 2005/02/02 11:51:44 $
Copyright © 1998-2000
Object Tools
<info@object-tools.com>
Table of Contents
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:
The GUI version of COM-Eiffel bridge is also available.
co2e
[options
] [@command_file
]
An option can start either from "-" or from "/".
-clsid:clsid
,...
generate Eiffel classes that wrap COM class(es) with the given CLSIDs
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
-gen:[da|en|vh|wp],...
set generation options:
da | allow 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, |
vh | force 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
|
wp | do not seek for already generated classes in
the "predefined" directory (see -pd: )
|
-help
-int:interface_name
,...
generate Eiffel classes that wrap COM interface(s) with the specified name(s)
These interfaces and the corresponding type library must be registered in the registry (see Registration in registry).
-nologo
-pd:predefined_dir
-tlb:tlb_path
,...
generate Eiffel classes for all items from the type libraries located in the files with the specified names
-verbose
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
Option | Type library |
---|---|
-clsid:clsid ,... | The type library is searched in the following order:
|
-int: | The name of the type library file obtained from the
registry under the given
interface_name
|
-tlb: | The name of the type library file specified in the
option (tlb_path )
|
If several sources are specified when running COM-Eiffel bridge, they are processed in the following order:
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.
All COM types are mapped into the corresponding Eiffel types.
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:
These collisions are resolved by appending an underscore at the end of the identifier.
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 type | IDL type | Eiffel 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_SAFEARRAY | SAFEARRAY | - [c] |
VT_UI1 | unsigned char | CHARACTER |
VT_UI2 | unsigned short | RTS_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.
Class ECOM_BSTR is expanded and
corresponds to the COM type BSTR
.
Class ECOM_BSTR does not support the
automatic memory reclamation for BSTR
s.
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.
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 type | Eiffel type |
---|---|
| expanded ECOM_PTR [mapping of
non_interface_type ] |
| expanded
ECOM_CLIENT_interface_name
|
Example 6. Mapping pointer types
IDL type | Eiffel 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 ...
The Eiffel class generated for the coclass has name
ECOM_COCLASS_coclass_name
,
where coclass_name
is a name of the coclass.
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
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
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
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.
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.
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.
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.
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.
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.
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 code | Eiffel 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 code | Eiffel 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 |
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 code | Eiffel 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.
Table A.1. User-defined type name
IDL type | IDL name | Eiffel class name |
---|---|---|
enum | enum_name | ECOM_ENUM_enum_name |
union | union_name | ECOM_UNION_union_name |
struct | struct_name | ECOM_struct_name |
coclass | coclass_name | ECOM_COCLASS_coclass_name |
interface | interface_name | ECOM_CLIENT_interface_name |
Table A.2. User-defined type member name
IDL type | IDL member name | Eiffel feature name |
---|---|---|
enum | member_name |
|
union | member_name | member_name |
struct | member_name | member_name |
coclass | interface_name | get_interface_name |
interface | method_name | method_name |
interface | property_name |
|
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.
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.