Building a COM Component
When building a COM component, the EiffelCOM wizard allows for the development of both in-process (DLLs) and out-of-process (EXEs) COM components in Eiffel.
If the project consists of adding a COM interface to an existing Eiffel project then the wizard will generate (and optionally compile) everything required to implement the COM component. No extra work is required.
If the project uses a COM definition file (either an IDL file or a type library), the EiffelCOM Wizard generates empty features in the classes corresponding to the component's coclasses. The implementation can then be added into these empty features.
Unlike the code generated to access an existing component, the code generated to implement a new COM component will differ for an in-process or an out-of-process component. The difference lies in the component activation code in the class ECOM_<Name_of_system>_REGISTRATION
. If the component is in-process then this class includes the four functions that need to be exported from an in-process COM component ( DllRegisterServer
, DllUnregisterServer
, DllGetClassObject
, and DllCanUnloadNow
). If the component is out-of-process then the registration class includes a feature initializing the component and its graphical user interface.
The architecture of the generated code is similar to the one used by the code generated to access an existing component: classes corresponding to the component's coclasses inherit from classes corresponding to the component's interfaces and implement all the deferred features. There is however a major difference: the classes corresponding to the component's coclasses should not be inherited from. Instead their implementation should be completed. Putting the implementation in a heir class would cause a runtime failure since the EiffelCOM generated code will instantiate the class corresponding to the component's coclass when being called rather than any descendant that would actually contain the implementation. The classes corresponding to the component's coclasses are all suffixed with _COCLASS_IMP
.
Component's GUI
In the case of an out-of-process server, the component might need a Graphical User Interface. There are two different scenarios in which the component can be activated: either its user launched it explicitly (e.g. by double clicking the executable icon) or it was launched by the COM runtime to satisfy a client request. In some cases it might be required for the component to have a different behavior in these two cases (for example the GUI could only be necessary when the component was launched explicitly by the user but should be hidden when launched by the COM runtime). The generated registration class for an out-of-process server includes the feature:
main_window: WEL_FRAME_WINDOW
This feature is a once
function that can return the class corresponding to the component window. By default, this window is displayed only if COM does not start the component. When COM loads an out-of-process component, it appends the command line parameter "-embedding" to the executable. The generated registration class looks for this option and if it is part of the process argument list then it sets the default window appearance to hidden. Of course this default implementation may be modified if needed.
Exceptions
The COM standard specifies that each COM routine should return a status code to the client. The status code is encoded in a HRESULT
value. Such behavior goes against the Command Query Separation Principle in Eiffel. The EiffelCOM generated system should use exceptions instead of returning HRESULT
values to the client. By default the EiffelCOM runtime will always return S_OK
meaning that the routine succeeded. To notify the client of a problem (e.g. invalid argument value), the EiffelCOM component should raise the corresponding exception from class ECOM_EXCEPTION
using the feature trigger
from the same class. The EiffelCOM runtime will catch the exception and send the corresponding HRESULT
back to the client. Here is what the Eiffel code for a COM component should look like:
note description: "Eiffel coclass server example" class ECOM_SERVER_COCLASS_IMP inherit ECOM_SERVER_COCLASS -- Generated by the wizard ECOM_EXCEPTION export {NONE} all end feature -- Basic Operations coclass_feature (an_argument: ARGUMENT_TYPE) -- Example of a coclass feature do if not is_valid (an_argument) then trigger (E_invalidargument) else -- Normal processing end end feature {NONE} -- Implementation is_valid (an_argument: ARGUMENT_TYPE): BOOLEAN -- Is an_argument a valid argument? do -- Test of validity of an_argument end end -- class ECOM_SERVER_COCLASS_IMP
This class inherits from the generated Eiffel coclass and from ECOM_EXCEPTION
. It implements the deferred feature coclass_feature
from the generated coclass. This feature is part of the interface functions that can be called by clients of the component. Its implementation uses the feature trigger
from ECOM_EXCEPTION
to raise exceptions in case the feature cannot be executed normally (invalid argument). The EiffelCOM runtime catches the exception and maps it into an HRESULT
that is sent back to the client.