Microsoft Corporation
October 18, 1995
Click to open or copy the files for the VTBLBIND sample application
This sample, and accompanying article, will illustrate how to implement and call Automation interfaces that support VTBL binding by providing a type library and by using the marshaller provided by Automation.
The VTBLBIND samples, CLIENT and SERVER, demonstrate the use of the 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 Automation. This 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 Automation compatible types. This marshaller implements 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. Automation uses this term to distinguish it from other binding mechanisms that it supports, such as late binding and ID binding.
The samples presented here require that the Platform SDK be installed. Be sure that your MSTOOLS environment variable points to the root of your Platform SDK directory.
This sample shows how to create a COM interface that can be standard-marshalled by Automation, how to create a type library that describes the interface, and how to register the interface so that 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 Platform SDK online documentation, "Interoperability Using Custom Interfaces [either MIDL or Manually Written Marshalling]" for a description of this limitation.)
The 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 Automation marshaller are restricted to the OLE-Automation–compatible types discussed below. In addition, because the Automation marshaller is generic, it is not as efficient as the marshalling code generated by MIDL.
The following steps describe the creation of an interface that can be marshalled by the Automation marshaller.
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 Automation.
The ODL syntax is described in the Platform SDK.
The types that can be used by the custom interface are restricted to Automation compatible types found in the following table.
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 the Platform SDK. |
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 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 Platform SDK. |
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 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 Automation" (http://support.microsoft.com/support/kb/articles/q122/2/89.asp) 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.
SERVER.H declares CVtblServer, which implements IVtblServer and CVtblServerCF, which in turn implements IClassFactory.
SERVER.CPP implements CVtblServer and CVtblServerCF.
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
The following steps describe the creation of a client that can use the custom interface:
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.
In addition to compile-time VTBL binding, Automation also allows run-time ID binding and late binding to the custom interface implementation through another interface called IDispatch. See the Platform SDK 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 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 Platform SDK demonstrate the implementation of these dual interfaces. The Platform SDK describes the implementation of dual interfaces.