All Manuals > LispWorks COM/Automation User Guide and Reference Manual > 3 Using Automation


3.4 Implementing Automation interfaces in Lisp

This section describes two techniques for implementing Automation interfaces in Lisp. The choice of technique usually depends on whether you are implementing a complete server or a simple event sink. The section then describes other kinds of interfaces that can be implemented and how to report errors to the caller of a method.

3.4.1 A complete implementation of an Automation server

In the case where you are designing an set of COM interfaces and implementing a server to support them, you need to make a complete implementation in Lisp. This allows several Automation interfaces to be implemented by a single class and also supports dual interfaces.

The implementation defines an appropriate class, inheriting from the class standard-i-dispatch to obtain an implementation of the COM interface i-dispatch. This implementation of i-dispatch will automatically invoke the appropriate COM method.

For dual interfaces, the methods should be defined in the same way as described for COM interfaces in Implementing COM interfaces in Lisp.

For dispinterfaces , the methods should be implemented using the macro define-dispinterface-method or by a specialized method of the generic function com-object-dispinterface-invoke.

To implement an Automation interface in Lisp with standard-i-dispatch, you need the following:

  1. A type library for the component, converted to Lisp as specified in Including Automation in a Lisp application.
  2. A COM object class defined with define-automation-component or define-automation-collection, specifying the coclass or interface(s) to implement.
  3. Implementations of the methods using define-com-method, define-dispinterface-method or com-object-dispinterface-invoke.
  4. For an out-of-process Automation component, either use automation-server-main or have registration code which calls register-server and unregister-server, typically after checking the result of automation-server-command-line-action or explicitly checking the command line for arguments /RegServer and /UnRegServer.
  5. Initialization code which either calls automation-server-top-loop or automation-server-main, or calls co-initialize and start-factories in a thread that will be processing Windows messages (for instance a CAPI thread).

3.4.2 A simple implementation of a single Automation interface

In the case where you are implementing a single dispinterface that was designed by someone else, for example an event sink, you can usually avoid needing to parse a type library or define a class to implement the interface.

Instead, you implement a dispinterface using the class simple-i-dispatch by doing the following:

  1. Obtain an interface pointer that will provide type information for the component, to be used as the related-dispatch argument in the call to the function query-simple-i-dispatch-interface. In the case where you are implementing an event sink, the source interface pointer will usually do this.
  2. Optionally, define a class with defclass inheriting from simple-i-dispatch. The class simple-i-dispatch can be used itself if no special callback object is required.
  3. Implement an invoke-callback that selects and implements the methods of the interface.
  4. Define initialization code which calls co-initialize, obtains the related-dispatch from step 1, makes an instance of the COM object class defined in step 2 with the invoke-callback from step 3, obtains its interface pointer by calling query-simple-i-dispatch-interface (passing the related-dispatch) and attaches this interface pointer to the appropriate sink in the related-dispatch (for example using connection point functions such as interface-connect). This must all be done in a thread that will be processing Windows messages (for instance a CAPI thread).

3.4.3 Implementing collections

Interfaces that support the Collection protocol can be implemented using the macro define-automation-collection. This defines a subclass of standard-automation-collection, which implements the minimal set of collection methods and calls Lisp functions to provide the items. If the collection items are interface pointers, appropriate reference counting must be observed.

See the example files here:

(example-edit-file "com/automation/collections/")

3.4.4 Implementing connection points

Lisp implementations can act as event sources via a built-in implementation of the IConnectionPointContainer interface, which define-automation-component provides if source interfaces are specified. A built-in implementation of IConnectionPoint handles connections for each interface and the macro do-connections can be used to iterate over the connections when firing the events.

3.4.5 Reporting errors

Classes defined using define-automation-component allow extended error information to be returned for all Automation methods. Within the body of a define-com-method definition, the function set-error-info can be called to describe the error. In addition, this function returns the value of DISP_E_EXCEPTION, which can be returned directly as the hresult from the method.

For example:

(define-com-method (i-test-suite-1 fx) 
    ((this c-test-suite-1))
  (print "in fx")
  (set-error-info :description "foo"
                  :iid 'i-test-suite-1
                  :source "fx"))

3.4.6 Registering a running object for use by other applications

If other applications need to be able to find one of your running objects from its coclass, then call register-active-object to register an interface pointer for the object in the system Running Object Table. Call revoke-active-object to remove the registration.

3.4.7 Automation of a CAPI application

For an example of how to implement an Automation server that controls a CAPI application, see the file:

(example-edit-file "com/automation/capi-application/build")

LispWorks COM/Automation User Guide and Reference Manual - 23 Mar 2017