Next Previous Up Top Contents Index

A.2 An example COM server and client

A.2.1 Creating the server stubs library

First we use the New Project wizard to create a Dylan library defining server-side stubs for the encryption interface.

1. Choose File > New... from the main window.
2. Select Project and click OK.
The New Project wizard appears.
3. In the Project Type section, select "Interface to COM Type Library" and click Next.

The next page allows you to name a type library to be translated. Most COM components store the location of their type libraries in the Windows Registry. These registered type libraries are listed in the Installed Type Libraries section of the window. You can select a type library from the list, or click Browse... in the Location pane to select a type library file from disk.

Figure A.1 Choosing a type library file to convert.

In this case, we use a type library supplied with Functional Developer.

4. Click Browse... and navigate to the Functional Developer examples folder.
The folder required is called Examples and is placed under the top-level Functional Developer folder.
It is usually C:\Program Files\Functional Objects\Functional Developer\Examples.
5. Go to the Documentation\RotNExample subfolder and choose the RotNExample.tlb file.
6. Click Next to continue to the next page of the wizard.

Figure A.2 Choosing the kind of skeleton code to generate.

The next page allows you to choose what kind of stubs to generate from the type library. There are two options:

Dispatch client interfaces

Defines Dylan code to allow you to interface to COM servers.
Dispatch server skeletons

Defines Dylan code to allow you to create COM servers implementing the interfaces described in the type library.

Because we are writing the server side of the application, we want to generate dispatch server skeleton code.

7. Select "Dispatch server skeletons".
8. Click Next to continue to the next page of the wizard.

Figure A.3 Choosing interfaces and classes from the type library.

The next page presents a list of COM classes and interfaces contained in the selected type library. You can select which to translate by choosing "Translate selected" and then clicking to select individual items, dragging or using the Shift key to select ranges, and using the Ctrl key to select additional items. Choose "Translate all" and all classes and interfaces in the type library will be translated. This is different from selecting all items under "Translate selected" because if classes or interfaces are added to the type library later, they will only be translated if you selected "Translate all".

9. Choose "Translate all", so that both the RotNExample COM class and the IRotNExample interface are translated.
10. Click Next.

Now we reach the Project Name and Location page. This and all subsequent pages are the same as those that you see for other kinds of project in the New Project wizard. Follow the remaining steps to finish defining the server stubs project.

11. Change the name of the project to RotNExample-server-stubs.
12. Choose a suitable Location for the project.
13. Make sure that the "Dynamic Link Library (DLL)" option is chosen in the Project Settings and Templates section.
14. Make sure that the "Include any available templates" option is not checked.
15. Click Next to continue.

We are now at the Use Libraries page. We are only defining the stubs for the server, so we do not need any unusual libraries.

16. Choose the "Minimal" option.
17. Click Next to continue.

We are now at the final page of the New Project wizard.

18. Make any changes you want to the Source File Headers section.
19. Click Finish.
The new project appears.

Figure A.4 The server stubs project.

In addition to the usual files, this project contains a file named type-library.spec. This is a specification file. It provides information to the type library tool.

20. Double-click on the specification file.
An editor window opens on the specification file.

The specification file looks something like this:

Origin: COM-type-library
Type-library: C:\Program Files\Functional Objects\Functional Developer\Examples\...\RotNExample.tlb
Module: type-library-module
Module-file: type-library-module.dylan
Generate: dispatch-servers
Stub-file: stubs.dylan

As you can see, the specification file contains all the information provided to the type library portion of the New Project wizard.

However, no skeleton code has yet been generated. The type library tool, which generates the skeleton code, only runs when you build the project.

21. Build the project with Project > Build.

The build process adds some new files to the project. These files were generated by the type library tool.

The file type-library-module.dylan defines a module in which the translated code resides. This module exports all translated symbols. If you look at module.dylan, you will see that the main module re-exports all of these symbols.

The file stubs.dylan contains the Dylan code generated by the type library tool. It defines a class for an implementation of the IRotNExample interface, and generic functions for the methods and properties of the interface:

/* Dispatch interface: IRotNExample version 0.0
* GUID: {822ED42A-3EB1-11D2-A3CA-0060B0572A7F}
* Description: An example interface for Functional Developer's Getting
  Started manual. */

define open dispatch-interface <IRotNExample> (<simple-dispatch>) uuid "{822ED42A-3EB1-11D2-A3CA-0060B0572A7F}"; virtual property IRotNExample/key :: type-union(<integer>, <machine-word>), name: "key", disp-id: 12288; function IRotNExample/encrypt (arg-pre :: <string>) => (arg-result :: <string>), name: "encrypt", disp-id: 24576; function IRotNExample/decrypt (arg-pre :: <string>) => (arg-result :: <string>), name: "decrypt", disp-id: 24577; end dispatch-interface <IRotNExample>;

define open generic IRotNExample/key (this :: <IRotNExample>) => (arg-result :: type-union(<integer>, <machine-word>));

define open generic IRotNExample/key-setter (arg-result :: type-union(<integer>, <machine-word>), this :: <IRotNExample>) => (arg-result :: type-union(<integer>, <machine-word>));

define open generic IRotNExample/encrypt (this :: <IRotNExample>, arg-pre :: <string>) => (result :: <HRESULT>, arg-result :: <string>); define open generic IRotNExample/decrypt (this :: <IRotNExample>, arg-pre :: <string>) => (result :: <HRESULT>, arg-result :: <string>);

This defines a class <IRotNExample> which implements the given interface. The implementation is not complete until methods are provided for the generics. This can be done by adding methods in the same library, or by defining a subclass of <IRotNExample> in another library and defining methods on the subclass. We will take the latter approach.

There is also generated code corresponding to the COM class RotNExample from the type library:

/* COM class: RotNExample version 0.0
* GUID: {C44502DB-3EB1-11D2-A3CA-0060B0572A7F}
* Description: Implementation of IRotNExample.
*/

define constant $RotNExample-class-id = as(<REFCLSID>, "{C44502DB-3EB1-11D2-A3CA-0060B0572A7F}");

/* You should define your coclass something like this: define coclass $RotNExample-type-info name "RotNExample"; uuid $RotNExample-class-id; default interface <IRotNExample>; end coclass; */

Note that the define coclass is within a comment, since you may want to define a COM class based on a subclass of <IRotNExample>.


Getting Started with Functional Developer - 31 MAR 2000

Next Previous Up Top Contents Index