Taking Advantage of the OLE Automation Marshaller

Microsoft Corporation

October 18, 1995

Click to open or copy the files for the VTBLBIND sample application.

Abstract

This sample, and accompanying article, will illustrate how to implement and call OLE Automation interfaces that support VTBL binding by providing a type library and by using the marshaller provided by OLE Automation.

Overview

The VTBLBIND samples, CLIENT and SERVER, demonstrate the use of the OLE Automation marshaller to marshall Component Object Model (COM) interfaces that you define.

A COM interface can be marshalled using standard marshalling or custom marshalling. In standard marshalling, proxy and stub code will marshall and unmarshall the parameters and/or return values of an interface method call between processes. The Microsoft Interface Definition Language (MIDL) compiler can be used to generate proxy and stub code to standard-marshall a COM interface.

In custom marshalling, the object will implement IMarshal to marshall the entire object to another process space. See the COM Specification (MSDN Library, Specifications) and Chapter 6 of Kraig Brockschmidt's Inside OLE, Second Edition (MSDN Library, Books), for further information on custom marshalling.

An interface can also be standard-marshalled by a marshaller provided by OLE Automation. This OLE Automation marshaller can marshall any custom interface that is described using a type library and that uses a limited set of data types called the OLE Automation compatible types. This marshaller implements OLE Automation's VTBL binding. VTBL binding is the mechanism used by any COM interface client to bind to the implementation of the COM interface in the server. OLE Automation uses this term to distinguish it from other binding mechanisms that it supports, such as late binding and ID binding.

Building the Samples

The samples presented here require that the Win32® Software Development Kit (SDK) be installed. Be sure that your MSTOOLS environment variable points to the root of your Win32 SDK directory (typically C:\MSTOOLS).

Running the Samples

  1. Register the server by running SERVER.EXE /REGSERVER.

  2. Run CLIENT.EXE in the client directory. CLIENT.EXE will use CoCreateInstance to create an instance of SERVER.EXE. It will then use IUnknown::QueryInterface to get the IVtblServer custom interface from the server. IVtblServer::put_Message is called to pass the server a string that the server will display later. IVtblServer::DisplayMessage is called with the coordinates at which the string is to be displayed and the number of times the string is to be displayed. The server will display the string "Hello, Universe" seven times, starting at coordinate (5, 3), as specified by the client. The client will then pause for ten seconds and release the server. The server will shut down when its last reference is released. Finally, the client will shut down.

Key Points

This sample shows how to create a COM interface that can be standard-marshalled by OLE Automation, how to create a type library that describes the interface, and how to register the interface so that OLE Automation will marshall it.

Marshalling code for COM interfaces is usually generated using the MIDL compiler. However, this marshalling code doesn't support 16-to-16-bit and 16-to-32-bit interoperability. (See the Win32 SDK online documentation, "Interoperability Using Custom Interfaces [either MIDL or Manually Written Marshalling]" for a description of this limitation.)

The OLE Automation marshaller supports 16-to-16-bit and 16-to-32-bit interoperability for local servers (.EXE servers) and can be used as a workaround to the interoperability problem if you are interested in 16-bit support. However, the types that can be used in interfaces that can be marshalled by the OLE Automation marshaller are restricted to the OLE-Automation–compatible types discussed below. In addition, because the OLE Automation marshaller is generic, it is not as efficient as the marshalling code generated by MIDL.

Architecture

Server Architecture

The following steps describe the creation of an interface that can be marshalled by the OLE Automation marshaller.

Step 1

The server describes the custom interface in the Object Description Language (ODL) in SERVER.ODL. For example, IVtblServer is described as follows:

   [
      uuid(2ED17402-F80F-11ce-8161-00AA0060D733),       // IID_IVtblServer
      oleautomation
    ]
    interface IVtblServer : IUnknown
    {                
        HRESULT put_Message([in] BSTR Message);
        HRESULT get_Message([out] BSTR *pMessage);
        HRESULT DisplayMessage([in] SAFEARRAY(unsigned char) Coordinate,
                               [in] short Times);
    }

put_Message is used to pass a string to be displayed to the server.

DisplayMessage will display the string "Times times" at the coordinate (COORD structure) passed using the SAFEARRAY(unsigned char).

get_Message will return the string that will be displayed by the server.

The IVtblServer custom interface derives from IUnknown and has three methods. The uuid attribute specifies the interface ID (IID) of the interface. The oleautomation attribute specifies that the interface can be marshalled by the marshaller provided by OLE Automation.

The ODL syntax is described in Chapter 7 of OLE Programmer's Reference, Volume 2, second edition (see "Microsoft Press Technical Reference Books" in the MSDN Library for ordering information).

The types that can be used by the custom interface are restricted to OLE Automation compatible types found in the following table.

OLE Automation Compatible Types

Type Description
short 16-bit signed integer.
long 32-bit signed integer.
float 32-bit IEEE floating-point number.
double 64-bit IEEE floating-point number.
CURRENCY 8-byte fixed-point number.
DATE 64-bit floating-point fractional number of days since December 30, 1899.
BSTR Length-prefixed string. Strings must be passed using this type. BSTRs must be created and manipulated using the functions described in OLE Programmer's Reference, Vol. 2.
Boolean Data item that can have the values True or False. The size maps to VARIANT_BOOL.
VARIANT VARIANT type. This VARIANT can contain any type marked with a [V] in the VARENUM enumeration in oaidl.h. VARIANTs that contain other types cannot be marshalled by OLE Automation.
SCODE OLE SCODE.
unsigned char Unsigned 8-bit data item. A SAFEARRAY(unsigned char) can be used to pass binary data.
IUnknown* IUnknown interface pointer. Any OLE interface can be passed using this type.
IDispatch* IDispatch interface pointer.
SAFEARRAY(type) Array of type. type can be any of the types above this row. type cannot be any of the types below this row. SAFEARRAYS must be created and manipulated using the SAFEARRAY functions described in the OLE Programmer's Reference, Vol. 2.
short* Pointer to short. Can be used to pass a short by reference.
long* Pointer to long. Can be used to pass a long by reference.
float* Pointer to float. Can be used to pass a float by reference.
double* Pointer to double. Can be used to pass a double by reference.
CURRENCY* Pointer to CURRENCY. Can be used to pass a CURRENCY by reference.
DATE* Pointer to DATE. Can be used to pass a DATE by reference.
BSTR* Pointer to BSTR. Can be used to pass a BSTR by reference.
boolean* Pointer to boolean. Can be used to pass a boolean by reference.
VARIANT* Pointer to variant. Can be used to pass a VARIANT by reference.
SCODE* Pointer to SCODE. Can be used to pass an SCODE by reference.
unsigned char* Pointer to unsigned char. Can be used to pass an unsigned char by reference.
IUnknown** Pointer to IUnknown*. Can be used to pass an IUnknown interface by reference.
IDispatch** Pointer to IDispatch*. Can be used to pass an IDispatch interface by reference.
SAFEARRAY(type) * Pointer to SAFEARRAY. Can be used to pass a SAFEARRAY by reference.
HRESULT Return type used for reporting error information in interfaces. Can only be used as a return type. Use SCODE for parameter types. SCODE and HRESULT are the same in Win32.

The marshaller provided by OLE Automation cannot understand structures. Structures can be passed using a SAFEARRAY(unsigned char). The sample demonstrates how a COORD structure is passed in this manner. Also see the article "Passing Structures in OLE Automation" (http://www.microsoft.com/kb/articles/q122/2/89.htm) for a discussion of passing structures.

The coclass entry in SERVER.ODL describes the server object and the interfaces that it supports. The uuid attribute of the coclass is the CLSID of the server and is used in the CoCreateInstance call in the client to create the server.

    [
       uuid(2ED17401-F80F-11ce-8161-00AA0060D733)        // CLSID_VtblServer
    ]
    coclass VtblServer
    {   
        [default] interface IVtblServer;
    }

SERVER.ODL is compiled by MKTYPLIB.EXE to produce the SERVER.TLB type library and the IVtblServer.h interface declaration file.

Step 2

SERVER.H declares CVtblServer, which implements IVtblServer and CVtblServerCF, which in turn implements IClassFactory.

Step 3

SERVER.CPP implements CVtblServer and CVtblServerCF.

Step 4

The server is a self-registering COM server (using the Self-Registering Server specification defined originally by the OLE Controls specification). The server will register its CLSID, full path, type library, and the custom interface in the system registry when it is run with the /RegServer command-line argument. The type library and custom interface are registered as follows by the RegisterTypeLib OLE function.

The custom interface, IVtblServer, is registered as follows:

HKEY_CLASSES_ROOT\Interface\{2ED17402-F80F-11ce-8161-00AA0060D733} = IVtblServer
HKEY_CLASSES_ROOT\Interface\{2ED17402-F80F-11ce-8161-00AA0060D733}\
   TypeLib = {2ED17400-F80F-11ce-8161-00AA0060D733}
HKEY_CLASSES_ROOT\Interface\{2ED17402-F80F-11ce-8161-00AA0060D733}\
   ProxyStubClsid = {00020424-0000-0000-C000-000000000046}
HKEY_CLASSES_ROOT\Interface\{2ED17402-F80F-11ce-8161-00AA0060D733}\
   ProxyStubClsid32 = {00020424-0000-0000-C000-000000000046}

The type library is registered as follows:

HKEY_CLASSES_ROOT\TypeLib\{2ED17400-F80F-11ce-8161-00AA0060D733}
HKEY_CLASSES_ROOT\TypeLib\{2ED17400-F80F-11ce-8161-00AA0060D733}\
   1.0 = VtblServer 1.0 Type Library
HKEY_CLASSES_ROOT\TypeLib\{2ED17400-F80F-11ce-8161-00AA0060D733}\
   1.0\0\win32 = server.tlb

Client Architecture

The following steps describe the creation of a client that can use the custom interface:

  1. IVtblServer.h, generated by MKTYPLIB.EXE in the server project, is copied into the client's directory. This file contains the CLSID of the server and the IID of the IVtblServer custom interface.

  2. In CLIENT.CPP, the client creates an instance of the server using CoCreateInstance and the CLSID of the server from IVtblServer.h. The custom interface is then obtained using IUnknown::QueryInterface and the IID from IVtblServer.h. IVtblServer::put_Message is called. A string must be passed as a BSTR. SysAllocString is used to allocate the BSTR that is passed to ::put_Message. IVtblServer::DisplayMessage is used to pass a COORD structure and a short. The COORD structure is passed in a SAFEARRAY(unsigned char) because the OLE Automation marshaller cannot understand structures.

GUID generation

This sample required generation of three GUIDs using the GUIDGEN.EXE tool: the CLSID of the server, the GUID of the type library, and the IID of the custom interface.

Related Information

In addition to compile-time VTBL binding, OLE Automation also allows run-time ID binding and late binding to the custom interface implementation through another interface called IDispatch. See chapter 1 of OLE Programmer's Reference, Vol. 2, second edition, for details of these binding mechanisms. It is possible for an interface to support VTBL binding, ID binding, and late binding by deriving from IDispatch and by using the dual attribute in the declaration of the interface in the .ODL file. Such an interface is called a dual interface.

Clients that use VTBL binding to access the dual interface will use the OLE automation marshaller, while clients that use ID binding and late binding will use IDispatch::Invoke to invoke methods in the dual interface. The HELLO (version 2.0) and LINES samples that ship with the Win32 SDK demonstrate the implementation of these dual interfaces. Chapters 1 and 2 of OLE Programmer's Reference, Vol. 2, second edition describe the implementation of dual interfaces.