[Previous] [Contents] [Next]

Marshaled Interface Pointers

The ORPC protocol transmits method parameters in the NDR format specified by OSF DCE RPC. NDR specifies exactly how all the primitive data types understood by IDL should be marshaled into data packets for network transmission. The only extension made to the NDR standard by ORPC is support for marshaled interface pointers. Use of the iid_is IDL keyword9 in an interface definition constitutes what can be considered a new primitive data type that can be marshaled: an interface pointer.

The term interface pointer is problematic because it conjures up a mental picture of a pointer to a pointer to a v-table structure that contains pointers to functions. But once it is marshaled into a data packet, an interface pointer does not look like that at all. It is a symbolic representation of access to an object and is therefore called an object reference. The format of a marshaled interface pointer is governed by the MInterfacePointer structure defined below.

// Wire representation of a marshaled interface pointer

typedef struct tagMInterfacePointer 
{
    ULONG ulCntData;                    // Size of data
    [size_is(ulCntData)] BYTE abData[]; // Data (OBJREF)
} MInterfacePointer;

After the ulCntData field, which defines the size of the structure, comes the abData byte array, which contains the actual object reference in a structure called an OBJREF. An OBJREF is the data type used to represent a reference to an object. The definition of the OBJREF structure is shown in the following code. Notice that OBJREF takes one of three forms, depending on the type of marshaling being employed: standard, handler, or custom.10

// OBJREF is the format of a marshaled interface pointer.
typedef struct tagOBJREF 
{
    unsigned long  signature;        // Must be OBJREF_SIGNATURE
    unsigned long  flags;            // OBJREF flags
    GUID           iid;              // IID

    [switch_is(flags), switch_type(unsigned long)] union {
        [case(OBJREF_STANDARD)] struct {
            STDOBJREF         std;        // Standard OBJREF
            DUALSTRINGARRAY   saResAddr;  // Resolver address
        } u_standard;

        [case(OBJREF_HANDLER)] struct {
            STDOBJREF         std;        // Standard OBJREF
            CLSID             clsid;      // CLSID of handler code
            DUALSTRINGARRAY   saResAddr;  // Resolver address
        } u_handler;

        [case(OBJREF_CUSTOM)] struct {
            CLSID           clsid;        // CLSID of unmarshaling code
            unsigned long   cbExtension;  // Size of extension data
            unsigned long   size;         // Size of data that follows
            [size_is(size), ref] byte *pData; // Extension plus class-
                                              //   specific data
        } u_custom;
    } u_objref;
} OBJREF;

The OBJREF structure begins with a signature field that is defined as the unsigned long hexadecimal value 0x574F454D. Interestingly, if you arrange this value in little endian format (4D 45 4F 57) and then convert each byte to its ASCII equivalent, the resulting characters spell MEOW. The great thing about the MEOW structure (a popular nickname for the OBJREF structure) is that when you scan through the mountains of packets captured by the Network Monitor utility it is easy to tell when you've hit upon an object reference: just say MEOW. Note that regardless of the format of the remainder of the NDR data, the wire representation of a marshaled interface pointer is always stored in little endian format.

Following the MEOW signature field is the flags field of the OBJREF structure, which identifies the type of object reference. You can set the flags field to OBJREF_STANDARD (1), OBJREF_HANDLER (2), or OBJREF_CUSTOM (4) to indicate the type of interface marshaling. The iid field is the last omnipresent field of the OBJREF structure; it specifies the IID of the interface being marshaled. Figure 19-8 shows a network packet that was captured as a result of the request PDU shown previously. There the CoCreateInstanceEx function had been called to request the object's IUnknown interface pointer. In this figure, you can see that the response PDU has returned to the client. It contains the marshaled interface pointer (decorated by MEOW and labeled 5) of the object's IUnknown interface.

Click to view at full size.

Figure 19-8. The response PDU captured when IRemoteActivation::RemoteActivation returns an OBJREF.

The Standard Object Reference

As you can see in Figure 19-8, the flags field of the OBJREF structure indicates that standard marshaling (OBJREF_STANDARD) is being used. Based on this field, the remainder of the structure contains a structure of the type STDOBJREF followed by a DUALSTRINGARRAY structure. Here is the definition of the STDOBJREF structure:

typedef struct tagSTDOBJREF 
{
    unsigned long  flags;        // SORF_ flags
    unsigned long  cPublicRefs;  // Count of references passed
    OXID           oxid;         // OXID of server with this OID
    OID            oid;          // OID of object with this IPID
    IPID           ipid;         // IPID of interface
} STDOBJREF;

The first field of the STDOBJREF structure specifies flags relating to the object reference. Although most of the possible settings for the flags parameter are reserved for use by the system, you can use the SORF_NOPING flag (0x1000) to indicate that the object does not need to be pinged. The ORPC network protocol uses pinging to implement a sophisticated garbage collection mechanism.11 The second field of the STDOBJREF structure, cPublicRefs, specifies the number of reference counts on the IPID that are being transferred in this object reference. You can allocate multiple reference counts on an interface as an optimization to avoid making remote method calls every time the client calls IUnknown::AddRef.12

The third field of the STDOBJREF structure specifies the OXID of the server that owns the object. Although an IPID is used to identify an interface of an object hosted by a component, an IPID alone does not contain enough information to actually carry out a method invocation because the RPC infrastructure uses strings to specify the binding information needed to carry out a remote call. These strings, called RPC string bindings, contain information such as the underlying network protocol and security subsystem that should be used to carry out the call as well as the network address of the server machine on which the component is running. An unsigned hyper (64-bit integer), also called an OXID, is used to represent this connection information. Before making a call, the client translates an OXID into a set of string bindings that the RPC system understands.13

The fourth field of the STDOBJREF structure specifies the OID of the object that implements the interface being marshaled. OIDs are 64-bit values used as part of the pinging mechanism. The final parameter of the STDOBJREF structure is the actual IPID of the interface being marshaled.

The DUALSTRINGARRAY Structure

As part of an object reference, the STDOBJREF structure is followed by the DUALSTRINGARRAY structure. The DUALSTRINGARRAY structure is a container for a large array that contains two parts, STRINGBINDING structures and SECURITYBINDING structures. The definition of the DUALSTRINGARRAY structure is shown here:

// DUALSTRINGARRAYs are the return type for arrays of network 
// addresses, arrays of endpoints, and arrays of both used in 
// many ORPC interfaces.
typedef struct tagDUALSTRINGARRAY 
{
    unsigned short    wNumEntries;     // Number of entries 
                                       //   in array
    unsigned short    wSecurityOffset; // Offset of security 
                                       //   info

    // The array contains two parts, a set of STRINGBINDINGs
    // and a set of SECURITYBINDINGs. Each set is terminated by 
    // an extra 0. The shortest array contains four 0s. 

    [size_is(wNumEntries)] unsigned short aStringArray[];
} DUALSTRINGARRAY;

The first two fields of the DUALSTRINGARRAY structure simply specify the total number of entries in the array (wNumEntries) and the offset at which the STRINGBINDING structures end and the SECURITYBINDING structures begin (wSecurityOffset). The array itself is pointed to by the aStringArray field.

A STRINGBINDING structure represents the connection information needed to bind to an object. The layout of the STRINGBINDING structure is shown here:

// This is the return type for arrays of string bindings or 
// protocol sequences (protseqs) used by many ORPC interfaces.

typedef struct tagSTRINGBINDING 
{
    unsigned short    wTowerId;      // Cannot be 0
    unsigned short    aNetworkAddr;  // Zero-terminated
} STRINGBINDING;

The first field of the STRINGBINDING structure, wTowerId, specifies the network protocol that can be used to reach the server using the second parameter, aNetworkAddr. The aNetworkAddr parameter is a Unicode string specifying the network address of the server. For example, if the wTowerId value is set to the tower identifier NCADG_IP_UDP,14 a valid network address for aNetworkAddr would be 199.34.58.4. The following table lists the valid tower identifiers for common protocols that can be used with the wTowerId parameter.

Tower Identifier Value Description
NCADG_IP_UDP 0x08 Connectionless UDP
NCACN_IP_TCP 0x07 Connection-oriented TCP
NCADG_IPX 0x0E Connectionless Internetwork Packet Exchange (IPX) Protocol
NCACN_SPX 0x0C Connection-oriented Sequenced Packet Exchange (SPX) Protocol
NCACN_NB_NB 0x12 Connection-oriented NetBEUI over NetBIOS
NCACN_NB_IPX 0x0D Connection-oriented NetBIOS over IPX
NCACN_HTTP 0x1F Connection-oriented HTTP

Each STRINGBINDING structure ends with a null character to indicate the end of the aNetworkAddr string. The last STRINGBINDING in a DUALSTRINGARRAY is indicated by the presence of two extra 0 bytes. After that come the SECURITYBINDING structures. The definition of the SECURITYBINDING structure is shown here:

// This value indicates that the default authorization 
// should be used.
const unsigned short COM_C_AUTHZ_NONE = 0xffff;

typedef struct tagSECURITYBINDING 
{
    unsigned short    wAuthnSvc;     // Must not be 0
    unsigned short    wAuthzSvc;     // Must not be 0
    unsigned short    aPrincName;    // NULL terminated
} SECURITYBINDING;

The SECURITYBINDING structure contains fields indicating the authentication service, wAuthnSvc, and the authorization service, wAuthzSvc, to be used. The wAuthzSvc field is typically set to 0xFFFF, which indicates that default authorization should be used.

The IRemUnknown Interface

IRemUnknown is a COM+ interface that handles reference counting and interface querying for remote objects. As its name suggests, IRemUnknown is the remote version of the holy IUnknown interface. Clients use the IRemUnknown interface to manipulate reference counts and request new interfaces based on IPIDs held by the client. Following standard reference counting rules in COM+, references are kept per interface rather than per object. The definition of the IRemUnknown interface is shown in the following IDL notation:

// The remote version of IUnknown is used by clients to 
// query for new interfaces, get additional references (for 
// marshaling), and release outstanding references.
[
    object,
    uuid(00000131-0000-0000-C000-000000000046)
]
interface IRemUnknown : IUnknown
{
    HRESULT RemQueryInterface
    (
        [in] REFIPID        ripid, // Interface to QueryInterface on
        [in] unsigned long  cRefs, // Count of AddRefs requested
        [in] unsigned short cIids, // Count of IIDs that follow
        [in, size_is(cIids)] IID* iids, // IIDs to QueryInterface for
        [out, size_is(,cIids)]
        REMQIRESULT**       ppQIResults // Results returned
    );

    HRESULT RemAddRef
    (
        [in] unsigned short    cInterfaceRefs,
        [in, size_is(cInterfaceRefs)]
        REMINTERFACEREF        InterfaceRefs[],
        [out, size_is(cInterfaceRefs)]
        HRESULT*               pResults
    );

    HRESULT RemRelease
    (
        [in] unsigned short    cInterfaceRefs,
        [in, size_is(cInterfaceRefs)]
        REMINTERFACEREF        InterfaceRefs[]
    );
}

A component developer never implements the IRemUnknown interface because the OXID object associated with each apartment already provides an implementation of this interface. The standard IUnknown interface is never remoted in COM+. The IRemUnknown interface is remoted in its place and results in local calls to QueryInterface, AddRef, and Release on the server. Client applications can call the IUnknown::AddRef method as often as they want. COM+ remotes calls to IUnknown::AddRef only when the first AddRef call is made; IUnknown::Release is called only for the final Release.

The IRemUnknown::RemQueryInterface method differs from the IUnknown::QueryInterface method in that it can request several interface pointers in one call. The standard IUnknown::QueryInterface method is actually used to carry out this request on the server side. This optimization is designed to reduce the number of round-trips executed. The array of REMQIRESULT structures returned by RemQueryInterface contains the HRESULT from the QueryInterface call executed for each requested interface, as well as the STDOBJREF structure containing the marshaled interface pointer itself. The definition of the REMQIRESULT structure is shown here:

typedef struct tagREMQIRESULT
{
    HRESULT      hResult;    // Result of call
    STDOBJREF    std;        // Data for returned interface
} REMQIRESULT;

The IRemUnknown::RemAddRef and IRemUnknown::RemRelease methods increase and decrease, respectively, the reference count of the object referred to by an IPID. Like RemQueryInterface, RemAddRef and RemRelease differ from their local counterparts; they can increase and decrease the reference count of multiple interfaces by an arbitrary amount in a single remote call. Imagine a scenario in which an object that receives a marshaled interface pointer wants to pass that pointer to some other object. According to the COM+ reference counting rules, AddRef must be called before this interface pointer can be passed to another object, resulting in two round-trips, one to get the interface pointer and another to increment the reference counter. The caller can optimize this process by requesting multiple references in one call. Thereafter, the interface pointer can be given out multiple times without additional remote calls to increment the reference counter.

The Windows implementation of COM+ typically requests five references when marshaling an interface pointer, which means that the client process receiving the interface pointer can marshal it to four different apartments in the current process or in other processes. Only when the client attempts to marshal the interface pointer for the fifth time does COM+ make a remote call to the object to request an additional reference. Also, in the interest of performance on the client side, COM+ typically does not immediately translate each call to IUnknown::AddRef or IUnknown::Release into a remote call to IRemUnknown::RemAddRef or IRemUnknown::RemRelease. Instead, it defers a remote call to the RemRelease method until all interfaces on an object have been released locally. Only then is a single RemRelease call made, with instructions to decrement the reference counter for all interfaces by the necessary amount.

It is important to note that in this scenario, when one component returns the interface pointer of another component to a client process, COM+ never allows one proxy to communicate with another proxy. For example, if client process A calls object B, which then returns an interface pointer for object C, any subsequent calls made by client A to object C are direct. This happens because the marshaled interface pointer contains information about how to reach the machine on which the actual object instance exists. In order for object B to call object C, object B must keep track of object C's OXID, IP address, IPID, and so on. When object B hands client A a pointer to object C, object B scribbles all that information into a new object reference (OBJREF) for client A. Object B is no longer part of the relationship, which saves network bandwidth and improves overall performance.

After calling the CoCreateInstanceEx function to instantiate the remote component, our client process possesses an initial IUnknown interface pointer. Typically, this call is followed by a call to the IUnknown::QueryInterface method to request another interface, as shown in the following code fragment:

hr = pUnknown->QueryInterface(IID_ISum, (void**)&pSum);

When the client process calls the IUnknown::QueryInterface method to request an interface pointer for ISum, the proxy manager in the client's address space calls the IRemUnknown::RemQueryInterface method on the server. Figure 19-9 shows the network packet that is transmitted for the RemQueryInterface method call. In this packet, you can clearly see that ORPC is requesting a count of five references for the ISum interface pointer.

Click to view at full size.

Figure 19-9. The request PDU transmitted for the IRemUnknown::RemQueryInterface(IPID, 5, 1, IID_ISum) call.

On the server side, the actual IUnknown::QueryInterface call is executed to request an ISum interface pointer from the component. This interface pointer is then returned to the client in the marshaled form of a standard object reference (STDOBJREF). Figure 19-10 shows the response PDU that is returned to the client.

Click to view at full size.

Figure 19-10. The response PDU transmitted for the IRemUnknown::RemQueryInterface call.

To prevent a malicious application from making a call to IRemUnknown::RemRelease and purposefully trying to force an object to unload while other clients might still be using it, a client can request private references. Private references are stored with the client's identity so that one client cannot release the private references of another. However, when you pass an interface pointer, private references cannot be provided from one object to another. Each client must request and release its own private references by explicitly calling the RemAddRef and RemRelease methods. These methods accept an argument that is an array of REMINTERFACEREF structures. The REMINTERFACEREF structure specifies an IPID and the number of public and private references that are being requested or released by the client. The definition of the REMINTERFACEREF structure is shown here:

typedef struct tagREMINTERFACEREF
{
    IPID           ipid;      // IPID to AddRef/Release
    unsigned long  cPublicRefs;
    unsigned long  cPrivateRefs;
} REMINTERFACEREF;

The IRemUnknown2 Interface

The IRemUnknown2 interface was introduced in version 5.2 of the ORPC protocol. Derived from the IRemUnknown interface, IRemUnknown2 adds the RemoteQueryInterface2 method, which enables clients to retrieve interface pointers to objects that supply additional data beyond the STDOBJREF in their marshaled interface packets. Like RemQueryInterface, this method queries for zero or more interfaces using the interface behind the IPID. Instead of returning the STDOBJREF marshaled interface packet, this method can return any marshaled data packet in the form of a byte array (including a traditional STDOBJREF). The IDL definition of the IRemUnknown2 interface is shown in the following code:

interface IRemUnknown2 : IRemUnknown
{
    HRESULT RemQueryInterface2
    (
        [in] REFIPID                            ripid,
        [in] unsigned short                     cIids,
        [in, size_is(cIids)] IID                *iids,
        [out, size_is(cIids)] HRESULT           *phr,
        [out, size_is(cIids)] MInterfacePointer **ppMIF
    );
}