Advanced COM Server Topics

Advanced topics about Fortran COM Servers described in this section include:

Choosing Between DLL or EXE COM Servers

The basic tradeoff in choosing between a DLL (in-process) COM server and an EXE (out-of-process) COM server is one of performance vs. robustness:

In addition to the tradeoff between performance and robustness, the following factors should also be considered:

You can load a DLL server into a "surrogate" to gain the benefits of an EXE server. This is explained in the next section.

DLL Server Surrogates

An in-process DLL server can be run in a separate process with the help of a surrogate. A surrogate runs as a separate process, loads the DLL server, and provides all of the mechanism that allows the DLL server to act as a local server. Windows provides a standard surrogate named DLLHOST.EXE. The primary advantage of using a surrogate is fault isolation. That is, if the server crashes it does not crash the client, and vice versa. The disadvantage is performance. There is significant performance overhead in executing methods in a separate application, as opposed to in a DLL.

A DLL server is associated with a surrogate via entries in the system registry. This is done by associating the DLL server with an AppID. An AppID is a GUID. When using the standard surrogate, you can use the CLSID of a class in the DLL as the AppID, or a newly generated GUID. To generate a new GUID, use GUIDGEN.EXE in the Developer Studio Tools subdirectory:

  1. Under the HKEY_CLASSES_ROOT\CLSID\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx} key of the class, add an AppID entry with the value of the CLSID. Using the AddingMachine class as an example, the registry key would be HKEY_CLASSES_ROOT\CLSID\{904245FC-DD6D-11D3-9835-0000F875E193} and the AppID value would be {904245FC-DD6D-11D3-9835-0000F875E193}.
  2. Under the HKEY_CLASSES_ROOT\AppID key, add a key using the AppID. Using the AddingMachine class as an example, the registry key would be HKEY_CLASSES_ROOT\AppID\{904245FC-DD6D-11D3-9835-0000F875E193}. Use the class name for the default value of the key, for example, "AddingMachine Class". Add a "DllSurrogate" entry with an empty string for the value.

To use a surrogate, the system must be running Windows 2000, Windows NT 4.0 Service Pack 2 or later, Windows 98, Windows Me, or Windows 95 with the DCOM update.

The client must request CLSCTX_LOCAL_SERVER rather than CLSCTX_INPROC_SERVER to use the surrogate rather than loading the DLL server in-process. Using a surrogate requires that the DLL server have a proxy/stub registered since the method invocations are between different processes. For information on proxies/stubs, see Marshalling, Proxies and Stubs.

It is also possible to write a custom surrogate. See the online Win32 Platform SDK documentation for information.

Discussion of Wizard Code Generation

The Wizard generates the code for your project from the files in the subdirectory of your project named project-nametemplates. The project-name.hie file contains the definition of your COM server in an undocumented text language. You should not manually edit the project-name.hie file – the Wizard does this for you.

Most of the other files in the project-nametemplates directory are templates of the source files generated for your project. These templates contain source code that is copied "as-is" to the generated sources, and embedded directives that guide the Wizard in generating the code specific to the COM server that you define. The directives use the information in the project-name.hie file. The directives are undocumented and subject to change.

When you create a new Fortran COM Server project, the AppWizard creates the project-nametemplates directory and copies the templates from the Visual Fortran COM Server Wizard templates directory, ...\Df98\Templates\COMServer.

The files in the COM Server Wizard templates directory may change with each release of Visual Fortran, but the templates in your project-nametemplates directory are never automatically updated. For example, if you create a COM server using Visual Fortran Version 6.5 and the next release of Visual Fortran (such as Version 6.5A) contains updated templates, the templates for your COM server are not automatically updated to the new 6.5A templates. If you modify the definition of your server, your project continues to use the 6.5 templates that it was created with. This has the advantage of not introducing different code into a project that you have developed and tested.

However, there are two cases where you may want to modify the templates that are used by your project:

The advantage of modifying a template is that you can customize the code generated by the Wizard. The disadvantages of modifying a template are:

Adding Support for COM ErrorInfo Objects

COM supports ErrorInfo objects to allow a server to return more complete error information than is returned in an HRESULT. An ErrorInfo object returns the contextual description of the error, the source of the error, and the interface ID of the interface that originated the error. The ErrorInfo object can also include a pointer to an entry in a help file that provides help information on dealing with the error.

To support ErrorInfo objects, your server must implement the ISupportErrorInfo interface. You can add a default implementation of this interface to your server:

ISupportErrorInfo contains a single method, InterfaceSupportsErrorInfo. The default implementation returns S_OK, which is sufficient if all of the interfaces of the class support ErrorInfo objects. Otherwise, you should modify the method to test the passed in interface ID and return S_OK for those interfaces that do support ErrorInfo objects, and E_FAIL for those that do not.

When an interface that supports ErrorInfo objects is going to return an error status in HRESULT, it should also initialize an ErrorInfo object using the ICreateErrorInfo interface. See the ...\Df98\SAMPLES\ADVANCED\COM\ERRORINFO sample for an example of using ErrorInfo objects in Fortran. See the Platform SDK documentation for more information on ErrorInfo objects.

Threading Models

The Wizard supports two COM threading models for the classes that you create in a DLL COM server, Apartment and Single. The Wizard uses the Apartment threading model (also known as the Single Threaded Apartment model "STA") by default. The basic rules of the Apartment threading model are:

This means that if the class shares any global data among its objects, the global data must be protected from simultaneous access using thread synchronization primitives. This is because two instances of the class could be running in different threads. However the per-object instance data, that is, the fields in the class derived-type, are protected from simultaneous access by COM mechanisms. This is true except in the case where an object calls out to another object that triggers a reentrant invocation of the first object.

A class using the Single threading model need not worry about simultaneous access to class global data, as well as per-object data. All objects of the class are created in a single thread and therefore only a single object of the class can be executing at any time.

An EXE COM server generated by the wizard is single threaded. All method invocations are serialized by the server's message queue. Therefore, with an EXE server, you need not worry about simultaneous access to class global data, as well as per-object data.

Explaining the COM threading models in detail is beyond the scope of this documentation. See the Win32 Platform SDK documentation for additional information.

Marshalling, Proxies and Stubs

COM supports the use of objects in separate processes (as when using an EXE server or a DLL surrogate), and the threading models described above, by the use of marshalling, proxies and stubs. This section presents an overview of marshalling, proxies and stubs. See the Win32 documentation for further information.

Marshalling is the process of reading the parameters for a method call and preparing them for transmission to another execution context (for example, thread, process, or machine). Marshalling is done by a proxy. From the client's perspective, a proxy has the same interface as the object itself. The proxy's job is to make the object look like an object in the same execution context as the client.

Proxies allow client code to be unconcerned about where the object actually lives. A proxy marshalls method parameters and transmits them to a stub associated with the object in the server. The stub unmarshalls the parameters and invokes the method in the server. From the server's perspective, this is no different than when it is called from a client in the same execution context. A server that is not an in-process DLL server always requires a proxy/stub pair. An in-process DLL server requires a proxy/stub pair when the client and object are in different apartments.

There are three ways to assign a proxy/stub pair to a server:

Type Library Marshalling: If you use only Automation-compatible data types in your methods and properties, COM can automatically use the "Universal Marshaller" as the proxy/stub for the server. The Universal Marshaller uses the description of the server in the type library to decide how to marshall the parameters. This is known as type library marshalling. Using type library marshalling requires no effort on your part, other than restricting your server to Automation-compatible data types.
MIDL-based Marshalling: Your project uses the MIDL compiler to compile the IDL description of the server into a type library. At the same time, the MIDL compiler also generates the C source code necessary to build a proxy/stub DLL for the server. You must have a C compiler to build a proxy/stub DLL from the MIDL generated code. The proxy/stub DLL is itself an in-process DLL server and needs to be registered with the system. You would need to use MIDL-based marshalling if your server uses non-Automation-compatible data types, and is used by a client in a different execution context.
Custom Marshalling: Your server can implement its own marshalling by implementing the IMarshall interface. This approach typically involves a lot of work and is done for performance reasons.

A Map of the Generated "Do Not Edit" Code

This section presents an overview of which parts of the COM server functionality are implemented in which of the "Do Not Edit" source files generated by the wizard:

File Name Description
server.idl Contains the IDL description of the server. It is compiled by the MIDL compiler to produce the server's type library.
servernameglobal.f90 Contains the global data and functions for the server.
dllmain.f90
(DLL server)
Contains the exported functions that are required of all COM Server DLLs. This includes DllMain, DLLRegisterServer, DLLUnregisterServer, DllGetClassObject, and DllCanUnloadNow.
server.def
(DLL server)
Declares the exported functions for the linker.
exemain.f90
(EXE server)
Contains the main entry point of an EXE server. It also processes the command-line argument.
serverhelper.f90 Contains helper functions for the server.
clsfactty.f90 Contains definitions of the IClassFactory interface that is used to create instances of the classes defined by the server.
clsfact.f90 Contains methods of the IClassFactory interface that is used to create instances of the classes defined by the server.
classnameTY.f90 Defines a module that contains definitions of parameters and types used in the implementation of the class. It also contains the implementation of the IUnknown methods of the class. A separate instance of this file is generated for each class defined in the server.
interfacename.f90 Defines a module that contains the Fortran interfaces of the methods in the interface. It also contains the implementation of the Fortran "wrappers" that are called directly from the class' VTBL and call the methods implemented by the user. A separate instance of this file is generated for each interface defined in the class.