[Previous] [Contents] [Next]

The OXID Resolver

The OXID Resolver is a service that runs on every machine that supports COM+. It performs two important duties:

Similar to the way CoCreateInstanceEx incorporates the functionality of CoGetClassObject and IClassFactory::CreateInstance, the IRemoteActivation interface incorporates the functionality of the IRemUnknown and IOXIDResolver interfaces so that only one round-trip is needed to activate an object. The OXID Resolver resides at the same endpoints as the SCM (as described earlier in the section titled "RemoteActivation.") Like the IRemoteActivation interface, the OXID Resolver implements an RPC interface (not a COM+ interface) named IOXIDResolver, which is shown in IDL notation in the following code. Notice that the object keyword is conspicuously absent from the interface header, indicating that this is not a COM+ interface.

[ // No object keyword here. Not a COM+ interface!
    uuid(99fcfec4-5260-101b-bbcb-00aa0021347a),
    pointer_default(unique)
]
interface IOXIDResolver
{
    // Method to get the protocol sequences, string bindings,
    // and machine ID for an object server given its OXID.
    [idempotent] error_status_t ResolveOxid
    (
    [in]       handle_t        hRpc,
    [in]       OXID            *pOxid,
    [in]       unsigned short  cRequestedProtseqs,
    [in,  ref, size_is(cRequestedProtseqs)]
               unsigned short  arRequestedProtseqs[],
    [out, ref] DUALSTRINGARRAY **ppdsaOxidBindings,
    [out, ref] IPID            *pipidRemUnknown,
    [out, ref] DWORD           *pAuthnHint
    );
    // Simple ping is used to ping a set. Client machines use
    // this technique to inform the object exporter that it is 
    // still using the members of the set. Returns S_TRUE if the
    // SetId is known by the object exporter, S_FALSE if not.
    [idempotent] error_status_t SimplePing
    (
    [in]  handle_t  hRpc,
    [in]  SETID    *pSetId  // Must not be 0
    );
    // Complex ping is used to create sets of OIDs to ping. The
    // whole set can subsequently be pinged using SimplePing,
    // thus reducing network traffic.
    [idempotent] error_status_t ComplexPing
    (
    [in]       handle_t        hRpc,
    [in, out]  SETID          *pSetId, // An in value of 0 on 
                                       // first call for new set
    [in]       unsigned short  SequenceNum,
    [in]       unsigned short  cAddToSet,
    [in]       unsigned short  cDelFromSet,
    [in, unique, size_is(cAddToSet)]   OID AddToSet[],
                 // Add these OIDs to the set.
    [in, unique, size_is(cDelFromSet)] OID DelFromSet[],
                 // Remove these OIDs from the set.
    [out]      unsigned short *pPingBackoffFactor
                 // 2^factor = multiplier
    );
    // In some cases, the client might be unsure that a 
    // particular binding will reach the server--for example, 
    // when the OXID bindings have more than one TCP/IP binding. 
    // This call can be used to validate the binding from 
    // the client.
        [idempotent] error_status_t ServerAlive
    (
    [in]       handle_t        hRpc
    );
}

NOTE
The idempotent flag specifies that a method does not modify the state of an object and returns the same results each time it is called. The RPC run-time library can invoke idempotent methods multiple times without adverse effects.

When the OXID Resolver is presented with an OXID, it obtains the associated RPC string binding necessary to connect to the object. On each machine, the OXID Resolver maintains a cached local table of mappings of OXIDs and their associated RPC string bindings. When asked to resolve an OXID into its associated string binding, the OXID Resolver first checks its cached local table for the OXID. If it finds the OXID, it returns the string binding immediately. If it cannot find the OXID, it contacts the server's OXID Resolver to request resolution of the OXID into a string binding.

The client machine's OXID Resolver then caches the string binding information provided by the server. This optimization enables the OXID Resolver to quickly resolve that OXID for other clients on the same machine that might want to connect in the future. If the client were to pass the object reference to a process running on a third machine, that computer's OXID Resolver service would not have a cached copy of the OXID's string bindings and thus would be obliged to make a remote call to the server to resolve the OXID for itself.

The purpose of the first method, IOXIDResolver::ResolveOxid, shown in the preceding code, is to resolve an OXID into the string bindings that are necessary to access an OXID object. The OXID being resolved is passed as the second parameter, pOxid, to the ResolveOxid method. When calling the ResolveOxid method, the client specifies what protocol sequences it is prepared to use when accessing the object, starting with the most preferred. The client passes this information in the arRequestedProtseqs array argument. The server's OXID Resolver attempts to resolve the OXID and then returns an array of DUALSTRINGARRAY structures, ppdsaOxidBindings, which contains the string binding information—again in decreasing order of preference—that can be used to connect to the specified OXID.

The OXID resolution process is described in the following steps:

  1. A server process calls CoRegisterClassObject to register itself with COM+.
  2. The OXID Resolver on the server machine caches a reference to the object.
  3. A client process calls CoCreateInstanceEx and receives an object reference for the object running on the server.
  4. The client asks its OXID Resolver to resolve the OXID for the server object.
  5. The client's OXID Resolver calls IOXIDResolver::ResolveOxid to request that the server's OXID Resolver return the string bindings for the OXID.
  6. The server's OXID Resolver performs a lookup in its local table and returns the desired string bindings to the client's OXID Resolver.
  7. The client's OXID Resolver caches the best string binding in its local table for future use and then returns the string binding for the OXID to the client process.
  8. The client binds to the object using the given string binding. The client can now invoke methods on the object.

Because machines can have many network protocols installed, allocating endpoints for each available protocol sequence can be a time-consuming and resource-intensive operation. However, the server typically registers all available protocol sequences at initialization time. As an optimization, the OXID Resolver can decide to defer protocol registration. To implement lazy protocol registration, the server waits until a client machine calls its IOXIDResolver::ResolveOxid method. Rather than registering all available protocols at initialization time, the implementation of the ResolveOxid method registers only the protocols requested by the client at the time of OXID resolution. The network protocols available to COM+ are in the HKEY_LOCAL_MACHINE\Software\Microsoft\Rpc registry key under the DCOM Protocols named-value. There you'll find the supported protocols listed in order of preference.

Version 5.2 of the ORPC protocol added the ResolveOxid2 method to the IOXIDResolver interface. This method enables a client to determine the version of the ORPC protocol used by the server when the server requests OXID resolution. Notice the addition of the last parameter in the IDL definition of the IOXIDResolver::ResolveOxid2 method, shown here in boldface:

[idempotent] error_status_t ResolveOxid2
(
    [in]       handle_t        hRpc,
    [in]       OXID            *pOxid,
    [in]       unsigned short  cRequestedProtseqs,
    [in,  ref, size_is(cRequestedProtseqs)]
               unsigned short  arRequestedProtseqs[],
    [out, ref] DUALSTRINGARRAY **ppdsaOxidBindings,
    [out, ref] IPID            *pipidRemUnknown,
    [out, ref] DWORD           *pAuthnHint,
    [out, ref] COMVERSION      *pComVersion
);