Accessing a COM Component

The wizard generates all the necessary code to access the existing component. The plumbing is already done, so instantiating an Eiffel coclass actually initializes COM internals.

Using the Generated Code

To access the component, you need to call features of the coclass. The data types of function arguments are either Eiffel types defined in Eiffel data structure libraries (EiffelBase), standard COM data types defined in the EiffelCOM library, or component COM data types specified in the definition file. For example, from the following IDL line

HRESULT InterfaceFunction ([in] int a, [out, retval] MyStruct * b)

The wizard generates the following feature in the Eiffel coclass:

interface_function (a: INTEGER): MY_STRUCT_RECORD

where MY_STRUCT_RECORD is a generated Eiffel class wrapping MyStruct.

Here is a more difficult example:

HRESULT a_function ([in] IsomeInterface * p_interface)

The wizard generates the following Eiffel feature:

a_function (p_interface: ISOME_INTERFACE_INTERFACE)

where ISOME_INTERFACE_INTERFACE is a generated deferred class. Where can you obtain an instance of the class? First, you may receive from other function. Second, you can create an implemented server interface ISOME_INTERFACE_IMPL_STUB. In the later case you should provide your own implementation.

Contracts

The wizard cannot generate fully specified contracts. Indeed, the tool has no domain specific knowledge and can only generate contracts that are domain independent. Such contracts, although useful, are not enough to describe entirely the behavior of the component. Generated contracts include void Eiffel objects as well as C pointer validity (for wrappers) checking. There might be other conditions to allow calls to an Eiffel coclass feature. Invariants and postconditions can be enforced in an heir of the generated Eiffel coclass. Preconditions, however, cannot be strengthened. A workaround provided by the wizard is to generate a precondition function for each feature in the interface. The default implementation of these functions always returns True. They should be redefined to implement the correct behavior:

    interface_function (a: INTEGER): MY_STRUCT is
                -- Example of a generated Eiffel coclass feature
        require
            interface_function_user_precondition: interface_function_user_precondition
        do
            ...
        ensure
            non_void_my_struct: Result /= Void
        end

So the complete class hierarchy for an Eiffel client coclass is the following:

EiffelCOM Client System

Another advantage of the previous hierarchy is that it adds incrementality to the EiffelCOM system. Indeed, should the definition file be modified and the wizard run once more against it, your code would not be changed. Only the generated Eiffel coclass would be, and it would suffice to adapt your heir accordingly.

Exceptions

COM standard requires that any interface function returns a status value (known as a HRESULT). This corresponds to side effect features which the Eiffel methodology tends to avoid. The workaround used in EiffelCOM systems is to map these return values into Eiffel exceptions. If the server returns an error code, the EiffelCOM runtime raises an Eiffel exception that your code should catch.

EiffelCOM Client System Exception Raising

As a result, any feature in the coclass client making calls to the user defined Eiffel coclass should include a rescue clause. The processing done in this clause might depend on the nature of the exception. All the standard COM exceptions can be found in the library class ECOM_EXCEPTION_CODES, which is inherited from by ECOM_EXCEPTION. The later also inherits from the kernel class EXCEPTIONS and can consequently be used by the coclass client to catch the exceptions.

The following code snippet illustrates how a client can process exceptions raised in the Eiffel coclass:

indexing
    description: "Eiffel coclass client example"

class
    COCLASS_CLIENT

inherit
    ECOM_EXCEPTION
        export
            {NONE} all
        end

create
    make

feature {NONE} -- Initialization

    make is
            -- Initialize Eiffel coclass.
        do
            create coclass.make
        end

feature -- Basic Operations

    coclass_feature_client is
            -- Example of a coclass feature caller
        local
            retried: BOOLEAN
            coclass: EIFFEL_COCLASS_PROXY
        do
            create coclass.make
            if not retried then
                coclass.coclass_feature -- Actual call
            end
        rescue
            if hresult = E_notimpl then
                -- Process non implemented function error.
                retried := True
                retry
            elseif hresult = E_invalidarg then
                -- Process invalid argument error.
                retried := True
                retry
            else
                -- Forward exception to caller.
            end
        end

end -- class COCLASS_CLIENT

Summary

There are a few rules to follow when building an Eiffel coclass client, but they are straightforward and do not add any constraints. First, inherit from the generated Eiffel coclass to implement the preconditions. Second, any feature call to the Eiffel coclass should include a rescue clause.

See Also:
Introduction Dialog and Main Window, Generated Code Type Dialog, Definition File Dialog, Eiffel Project File Dialog, Destination Folder Dialog, IDL Marshaling Definition Dialog, Type Library Marshaling Definition Dialog, Final Dialog, COM Definition File Processing, Eiffel Project Processing, Generated Files, Class Hierarchy, Accessing a Component, Building a Component