The Distributed Component Object Model protocol (DCOM) is an application-level protocol for object-oriented remote procedure calls and is thus also called ``Object RPC'' or ORPC. The protocol consists of a set of extensions, layered on the distributed computing environment (DCE) RPC specification [CAE RPC], with which familiarity is assumed. Familiarity is also assumed with the COM (Component Object Model) specification [COM].
Object RPC specifies:
How calls are made on an object
How object references are represented, communicated, and maintained
There is a natural tendency in a networked environment to create entirely new application-level protocols as each new or seemingly unique combination of client, user agent, and server requirement arises.
While in many situations the definition of a new protocol is useful and justifiable, there are numerous features which have eventually been added to or required from each new protocol (or which become layered above them) as they evolve and become used in broader contexts.
A design goal of the DCOM protocol is the inherent support of standard features required by any distributed application communication protocol. In other words, to act as a framework to facilitate the construction of task-specific communication paths between distributed applications.
A common occurrence among user agents using the HTTP protocol today is the use of complex, task-specific Query URL syntax and HTTP POSTs. Also increasingly common is the POSTing and response with custom MIME types to and from resources which interpret the format and reply in same. While workable, there are drawbacks to this approach including increased complexity and work to produce and consume each new (and unique) format in the client and server, lessened ability to build task-specific firewalls for administration and security purposes, and in many cases definition of platform-centric formats.
DCOM utilizes the Network Data Representation (NDR) for arbitrary data types supported by DCE RPC.
DCOM leverages the authentication, authorization, and message integrity capabilities of DCE RPC. An implementation may support any level of DCE RPC security. Any connection or call can be made as secure or as insecure as negotiated by the client and the server.
In DCOM versioning of interfaces is done through identifiers which are universally unique (UUID's). To version a published interface, a new interface is defined with a different UUID to the updated specification. Multiple parties can simultaneously introduce ``revisions'' to interfaces by defining related but distinct interfaces without fear of colliding with each other's version numbers and without fear of breaking each other's down-level or up-level clients.
To date, the bulk of task-specific protocols (such as custom POSTs or MIME types using HTTP) have little or no concept of versioning at all, and simply ``narrow'' the incompatibility window by updating clients (typically pages which are being downloaded anyway) and servers (CGI scripts or other HTTP server infrastructure) simultaneously.
The Object RPC protocol highly leverages the DCE RPC network protocol (see the reference [CAE RPC]). This leverage occurs at both the specification level and the implementation level: the bulk of the implementation effort involved in implementing the DCOM network protocol is in fact that of implementing the DCE RPC network protocol on which it is built.
An actual COM network remote procedure call (hereinafter referred to as ``an ORPC'') is in fact a true DCE remote procedure call (herein termed ``a DCE RPC''), a ``Request PDU'' conforming to the specification for such calls per [CAE RPC].
In an ORPC, the object ID field of the invocation header as specified in [CAE RPC] contains an ``IPID''. An IPID is a 128-bit identifier known as an interface pointer identifier which represents a particular interface on a particular object in a particular server. As it is passed in the object ID fields of a DCE RPC, the static type of an IPID is in fact a UUID. However, IPIDs are scoped not globally but rather only relative to the server process which originally allocated them; IPIDs do not necessarily use the standard UUID allocation algorithm, but rather may use a machine-specific algorithm which can assist with dispatching.
In an ORPC, the interface ID field of the RPC header specifies the IID,
and
arguments are found in the body, as usual.
However, when viewed from the DCE
RPC perspective an additional first argument is always present that is absent
in the corresponding COM interface specification.
This argument is of type
ORPCTHIS
, which is described in Section
Section 22.3.7.
It is placed first in the body of the Request PDU, before the actual arguments
of the ORPC.
It is specifically legal for an ORPC to attempt a call a method number on a given interface which is beyond the number of methods believed by the server to be in that interface. Such calls should cause a fault.
Similarly, in a reply to an ORPC (a DCE RPC ``Response PDU''), when
viewed from
the DCE RPC perspective, an additional first return value is always present
that is absent in the corresponding COM interface specification.
This argument
is of type
ORPCTHAT
, which is described in
Section 22.3.8.
It is placed first in
the body of the Response PDU, before the actual return values of the ORPC.
An
ORPCTHAT
may also be present in a ``Fault PDU.''
In the Connectionless (CL)
Fault PDU, it is placed four bytes after the 32- bit fault code which normally
comprises the entire body of the PDU, thus achieving eight byte alignment
for
the
ORPCTHAT
; the intervening padding bytes are presently
reserved and must be
zero.
The PDU body length is of course set to encompass the entire body of
the
Fault PDU, including the
ORPCTHAT
.
In the Connection-Oriented
(CO) Fault PDU,
the
ORPCTHAT
is placed in the standard location allocated
for the ``stub data.''
In a Fault PDU of either form that results from an ORPC, if an
ORPCTHAT
is not
present then no other data may be substituted in its here-specified location
in
the PDU.
Although an IPID, from a logical perspective, semantically determines the server, object and interface to which a particular call should be directed, it does not by itself indicate the binding information necessary to actually carry out an invocation.
The protocol represents this ``how-to'' communication information in a 64-bit value called an object exporter identifier, otherwise known as an OXID. Conceptually, an OXID can be thought of as an implementation scope for an object, which may be a whole machine, a given process, a thread within that process, or other more esoteric implementation scope, but the exact definition of such scopes has no bearing on the protocol itself. Data structures in each Object Exporter keep track of the IPIDs exported and imported by that Object Exporter.
A given machine at any moment may support several OXIDs; however there
is
always a unique OXID Resolver service per machine which coordinates the
management of all the OXIDs on the machine.
The
OXID
Resolver
typically (but
not necessarily) resides at well-known ports (or endpoints, depending on your
terminology--one per protocol, of course) on the machine.
It supports
a DCE
RPC interface known as
IOXIDResolver()
, described in
Section
Section 22.5.2.1.
An OXID is used to determine the RPC string bindings that allow calls to reach their target IPID. Before making a call, the calling process must translate an OXID into a set of bindings that the underlying RPC implementation understands. It accomplishes this by maintaining a cache of these mappings. When the destination application receives an object reference, it checks to see if it recognizes the OXID. If it does not, then it asks the OXID Resolver which scopes the OXID specified in the object reference for the translation, and saves the resulting set of string bindings in a local table that maps OXIDs to string bindings.
Associated with each OXID (ie each Object Exporter) is COM object termed
an
``OXID object.'' OXID objects implement (at least) the
IRemUnknown()
interface, a COM interface through which remote management of reference counts
and
requests for interfaces are returned.
The DCOM protocol extends the Network Data Representation (NDR) standard specified in [CAE RPC] by defining what can be thought of as a new primitive data type that can be marshaled: that of an interface reference to an object. This is the only extension to NDR made by the DCOM protocol.
A marshaled interface reference is described by a type known as an
OBJREF
,
which is described in detail in Section
Section 22.3.3.
An
OBJREF
in actuality has several variations:
NULL
This is a reference to no object.
STANDARD
A standard remote reference.
Known as a
STDOBJREF
.
A
STDOBJREF
contains:
An IPID, which uniquely specifies the interface and object.
An object ID (OID), which uniquely specifies the identity of the object on which the IPID is found (scoped to the object exporter with which the object is associated).
An OXID, which identifies the scope where the implementation of the object is active, and can be used to reach the interface pointer.
A reference count, indicating the number of references to this IPID that are conveyed by this marshaling. This count, though typically a value of one, may in fact be zero, one, or more (see the next section).
Some flags, explained later.
CUSTOM
Contains a class ID (CLSID) and class-specific information.
The Custom format gives an object control over the representation of references to itself. For example, an immutable object might be passed by value, in which case the class-specific information would contain the object's immutable data.
A sub-case of the custom reference in which the class- specific information is standardized.
For example, an object wishes to be represented in client address spaces by a proxy object that caches state. In this case, the class-specific information is just a standard reference to an interface pointer that the handler (proxy object) will use to communicate with the original object.
Interface references are always marshaled in little-endian byte order, irrespective of the byte order prevailing in the remainder of the data being marshaled.
In the DCOM protocol, remote reference counting is conducted per interface (per IPID).
The actual increment and decrement calls are carried out using (respectively)
the
RemAddRef
and
RemRelease
methods
in
a COM interface known as
IRemUnknown()
found on the OXID object associated with each OXID, the IPID of which is
returned from the function
IOXIDResolver::ResolveOxid
(Section
Section 22.5.2.1).
In contrast to their analogues
in
IUnknown()
,
RemAddRef
and
RemRelease
can in one call increment or decrement the reference
count of many different IPIDs by an arbitrary amount; this allows for greater
network efficiency.
In the interests of performance, client COM implementations
typically do not immediately translate each local
AddRef()
and
Release()
into a remote
RemAddRef
and
RemRelease
.
Rather, the actual remote release of all interfaces
on an object is typically deferred until all local references to all interfaces
on
that object have been released.
Further, one actual remote reference count
may be
used to service many local reference counts; that is, the client infrastructure
may
multiplex zero or more local references to an interface into zero or one remote
references on the actual IPID.
To prevent a malicious application from calling
RemRelease
incorrectly, an application may request secure references.
In that case the
application
must call
RemAddRef
(and
RemRelease
later on)
securely and must request private references.
Private references are stored
by client
identity so one client cannot release another client's references.
DCOM requires
that
each client make a call to get his own secure references, rather then receiving
a
secure reference from someone who already has one.
This reduces the efficiency
of
interface marshalling because the client must make a callback.
The above reference counting scheme would be entirely adequate on its own if clients never terminated abnormally, but in fact they do, and the system needs to be robust in the face of clients terminating abnormally when they hold remote references. In a DCE RPC, one typically addresses this issue through the use of context handles. Context handles are not used, however, by the DCOM protocol, for reasons of expense. The basic underlying technology used in virtually all protocols for detecting remote abnormal termination is that of periodic pings. Naive use of RPC context handles would result in per object per client process pings being sent to the server. The DCOM protocol includes a pinging infrastructure to significantly reduce network traffic by relying on the client OXID Resolver implementation to do local management of client liveness detection, and having the actual pings be sent only on a machine by machine basis.
Pinging is carried out on a per-object (per OID), not a per- interface
(per-IPID) basis.
Architecturally, at its server machine, each exported object
(each exported OID) has associated with it a
pingPeriod
time value and a
numPingsToTimeOut
count which together (through their product)
determine the
overall amount of time known as the ``ping period'' that must elapse without
receiving a ping on that OID before all the remote references to IPIDs
associated with that OID can be considered to have expired.
Once expiration
has
occurred, the interfaces behind the IPIDs can, as would be expected, be
reclaimed solely on the basis of local knowledge, though the timeliness with
which this is carried out, if at all, is implementation specific detail of
the
server.
If the server COM infrastructure defers such garbage collection in
this
situation (perhaps because it has local references keeping the interface
pointer alive) and it later hears a ping , then it knows a network partition
healed.
It can consider the extant remote references to be reactivated and
can
continue remote operations.
When interface pointers are conveyed from one client to another, such
as being
passed as either [in] or [out] parameters to a call, the interface pointer
is
marshaled in one client and unmarshaled in the other.
In order to successfully
unmarshal the interface, the destination client must obtain at least one
reference count on the interface.
This is usually accomplished by passing
in
the marshaled interface
STDOBJREF
a
cPublicRefs
of (at least) one; the
destination client then takes ownership of that many (more) reference counts
to
the indicated IPID, and the source client then owns that many fewer reference
counts on the IPID.
It is legal, however, for zero reference counts to be
passed in the
STDOBJREF
; here, the destination client must
(if it does not
already have access to that IPID and thus have a non-zero reference count
for
it) before it successfully unmarshals the interface reference (concretely,
e.g., before
CoUnmarshalInterface()
returns) call to the
object exporter using
IRemUnknown::RemAddRef
to obtain a reference count for
it.
If the destination
client is in fact the object's server, then special processing is required
by
the destination client.
The remote reference counts being passed to it should,
in effect, be ``taken out of circulation,'' as what where heretofore remote
references are being converted into local references.
Thus, the reference
counts present in the
STDOBJREF
are in fact decremented
from the remote
reference count for the IPID in question.
Some objects have a usage model such that they do not need to be pinged
at all;
such objects are indicated by the presence of a flag in a
STDOBJREF
to an
interface on the object.
Objects which are not pinged in fact need not be
reference counted either, though it is legal (but pointless) for a client
to
reference count the IPIDs of such objects.
For all other objects, assuming a non-zero ping period, it is the responsibility of the holder of an interface reference on some object to ensure that pings reach the server frequently enough to prevent expiration of the object. The frequency used by a client depends on the ping period, the reliability of the channel between the client and the server, and the probability of failure (no pings getting through and possible premature garbage-collection) that the client is willing to tolerate. The ping packet and / or its reply may both request changes to the ping period. Through this mechanism, network traffic may be reduced in the face of slow links to busy servers.
Without any further refinements, ping messages could be quite hefty. If machine A held 1024 remote object references (OIDs) on machine B, then it would send 16K byte ping messages. This would be annoying if the set of remote objects was relatively stable and the ping messages were the same from ping to ping.
The delta mechanism reduces the size of ping messages. It uses a ping-set interface that allows the pinging of a single set to replace the pinging of multiple OIDs.
Instead of pinging each OID, the client defines a set. Each ping contains only the set ID and the list of additions and subtractions to the set. Objects that come and go within one ping period are removed from the set without ever having been added.
The pinging protocol is carried out using two methods in the (DCE RPC)
interface
IOXIDResolver()
on the
OXID
Resolver:
ComplexPing
, and
SimplePing
.
ComplexPing
is used by clients to group the set of OIDs
that they must ping
into sets known to the server.
These entire sets of OIDs can then be
subsequently pinged with a single, short, call to
SimplePing
.
The
IRemUnknown()
interface on the OXID object, in
addition to
servicing reference counting as described above also services
QueryInterface()
calls for remote clients for IPIDs managed by that object exporter.
IRemUnknown::RemQueryInterface
differs from
IUnknown::QueryInterface()
in much the same way as
RemAddRef
and
RemRelease
differ from
AddRef()
and
Release()
, in
that it is optimized for network access by being able to retrieve many
interfaces at once.
Each ORPC carries with it a UUID known as the causality ID that connects together the chain of ORPC calls that are causally related. If an outgoing ORPC is made while servicing an incoming ORPC, the outgoing call is to have the same causality ID as the incoming call. If an outgoing ORPC is made while not servicing an incoming ORPC, then a new causality ID is allocated for it.
Causality IDs may in theory be reused as soon as it is certain that no transitively outstanding call is still in progress which uses that call. In practice, however, in the face of transitive calls and the possibility of network failures in the middle of such call chains, it is difficult to know for certain when this occurs. Thus, pragmatically, causality IDs are not reusable.
The causality ID can be used by servers to understand when blocking or deferring an incoming call (supported in some COM server programming models) is very highly probable to cause a deadlock, and thus should be avoided.
The causality ID for maybe, idempotent, and broadcast calls must be set to null (e.g., all zeros). If a server makes a ORPC call while processing such a call, a new causality ID must be generated as if it were a top level call.
This following several sections present the technical details of the DCOM protocol.
Object RPC sits entirely on top of DCE RPC. The following list describes the elements of ORPC that are specified above and beyond DCE RPC.
The object ID field of the header must contain the IPID.
The interface ID of the RPC header must contain the IID, even though it is not needed given the IPID. This allows ORPC to sit on top of DCE RPC. An unmodified DCE RPC implementation will correctly dispatch based on IID and IPID. An optimized RPC need only dispatch based on IPID.
An IPID uniquely identifies a particular interface on a particular object on a machine. The converse is not true; a particular interface on a particular object may be represented by multiple IPIDs. IPIDs are unique on their OXID. IPIDs may be reused, however reuse of IPIDs should be avoided.
Datagram, maybe, and idempotent calls are all allowed in ORPC.
Interface pointers may not be passed on maybe or idempotent calls.
Datagram broadcasts are not allowed in ORPC.
Faults are returned in the stub fault field of the DCE RPC
fault packet.
Any
32 bit value may be returned.
Only
RPC_E_VERSION_MISMATCH
is pre-specified:
DCE RPC cancel is supported.
All interface version numbers must be 0.0.
There are several fundamental data types and structures on which the COM network protocol is built. These types are shown here in standard C header format.
//////////////////////////////////////////////////////////// // Basic Definitions //////////////////////////////////////////////////////////// typedef unsiged long HRESULT; // 32-bit integer: success/failure typedef t_uuid UUID; // rename DCE-RPC type typedef UUID GUID; // Globally Unique IDentifier typedef unsigned hyper ID; // 64-bit integer typedef ID OXID; // Object Exporter Identifier typedef ID OID; // Object Identifer typedef ID SETID; // Ping Set Identifier typedef GUID IPID; // Interface Pointer Identifier typedef GUID* REFIPID; typedef GUID CID; // Causality Identifier #define CID_NULL uuid_null; // All zeros ////////////////////////////////////////////////////////////////// // ORPC Call Packet Format ////////////////////////////////////////////////////////////////// const unsigned short COM_MAJOR_VERSION = 5; const unsigned short COM_MINOR_VERSION = 1; typedef struct tagCOMVERSION { unsigned short MajorVersion; // Major version number unsigned short MinorVersion; // Minor version number } COMVERSION; const unsigned long ORPCF_NULL = 0; // no additional info // in packet const unsigned long ORPCF_LOCAL = 1; // call is local to this // machine const unsigned long ORPCF_RESERVED1 = 2; // reserved for local use const unsigned long ORPCF_RESERVED2 = 4; // reserved for local use const unsigned long ORPCF_RESERVED3 = 8; // reserved for local use const unsigned long ORPCF_RESERVED4 = 16; // reserved for local use // Extension to implicit parameters. typedef struct tagORPC_EXTENT { GUID id; // Extension identifier unsigned long size; // Extension size byte data[]; // [size_is((size+7)&~7)] } ORPC_EXTENT; // Array of extensions. typedef struct tagORPC_EXTENT_ARRAY { unsigned long size; // Num extents. unsigned long reserved; // Must be zero. ORPC_EXTENT** extent; // [size_is((size+1)&~1), unique] } ORPC_EXTENT_ARRAY; // implicit 'this' pointer which is the first [in] parameter on // every ORPC call. typedef struct tagORPCTHIS { COMVERSION version; // COM version number unsigned long flags; // ORPCF flags for presence of // other data unsigned long reserved1; // set to zero CID cid; // causality ID of caller ORPC_EXTENT_ARRAY* extensions; // [unique] extensions } ORPCTHIS; // implicit 'that' pointer which is the first [out] parameter on // every ORPC call. typedef struct tagORPCTHAT { unsigned long flags; // ORPCF flags for presence // of other data ORPC_EXTENT_ARRAY *extensions; // [unique] extensions } ORPCTHAT; ////////////////////////////////////////////////////////////////// // Marshaled COM Interface Wire Format ////////////////////////////////////////////////////////////////// typedef enum tagMSHLFLAGS { MSHLFLAGS_NORMAL = 0, MSHLFLAGS_TABLESTRONG = 1, MSHLFLAGS_TABLEWEAK = 2, } MSHLFLAGS; // Tower IDs for common protocols const unsigned short NCADG_IP_UDP = 0x08; const unsigned short NCACN_IP_TCP = 0x07; const unsigned short NCADG_IPX = 0x0E; const unsigned short NCACN_SPX = 0x0C; const unsigned short NCACN_NB_NB = 0x12; const unsigned short NCACN_NB_IPX = 0x0D; const unsigned short NCACN_DNET_NSP = 0x04; const unsigned short NCALRPC = 0x10; // This is the return type for arrays of string bindings or protseqs // used by many ORPC interfaces. typedef struct tagSTRINGBINDING { unsigned short wTowerId; // Cannot be zero. unsigned short aNetworkAddr; // Zero terminated. } STRINGBINDING; // this value (invalid in DCE RPC) indicates to use default authz const unsigned short COM_C_AUTHZ_NONE = 0xffff; typedef struct tagSECURITYBINDING { unsigned short wAuthnSvc; // Must not be zero unsigned short wAuthzSvc; // Must not be zero unsigned short aPrincName; // NULL terminated } SECURITYBINDING; // 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; // # 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 zero. The shortest array contains four zeros. unsigned short aStringArray[]; // [size_is(wNumEntries)] } DUALSTRINGARRAY; // arbitrary value to help ensure validity const unsigned long OBJREF_SIGNATURE = 0x574f454d; const unsigned long OBJREF_STANDARD = 0x1; const unsigned long OBJREF_HANDLER = 0x2; const unsigned long OBJREF_CUSTOM = 0x4; // Flag values for a STDOBJREF (standard part of an OBJREF). // SORF_OXRES1 - SORF_OXRES8 are reserved for the object exporters // use only, object importers must ignore them and must not enforce // MBZ. const unsigned long SORF_NULL = 0x0000; // convenient for init const unsigned long SORF_OXRES1 = 0x0001; // reserved by exporter const unsigned long SORF_OXRES2 = 0x0020; // reserved by exporter const unsigned long SORF_OXRES3 = 0x0040; // reserved by exporter const unsigned long SORF_OXRES4 = 0x0080; // reserved by exporter const unsigned long SORF_OXRES5 = 0x0100; // reserved by exporter const unsigned long SORF_OXRES6 = 0x0200; // reserved by exporter const unsigned long SORF_OXRES7 = 0x0400; // reserved by exporter const unsigned long SORF_OXRES8 = 0x0800; // reserved by exporter const unsigned long SORF_NOPING = 0x1000; // Pinging not required typedef struct tagSTDOBJREF { unsigned long flags; // SORF_ flags (see above) 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; // although this structure is conformant, it is always marshaled // in little-endian byte-order. typedef struct tagOBJREF { unsigned long signature; // must be OBJREF_SIGNATURE unsigned long flags; // OBJREF flags (see above) GUID iid; // interface identifier union { // [switch_is(flags), switch_type(unsigned long)] struct { // [case(OBJREF_STANDARD)] STDOBJREF std; // standard objref DUALSTRINGARRAY saResAddr; // resolver address } u_standard; struct { // [case(OBJREF_HANDLER)] STDOBJREF std; // standard objref CLSID clsid; // Clsid of handler code DUALSTRINGARRAY saResAddr; // resolver address } u_handler; struct { // [case(OBJREF_CUSTOM)] CLSID clsid; // Clsid of unmarshaling code unsigned long cbExtension; // size of extension data unsigned long size; // size of data that follows byte *pData; // extension + // class specific data // [size_is(size), ref] } u_custom; } u_objref; } OBJREF; // wire representation of a marshalled interface pointer, // always the little-endian form of an OBJREF typedef struct tagMInterfacePointer { ULONG ulCntData; // size of data byte abData[]; // [size_is(ulCntData)] data } MInterfacePointer, *PMInterfacePointer; // OXID Resolver information associated with each OXID. typedef struct tagOXID_INFO { DWORD dwTid; // thread ID of object exporter DWORD dwPid; // process ID of obj exporter IPID ipidRemUnknown; // IRemUnknown IPID for object exporter DWORD dwAuthnHint; DUALSTRINGARRAY *psa; // protocol and security info } OXID_INFO; //////////////////////////////////////////////////////////////////
An
OBJREF
is the data type used to represent an actual
marshaled
object reference.
An
OBJREF
can either be empty or assume
one of three
variations, depending on the degree to which the object being marshaled uses
the hook architecture (IMarshal()
, etc.) in the marshaling
infrastructure.
The
OBJREF
structure is a union consisting of a switch flag
followed by the
appropriate data.
Contains one interface of an object marshaled in standard form.
Contains
a standard reference, along with a set of protocol sequences and network
addresses that can be used to bind to an OXID resolver that is able to resolve
the OXID in the
STDOBJREF
.
This is useful when marshaling
a proxy to give to
another machine (a.k.a.
the ``middleman'' case).
The marshaling machine specifies
the
saResAddr
for the OXID Resolver on the server machine,
eliminating the need
for the unmarshaler to call the marshaler (middleman) back to get this
information.
Further, the marshaler does not need to keep the OXID in its
cache
beyond the lifetime of its own references in order to satisfy requests from
parties that it just gave the
OBJREF
to.
Member |
Type |
Semantic |
Signature | unsigned long | must be
OBJREF_SIGNATURE |
flags | unsigned long | OBJREF
flags (Section
Section 22.3.3) |
GUID | iid | Interface identifier |
std | STDOBJREF |
A standard object reference used to connect to the source object (Section Section 22.3.4). |
SaResAddr | STRINGARRAY |
The resolver address. |
A marshaling of an object that wishes to use handler marshaling.
For
example, an object wishes to be represented in client address spaces by a
proxy
object that caches state.
In this case, the class-specific information is
just
a standard reference to an interface pointer that the handler (proxy object)
will use to communicate with the original object.
See the
IStdMarshalInfo()
interface.
Member |
Type |
Semantic |
signature | unsigned long | must be
OBJREF_SIGNATURE |
flags | unsigned long | OBJREF
flags (Section
Section 22.3.3) |
GUID | iid | Interface identifier |
std | STDOBJREF |
A standard object reference used to connect to the source object (Section Section 22.3.4). |
clsid | CLSID | The CLSID of handler to create in the destination client. |
SaResAddr | STRINGARRAY |
The resolver address. |
A marshaling of an object which supports custom marshaling.
The Custom
format gives an object control over the representation of references to itself.
For example, an immutable object might be passed by value, in which case the
class-specific information would contain the object's immutable data.
See
the
IMarshal()
interface.
Member
|
Type
|
Semantic
|
signature | unsigned long | must be
OBJREF_SIGNATURE |
flags | unsigned long | OBJREF
flags (Section
Section 22.3.3) |
GUID | iid | Interface identifier |
clsid | CLSID | The CLSID of the object to create in the destination client. |
cbExtension | unsigned long | The size of the extension data. |
Size | unsigned long | The size of the marshaled data provided by the source object, plus the size of the extension data, and passed in pData. |
PData | byte* | The data bytes that should be passed to
IMarshal::UnmarshalInterface()
on a new instance of class clsid in
order to initialize it and complete the unmarshal process (class specific
data).
The first cbExtension bytes are the reserved for future extensions
to the protocol, and should not be passed into the custom unmarshaler.
CoUnmarshalInterface()
should skip the extension data, and the data
starting at pData+cbExtension should be given to the custom unmarshaler.
|
An instance of a
STDOBJREF
represents a COM interface
pointer
that has been marshaled using the standard COM network protocol.
The members and semantics of the
STDOBJREF
structure
are as follows:
Member
|
Semantic
|
flags | Flag values taken from the enumeration
SORFFLAGS . |
cPublicRefs | The number of reference counts on ipid that are being transferred in this marshaling. |
oxid | The OXID of the server that owns this OID. |
oid | The OID of the object to which ipid corresponds. |
ipid | The IPID of the interface being marshaled. |
The various
SORFLAGS
values have the following meanings.
The
SORF_OXRESxxx
bit flags are reserved for the object exporter's
use only, and must be ignored by object importers.
They need not be passed
through when
marshaling an interface proxy.
Flag
|
Value
|
Meaning
|
SORF_NULL |
0 | Convenient for initialization. |
SORF_OXRES1 |
1 | Reserved for exporter. |
SORF_OXRES2 |
32 | Reserved for exporter. |
SORF_OXRES3 |
64 | Reserved for exporter. |
SORF_OXRES4 |
128 | Reserved for exporter. |
SORF_OXRES5 |
256 | Reserved for exporter. |
SORF_OXRES6 |
512 | Reserved for exporter. |
SORF_OXRES7 |
1024 | Reserved for exporter. |
SORF_OXRES8 |
2048 | Reserved for exporter. |
SORF_NOPING |
4096 | This OID does not require pinging. Further, all interfaces on this OID, including this IPID, need not be reference counted. Pinging and reference counting on this object and its interfaces are still permitted, however, though such action is pointless. |
The various
ORPCINFOFLAGS
have the following meanings.
Flag |
Meaning |
ORPCF_NULL |
(Not a real flag. Merely a defined constant indicating the absence of any flag values.) |
ORPCF_LOCAL |
The destination of this call is on the same machine on which it originates. This value is never to be specified in calls which are not in fact local. |
ORPCF_RESERVED1 |
If
ORPCF_LOCAL
is set,
then reserved for local use; otherwise, reserved for future use.
|
ORPCF_RESERVED2 |
If
ORPCF_LOCAL
is set,
then reserved for local use; otherwise, reserved for future use.
|
ORPCF_RESERVED3 |
If
ORPCF_LOCAL
is set,
then reserved for local use; otherwise, reserved for future use.
|
ORPCF_RESERVED4 |
If
ORPCF_LOCAL
is set,
then reserved for local use; otherwise, reserved for future use.
|
Implementations may use the local and reserved flags to indicate any
extra
information needed for local calls.
Note that if the
ORPCF_LOCAL
bit is not set
and any of the other bits are set then the receiver should return a fault.
In every Request PDU that is an ORPC, the body (CL case) or the
stub data (CO>
case) which normally contains the marshaled arguments in fact
begins with an instance of the
ORPCTHIS
structure.
The
marshaled arguments of
the COM interface invocation follow the
ORPCTHIS
; thus,
viewed at the DCE RPC
perspective, the call has an additional first argument.
The
ORPCTHIS
is padded
with zero-bytes if necessary to achieve an overall size that is a multiple
of
eight bytes; thus, the remaining arguments are as a whole eight byte aligned.
As in regular calls, the causality ID must be propagated.
If A calls
ComputePi
on B, B calls
Release()
on C (which gets converted to
RemRelease
), and C calls
Add on A, A will see the same causality ID that it called B with.
Member
|
Type
|
Semantic
|
version | COMVERSION |
The version number of the COM protocol used to make this
particular ORPC.
The initial value will be 5.1.
Each packet contains the sender's
major and minor ORPC version numbers.
The client's and server's major versions
must be equal.
Backward compatible changes in the protocol are indicated by
higher minor version numbers.
Therefore, a server's minor version must be
greater than or equal to the client's.
However, if the server's minor version
exceeds the client's minor version, it must return the client's minor version
and restrict its use of the protocol to the minor version specified by the
client.
A protocol version mismatch causes the
RPC_E_VERSION_MISMATCH
ORPC fault to be returned.
|
flags | unsigned long | Flag values taken from the enumeration
ORPCINFOFLAGS
(Section
Section 22.3.6). |
Reserved | unsigned long | Must be set to zero. |
reserved1 | unsigned long | Set to zero. |
cid | CID |
The causality ID of this ORPC. |
extensions | ORPC_EXTENT_ARRAY* |
The body extensions, if any, passed with this call. Body extensions are GUID-tagged blobs of data which are marshaled as an array of bytes. Extensions are always marshaled with initial eight byte alignment. Body extensions which are presently defined are described in Section Section 22.3.10. |
The cid field contains the causality ID. Each time a client makes a unique call, a new causality ID is generated. If a server makes a call while processing a request from a client, the new call must have the same causality ID as the call currently being processed. This allows simple servers to avoid working on more then one thing at a time (for example A calls B calls A again, meanwhile C tries to call A with a new causality ID). It tells the server that he is being called because he asked someone to do something for him. There are several interesting exceptions.
The causality ID for maybe and idempotent calls must be set to
CID_NULL
.
If a
server makes a ORPC call while processing such a call, a new causality ID
must
be generated.
In the face of network failures, the same causality ID may end up in use by two independent processes at the same time. If A calls B calls C calls D and C fails, both B and D can independently, simultaneously make calls to E with the same causality ID.
The extensions field contains extensions to the channel header, described
in
Section
Section 22.3.10.
Note that in order to force the
ORPCTHIS
header to be 8
byte
aligned an even number of extensions must be present and the size of the
extension data must be a multiple of 8.
In every Response PDU that is an ORPC, the body (CL case) or the
stub data (CO case) which normally contains the marshaled output parameters
in
fact begins with an instance of the
ORPCTHAT
structure.
The marshaled output
parameters of the COM interface invocation follow the
ORPCTHAT
;
thus, viewed at
the DCE RPC perspective, the call has an additional output parameters.
The
ORPCTHAT
is padded with zero-bytes if necessary to achieve
an overall size that
is a multiple of eight bytes; thus, the remaining output parameters as a whole
are eight byte aligned.
Member
|
Type
|
Semantic
|
flags | unsigned long | Flag values taken from the enumeration
ORPCINFOFLAGS
(Section 22.3.6). |
extensions | ORPC_EXTENT_ARRAY* |
The body extensions, if any, returned by this call. See Section 22.3.10 for a general description of body extensions as well as a description of existing well-known extensions. |
HRESULT
s are the 32-bit return value from all ORPC
methods.
The
following is a partial list of already defined
HRESULT
s.
S_OK |
Success. (0x00000000) |
E_OUTOFMEMORY |
Insufficient memory to complete the call. (0x80000002) |
E_INVALIDARG |
One or more arguments are invalid. (0x80000003) |
E_NOINTERFACE |
No such interface supported (0x80000004) |
E_ACCESSDENIED |
A secured operation has failed due to (0x80070005) inadequate security privileges. |
E_UNEXPECTED |
Unknown, but relatively catastrophic (0x8000FFFF) error. |
RPC_E_INVALID_OXID |
The object exporter was not found. (0x80070776) |
RPC_E_INVALID_OID |
The object specified was not found or (0x80070777) recognized. |
RPC_E_INVALID_SET |
The object exporter set specified was (0x80070778) not found. |
RPC_E_INVALID_OBJECT |
The requested object does not exist |
The high-order bit in the
HRESULT
indicates whether
the return
value represents success or failure.
If set to zero,
SEVERITY_SUCCESS
,
the value indicates success.
If set to 1,
SEVERITY_ERROR
,
it indicates
failure.
The facility field indicates the system service responsible for the
error.
There are
currently five facilities:
FACILITY_NULL
,
FACILITY_ITF
,
FACILITY_DISPATCH
,
FACILITY_RPC
, and
FACILITY_STORAGE
.
Microsoft allocates new facility codes
as they
become necessary.
Most
HRESULT
s set the facility field
to
FACILITY_ITF
, indicating an interface method error.
The following table describes the various facility fields:
Facility
|
Description
|
FACILITY_NULL |
For broadly applicable common status codes
such as
S_OK .
This facility code has a value of zero. |
FACILITY_ITF |
For most status codes returned from interface
methods, value is
defined by the interface.
That is, two
HRESULT s
with exactly the same 32-bit value returned from two different
interfaces might have different meanings.
This facility has a
value of 4. |
FACILITY_DISPATCH |
For late-binding
IDispatch
interface
errors.
This facility has a value of 2. |
FACILITY_RPC |
For status codes returned from remote procedure calls. This facility has a value of 1. |
FACILITY_STORAGE |
For status codes returned from
IStorage
or
IStream
method calls relating to structured storage.
Status codes
whose code (lower 16 bits) value is in the range of DOS error codes (that
is,
less than 256) have the same meaning as the corresponding
DOS error.
This facility has a value of 3. |
FACILITY_WINDOWS |
Used for additional error codes from Microsoft-defined interfaces. |
FACILITY_WIN32 |
Used to provide a means of handling error
codes from functions
in the Win32 API as an
HRESULT .
Error codes in 16-bit OLE
that duplicated Win32 error codes have also been changed to
FACILITY_WIN32 . |
The code field is a unique number that is assigned to represent the error or warning.
By convention,
HRESULT
s generally have names in the
following format:
Facility_Severity_Reason
where
Facility
is either the facility name or some
other distinguishing
identifier;
Severity
is a single letter, S or E, that
indicates whether
the function call succeeded (S) or failed (E); and
Reason
is an identifier
that describes the meaning of the code.
For example, the status code
STG_E_FILENOTFOUND
indicates a storage-related error has
occurred;
specifically, a requested file does not exist.
Status codes from
FACILITY_NULL
omit the
Facility_
prefix.
Error codes are defined within the context of an interface implementation.
Once defined,
success codes cannot be changed or new success codes added.
However, new failure
codes can
be written since they generally only provide hints at what might have gone
wrong.
Microsoft
reserves the right to define new failure codes (but not success codes) for
the interfaces
described in
FACILITY_ITF
or in new facilities.
Body Extensions are UUID-tagged blocks of data which are useful
for conveying additional, typically out-of-band, information on incoming
invocations (within
ORPCTHIS
, Section
Section 22.3.7)
and in replies (within
ORPCTHAT
, Section
Section 22.3.8).
Any implementations of the DCOM protocol may define its own extensions with their own UUIDs. Implementations should skip over extensions which they do not recognize or do not wish to support.
Body Extensions are marshaled as an array of bytes with initial eight byte alignment. The following sections describe several existing body extensions.
{f1f19680-4d2a-11ce-a66a-0020af6e72f4}
This extension aids in debugging ORPC. In particular it is designed to allow single stepping over an ORPC call into the server and out of the server into the client.
{f1f19681-4d2a-11ce-a66a-0020af6e72f4}
The extended error information body extension conveys extended error information concerning the original root cause of a error back to a caller so that the caller can deal with it. This extension is only semantically useful in Response and Fault PDUs.
It is intended that this error information is suitable for displaying information to a human being who is the user; this information is not intended to be the basis for logic decisions in a piece of client code, for doing so couples the client code to the implementation of the server. Rather, client code should act semantically only on the information returned through the interface that it invokes.
The
IRemUnknown()
interface is used by remote clients
for manipulating
reference counts on the IPIDs that they hold and for obtaining additional
interfaces on the objects on which those IPIDs are found.
References are kept per interface rather then per object.
This interface is implemented by the COM ``OXID object'' associated
with each
OXID ( i.e.
each Object Exporter).
The IPID for the
IRemUnknown()
interface on this
object is returned from
IOXIDResolver::ResolveOxid
; see
Section 22.5.2.1.
An
OXID
object need never be pinged; its interfaces (this
IPID
included) need never be reference counted.
IRemUnknown()
is
specified as follows:
// The remote version of IUnknown. This interface exists on every // OXID (whether an OXID represents either a thread or a process is // implementation specific). It is used by clients to query for new // interfaces, get additional references (for marshaling), and release // outstanding references. // This interface is passed along during OXID resolution. // [ object, uuid(00000131-0000-0000-C000-000000000046) ] interface IRemUnknown : IUnknown { typedef struct tagREMQIRESULT { HRESULT hResult; // result of call STDOBJREF std; // data for returned interface } REMQIRESULT; HRESULT RemQueryInterface ( [in] REFIPID ripid, // interface to QI 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 QI for [out, size_is(,cIids)] REMQIRESULT** ppQIResults // results returned ); typedef struct tagREMINTERFACEREF { IPID ipid; // ipid to AddRef/Release unsigned long cPublicRefs; unsigned long cPrivateRefs; } REMINTERFACEREF; 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[] ); }
QueryInterface()
for and return the result thereof
for zero or more
interfaces from the interface behind the IPID ipid.
ipid must designate an
interface derived from
IUnknown()
(recall that all remoted
interfaces must derive
from
IUnknown()
).
The
QueryInterface()
calls on the object
that are used to service this request are conducted on the
IUnknown()
interface of the object.
Argument |
Type |
Semantic |
ripid | REFIPID |
The interface on an object from whom more interfaces are desired. |
CRefs | unsigned long |
The number of references sought on each of the returned IIDs. |
CIids | unsigned short |
The number of interfaces being requested. |
iids | IID* |
The list of IIDs that name the interfaces sought on this object. |
ppQIResults | REMQIRESULT** |
The place at which the array of the results
of the various
QueryInterface()
calls are returned.
|
Return Value |
Meaning |
S_OK |
Success.
An attempt was made to retrieve
each of the requested interfaces from the indicated object; that is,
QueryInterface()
was actually invoked for each IID.
QueryInterface()
returned
S_OK
for every IID specified.
|
S_FALSE |
Success.
An attempt was made to retrieve
each of the requested interfaces from the indicated object; that is,
QueryInterface()
was actually invoked for each IID.
QueryInterface()
returned a failure code for at least one of the IIDs specified.
|
E_NOINTERFACE |
QueryInterface()
returned
a failure code for every IID specifed.
|
E_INVALIDARG |
One or more arguments (likely ipid) were invalid. No result values are returned. |
E_UNEXPECTED |
An unspecified error occurred. |
E_OUTOFMEMORY |
Insufficient memory to complete the call. |
RPC_E_INVALID_OBJECT |
The requested object does not exist. No result values are returned. |
The
REMQIRESULT
structure contains the following
members:
Member |
Type |
Semantic |
hResult | HRESULT |
The result code from the
QueryInterface()
call made for the requested IID. |
std | STDOBJREF |
The data for the returned interface.
Note
that if hResult indicates failure then the contents of
STDOBJREF
are undefined. |
Obtain and grant ownership to the caller of one or more reference counts on one or more IPIDs managed by the corresponding OXID.
Argument |
Type |
Semantic |
cInterfaceRefs | unsigned short |
The size of the rgRefs array. |
InterfaceRefs | REMINTERFACEREF[] |
An array of
REMINTERFACEREF s,
cRefs
large.
Each IPID indicates an interface managed by this OXID
on whom more reference counts are sought.
The corresponding reference count
(cInterfaceRefs), which may not be zero (and thus is
one or more), indicates the number of reference counts sought on that IPID.
|
PResults | HRESULT* |
An array of HRESULTs cInterfaceRefs large,
each containing the result of attempting an
AddRef()
on
the ipid in the corresponding REMINTERFACREF.
|
Return Value |
Meaning |
S_OK |
Success. An attempt was made to retrieve each of the requested interface references. |
E_INVALIDARG |
One or more of the IPIDs indicated were not in fact managed by this OXID, or one or more of the requested reference counts was zero. None of the requested reference counts have been granted to the caller; the call is a no-op. |
E_UNEXPECTED |
An unspecified error occurred. It is unknown whether any or all of the requested reference counts have been granted. |
CO_E_OBJNOTREG |
Object is not registered. |
A useful optimization is for a caller to
RemAddRef
more than needed.
When a process receives an out marshaled interface, it receives one reference count. If the process wishes to pass that interface as an out parameter, it must get another reference to pass along. Instead, the process (or middleman) should get a large number of references. Then if the interface is passed out multiple times, no new remote calls are needed to gain additional references.
A marshaler may optionally specify more than one reference in the
STDOBJREF
when marshaling an interface.
This allows the middle man case to pre-fill
its
cache of references without making an extra
RemAddRef
call.
The number of
references passed is always specified in the
STDOBJREF
field.
If
cPrivateRefs
is not zero for all IPIDs, the call
to
RemAddRef
must be made securely.
DCOM on the server remembers
the name of the client and the authentication and authorization service used
to
make to
RemAddRef
call.
Member |
Type |
Semantic |
IPID |
ipid | ipid to
AddRef /Release .
|
unsigned long |
cPublicRefs | Number of public references granted . |
unsigned long |
cPrivateRefs | Number of private references granted.
Private
references belong only to this client and can not be passed to other clients
when marshaling the proxy.
If a client has only private references and wishes
to pass the proxy to some other client, it must first obtain some public references
via
IRemUnknown::RemAddRef()
and then pass one or more
of those references in the
STDOBJREF
cPublicRefs field
of the marshaled interface.
|
Release()
ownership of one or more reference counts
on one or more
IPIDs managed by the corresponding OXID.
If
cPrivateRefs
is not zero for all IPIDs, the call
to
RemRelease
must be made
securely.
For each IPID, DCOM maintains a table of reference counts indexed
by
the client identity (name, authn svc, authz svc).
All public references are
stored in one entry.
Any call to
RemRelease
can release
public references.
Private references can only be released by the client that added them.
Argument |
Type |
Semantic |
cRefs | unsigned short |
The size of the rgRefs array. |
rgRefs | REMINTERFACEREF[] |
An array of
REMINTERFACEREF s,
cRefs large.
Each IPID indicates an interface managed by this OXID on whom
more reference counts are being returned.
The corresponding reference count,
which may not be zero (and thus is one or more), indicates the number of reference
counts returned on that IPID.
|
Return Value |
Meaning |
S_OK |
Success. An attempt was made to release each of the requested interface references. |
E_INVALIDARG |
One or more of the IPIDs indicated were not in fact managed by this OXID, or one or more of the requested reference counts was zero. None of the offered reference counts have been accepted by the server; the call is a no-op. |
E_UNEXPECTED |
An unspecified error occurred. It is unknown whether any or all of the offered reference counts have been accepted. |
Each machine that supports the COM network protocol supports a one- per-machine service known as the machine's ``OXID Resolver.'' Communication with an OXID Resolver is via a DCE RPC, not an ORPC.
The OXID Resolver performs several services:
It caches and returns to clients when asked the string bindings necessary to connect to OXIDs of exported objects for which this machine is either itself a client or is the server. Note that it typically returns only to client processes on the same machine as itself, the OXIDs for which it is a client.
It receives pings from remote client machines to keep its own objects alive.
May do lazy protocol registration in the servers which it scopes.
These services are carried out through an RPC interface (not a COM interface)
known as
IOXIDResolver()
.
An
OXID
Resolver
may be asked for the information
required to connect to one of two different kinds of OXIDs, either the OXIDs
associated with its own objects, or the OXIDs associated with objects for
which
it is itself a client The second case occurs when two or more client
processes on the same machine ask their local OXID Resolver to resolve a given
OXID.
The client OXID Resolver in this case can cache the OXID information
an
return it to local clients without having to contact the server's OXID Resolver
again.
The OXID Resolver resides at different endpoints (ports) depending on the transport being used. The OXID Resolver optimally resides at the same endpoints as the DCE RPC Endpoint Mapper (EPM>). To accommodate systems where DCOM will coexist with existing DCE RPC installations (i.e., where an EPM and presumably a complete DCE RPC runtime already exists), the DCOM implementation on that system will register its interfaces with the DCE EPM and all DCOM implementations must be able to fall back if they make DCOM-specific calls on the DCE EPM endpoint which fail.
Protocol String
Name(s) |
Description |
Endpoint |
ncadg_ip_udp, ip |
CL over UDP/IP | 135 |
ncacn_ip_tcp |
CO over TCP/IP | 135 |
ncadg_ipx |
CL over IPX | TBD |
ncacn_spx |
CO over SPX | TBD |
ncacn_nb_nb |
CO over NetBIOS over NetBEUI | TBD |
ncacn_nb_tcp |
CO over NetBIOS over TCP/IP | 135 |
ncacn_np |
CO over Named Pipes | TBD |
ncacn_dnet_nsp |
CO over DECNet Network Services 96 Protocol (DECNet Phase IV) | |
ncacn_osi_dna |
CO over Open Systems 69 Interconnection (DECNet Phase V) | |
ncadg_dds, dds |
CL over Domain Datagram Service | 12 |
ncahttp |
Hybrid over HTTP | 80 |
IOXIDResolver()
(in earlier DCOM documentation this
interface was
named
IObjectExporter
) is defined as follows:
[ 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 // 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 zero ); // 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, // In 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 = multipler ); // In some cases the client maybe unsure that a particular // binding will reach the server. (For example, when the oxid // bindings have more then 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 ); }
Return the string bindings necessary to connect to a given OXID object.
On entry,
arRequestedProtseqs
contains the protocol
sequences the client is
willing to use to reach the server.
These should be in decreasing order of
protocol preference, with no duplicates permitted.
Local protocols (such as
``ncalrpc'') are not permitted.
On exit,
psaOxidBindings
contains the string bindings
that may be used to
connect to the indicated OXID; if no such protocol bindings exist which match
the requested protocol sequences,
NULL
may be returned.
The returned string
bindings are in decreasing order of preference of the server, with duplicate
string bindings permitted (and not necessarily of the same preferential
priority), though of course duplicates are of no utility.
Local protocol
sequences may not be present; however, protocol sequences that were not in
the
set of protocol sequences requested by the client may be.
The string bindings
returned need not contain endpoints; the endpoint mapper will be used as usual
to obtain these dynamically.
Argument |
Type |
Description |
hRpc | handle_t |
An RPC binding handle used to make the request. |
pOxid | OXID* |
The OXID for whom string bindings are requested. |
cRequestedProtseqs | unsigned short |
The number of protocol sequences requested. |
arRequestedProtseqs | unsigned short[] |
arRequestedProtseqs must be initialized with all the protocol ids the client is willing to use to reach the server. It cannot contain local protocol sequences. The object exporter must take care of local lookups privately. The protocol sequences are in order of preference or random order. No duplicates are allowed. See Section 22.5.2.1.2 for more details. |
psaOxidBindings | STRINGARRAY** |
The string bindings supported by this OXID, in preferential order. Note that these are Unicode strings. |
pipidRemUnknown | IPID* |
The IPID to the
IRemUnknown()
interface the OXID object for this OXID.
|
pdwAuthnHint | unsigned long* |
A value taken from the
RPC_C_AUTHN
constants.
A hint to the caller as to the minimum authentication
level which the server will accept.
|
Return Value |
Meaning |
S_OK |
Success. The requested information was returned. |
RPC_E_INVALID_OXID |
This OXID is unknown to this OXID Resolver, and thus no information was returned. |
E_UNEXPECTED |
An unspecified error occurred. Some of the requested information may not be returned. |
Object references are transient things. They are not meant to be stored in files or otherwise kept persistently.
Conversely, since object references are aged, it is the responsibility of each client to unmarshal them and begin pinging them in a timely fashion.
The basic use of the
ResolveOxid
method is to translate
an OXID to string
bindings.
Put another way, this method translates an opaque process and machine
identifier to the information needed to reach that machine and process.
There
are four interesting cases:
Looking up an OXID the first time an interface is unmarshaled on a machine,
Looking up an OXID between a pair of machines that already have connections,
Looking up an OXID from a middleman, and
Looking up string bindings with unresolved endpoints (lazy use protseq).
Another interesting topic is garbage collection of stored string binding vectors.
Consider the case with two machines A and B. Machine A has OXID Resolver process C and client process D. Machine B has OXID Resolver process E and server process F.
Server process F, when it starts up, registers its RPC string bindings
with
its local OXID Resolver process E, and creates an
OBJREF
to some object inside process F.
At some future time client process D receives
that
OBJREF
(Note: the mechanism used to acquire this
OBJREF
is not relevant to this discussion, it may have
come
through the object activation protocol (beyond the scope of this document)
or
as an [out] interface parameter in some other ORPC call, or through some other
mechanism).
The
OBJREF
contains the OXID for process F,
and
the string bindings for the Resolver process E on the server machine.
Client Process D asks its local OXID Resolver to resolve the OXID for F. It passes it the OXID and the string bindings for OXID Resolver E. If Resolver D has never seen the OXID before, it calls the OXID Resolver E to ask it to resolve the OXID. Resolver E looks up the OXID and returns the string bindings for server process F. Resolver D then caches this information for future use, and returns the string bindings to client process C. Client process C then binds to the string bindings and is now able to make ORPC calls directly to the server process F.
If other client processes on machine A receive an
OBJREF
for
process F, the OXID resolve can be handled completely by the Resolver process
D
on machine A.
There is no need to contact Resolver process E on the server
again.
If machine A gives an
OBJREF
for F to a client process
on
another machine G, then the Resolver process on G will repeat the same steps
as
Resolver process D did to resolve the OXID.
+============+============+ +===========+===========+ | Machine A | | Machine B | +============+============+ +===========+===========+ +============+============+ +===========+===========+ | Process C | Resolver D | | Resolver E| Process F | +============+============+ +===========+===========+ | | | | | register | | | | | | endpoints | | | | | | with local| | | | | | Resolver E| +------------+------------+ +-----------+-----------+ | | | | cache F | | | | | | and its | | | | | | endpoints | | | | | | | | +------------+------------+ +-----------+-----------+ | receive | | | | | | OBJREF | | | | | | to F | | | | | | | | | | | +------------+------------+ +-----------+-----------+ | ask local | | | | | | Resolver C | | | | | | to resolve | | | | | | F | | | | | +------------+------------+ +-----------+-----------+ | | ask remote | | | | | | Resolver | | | | | | E to | | | | | | resolve F | | | | +------------+------------+ +-----------+-----------+ | | | | lookup F | | | | | | and | | | | | | return | | | | | | endpoints | | +------------+------------+ +-----------+-----------+ | | cache | | | | | | endpoints | | | | | | to F and | | | | | | return to D| | | | +------------+------------+ +-----------+-----------+ | bind to | | | | | | endpoint | | | | | | for F | | | | | | | | | | | +------------+------------+ +-----------+-----------+ | invoke | | | | | | method on | | | | | | F | | | | | | | | | | | +------------+------------+ +-----------+-----------+
In a homogeneous network, all machines communicate via the same protocol
sequence.
In a heterogeneous network, machines may support multiple protocol
sequences.
Since it is often expensive in resources to allocate endpoints
(RpcServerUseProtseq
) for all available protocol sequences,
ORPC provides a mechanism where they may be allocated on demand.
To implement
this extension fully, there are some changes in the server.
However, changes
are optional.
If not implemented, ORPC will still work correctly if less
optimally in heterogeneous networks.
There are two cases: either the server implements lazy protocol registration or it does not.
If the server is using lazy protocol registration, the implementation
of
ResolveOxid
is modified slightly.
When the client OXID
Resolver
calls the server OXID Resolver, it passes the requested protocol sequence
vector.
If none of the requested protocol sequences have endpoints allocated in the
server,
the server OXID Resolver allocates them according to its own endpoint allocation
mechanisms.
If the server does not implement the lazy protocol registration, then all protocol sequences are registered by the server at server initialization time.
When registering protocol sequences, the server may register endpoints and the server's string bindings will contain the complete endpoints. However, if the server chooses not register endpoints when it registers protocol sequences the endpoint mapper process can be used to forward calls to the server. Using the endpoint mapper requires that all server IIDs be registered in the endpoint mapper. It also allows a different lazy protocol registration mechanism. The endpoint mapper can perform some local magic to force the server to register the protocol sequences.
The client will always pass in a vector of requested protocol sequences which the server can ignore if it does not implement lazy protocol registration.
Pings provide a mechanism to garbage collect interfaces.
If an interface
has references but is not being pinged, it may be released.
Conversely, if
an
interface has no references, it may be released even though it has recently
been pinged.
SimplePing
just pings the contents of a set.
The set must be
created with
ComplexPing
(Section
Section 22.5.2.3).
Ping a set, previously created with
IOXIDResolver::ComplexPing
, of OIDs owned
by this OXID Resolver.
Note that neither IPIDs nor OIDs may be pinged, only
explicitly created SETIDs.
Argument |
Type |
Description |
hRpc | handle_t |
An RPC binding handle used to make the request. |
PSetId | SETID* |
A SETID previously created with
IOXIDResolver::ComplexPing()
on this same OXID Resolver.
|
Return Value |
Meaning |
S_OK |
Success. The set was pinged. |
RPC_E_INVALID_SET |
This SETID is unknown to this OXID Resolver, and thus the ping did not occur. |
E_UNEXPECTED |
An unspecified error occurred. It is not known whether the ping was done or not. |
Ping a ping set.
Optionally, add and/or remove some OIDs from the set.
Optionally,
adjust some ping timing parameters associated with the set.
After a set is
defined,
a
SimplePing
will mark the entire contents of the set as
active.
After a set is defined,
SimplePing
should be used to ping
the set.
ComplexPing
need only be used to adjust the contents of
the set
(or to adjust the time-out).
Ping set IDs (SETIDs) are allocated unilaterally by the server OXID Resolver. The client OXID Resolver then communicates with the server OXID Resolver to add (and later remove) OIDs from the ping set..
Each OID owned by a server OXID Resolver may be placed in zero or more ping sets by the various clients of the OID. The client owner of each such set will set a ping period and a ping time-out count for the set, thus determining an overall time-out period for the set as the product of these two values. The time-out period is implicitly applied to each OID contained in the set and to future OIDs that might add be added to it. The server OXID Resolver is responsible for ensuring that an OID that it owns does not expire until at least a period of time t has elapsed without that OID being pinged, where t is the maximum time-out period over all the sets which presently contain the given OID, or, if OID is not presently in any such sets but was previously, t is the time-out period for the last set from which OID was removed at the instant that that removal was done; otherwise, OID has never been in a set, and t is a default value (ping period equals 120 seconds, ping time-out count equals three (3), t equals 360 seconds, or six (6) minutes).
Clients are responsible for pinging servers often enough to ensure that
they do
not expire given the possibility of network delays, lost packets, and so on.
If
a client only requires access to a given object for what it would consider
less
than a time-out period for the object (that is, it receives and release the
object in that period of time), then unless it is certain it has not itself
passed the object to another client, it must be sure to nevertheless ping
the
object (a
ComplexPing
that both adds and removes the OID
will
suffice).
This ensures that an object will not expire as it is passed through
a
chain of calls from one client to another.
An OID is said to be pinged when a set into which it was previously
added and
presently still resides is pinged with either a
SimplePing
or
a
ComplexPing
, or when it is newly added to a set with
ComplexPing
.
Note that these rules imply that a
ComplexPing
that removes an OID from a set still counts
as a
ping on that OID.
In addition to pinging the set
SETID
,
this
call sets the time-out period of the set as the product of a newly-specified
ping
period and a newly-specified ``ping count to expiration;'' these values take
effect
immediately.
Ping periods are specified in tenths of a second, yielding a
maximum
allowable ping period of about 1 hr 50 min.
Adjustment of the time-out period of the set is considered to happen
before the
addition of any new OIDs to the set, which is in turn considered to happen
before
the removal of any OIDs from the set.
Thus, an
OID
that
is added
and removed in a single call no longer resides in the set, but is considered
to
have been pinged, and will have as its time-out at least the time-out period
specified in that
ComplexPing
call.
On exit, the server may request that the client adjust the time-out
period;
that is, ask it to specify a different time-out period in subsequent calls
to
ComplexPing
.
This capability can be used to reduce traffic
in
busy servers or over slow links.
The server indicates its desire through the
values
it returns through the variables
pReqSetPingPeriod
and
pReqSetNumPingsToTimeOut
.
If the server seeks no change,
it
simply returns the corresponding values passed by the client; if it wishes
a longer
time-out period, it indicates larger values for one or both of these variables;
if
it wishes a smaller period, it indicates smaller values.
When indicating a
larger
value, the server must start immediately acting on that larger value by adjusting
the time-out period of the set.
However, when indicating a smaller value,
it must
consider its request as purely advice to the client, and not take any action:
if the
client wishes to oblige, it will do so in a subsequent call to
ComplexPing
by specifying an appropriate time-out period.
Argument |
Type |
Description |
hRpc | handle_t |
An RPC binding handle used to make the request. |
pSetId | SETID |
The SETID being manipulated. |
SequenceNum | unsigned short | The sequence number allows the object exporter to detect duplicate packets. Since the call is idempotent, it is possible for duplicates to get executed and for calls to arrive out of order when one ping is delayed. |
cAddToSet | unsigned short |
The size of the array AddToSet. |
cDelFromSet | unsigned short |
The size of the array DelFromSet. |
AddToSet | OID[] |
The list of OIDs which are to be added to this set. Adding an OID to a set in which it already exists is permitted; such an action, as would be expected, is considered to ping the OID. |
DelFromSet | OID[] |
The list of OIDs which are to be removed from this set. Removal counts as a ping. An OID removed from a set will expire after the number of ping periods has expired without any pings (not the number of ping periods - 1). If an id is added and removed from a set in the same ComplexPing, the id is considered to have been deleted. |
PPingBackoffFactor | unsigned short* |
Acts as a hint (only) from the server to the client in order to reduce ping traffic. Clients are requested to not ping more often than (1<<*pPingBackoffFactor)* (BasePingInterval=120) seconds, and the number of pings until timeout remains unchanged at the default of 3. Clients may choose to assume that this parameter is always zero. |
Return Value |
Meaning |
S_OK |
Success. The set was pinged, etc. |
RPC_E_INVALID_OID |
Indicates that some OID was not recognized. There is no recovery action for this error, it is informational only. |
E_ACCESSDENIED |
Access is denied. |
E_OUTOFMEMORY |
There was not enough memory to service the call. The caller may retry adding OIDs to the set on the next ping. |
E_UNEXPECTED |
An unspecified error occurred. It is not known whether the ping or any of the other actions were done or not. |
This is an example of a server side wrapper for the Bar method. It assumes the existence of small helper functions to import and export object references and lookup previously exported object references.
RPC_STATUS Bar(handle_t h, short i, OBJREF * prIB, OBJREF ** pprIW) { UUID ipid; RPC_STATUS status; IFoo * pIF; IBar * pIB; IWaz * PIW; HRESULT hR; status = RpcBindingInqObject(h, &ipid); if (status) return(SOMETHING); status = ORpcLookupIPID(ipid, &pIF); if (status) return(SOMETHING); status = ORpcImportObjRef(prIB, &pIB); if (status) return(SOMETHING); hR = pIF->Bar(i, pIB, &pIW); // actual call to the method! pIB->Release(); status = ORpcExportObjRef(pIW, pprIW); return(hR ? hR : SOMETHING); };
This is an example of the client side wrapper for Bar:
// assume some class CFoo that implements Bar method class CFoo : IUnknown, IFoo { UUID ipid; // one for each interface? handle_t h; virtual HRESULT QueryInterface(UUID iid, void **ppvoid); virtual HRESULT AddRef(); virtual HRESULT Release(); virtual HRESULT Bar(short i, IFoo * pIF, IWaz ** ppIW); }; HRESULT CFoo::Bar(short i, IFoo * pIF, IWaz ** ppIW) { OBJREF * prIF; OBJREF * prIW; HRESULT hR; RPC_STATUS status; status = RpcBindingSetObject(this->h, this->ipid); if (status) return(SOMETHING); status = ORpcExportObjRef(pIF, &prIF); if (status) return(SOMETHING); hR = Bar(this->h, i, prIF, &prIW); status = ORpcImportObjRef(prIW, ppIW); ORpcFreeObjRef(prIF); ORpcFreeObjRef(prIW); return(hR ? hR : SOMETHING); };
Since the implicit parameters are specified as IDL, the ORPC header received by RPC will contain many fields inserted by MIDL. Here are the definitions for the header on the wire.
/* An inbound header would be laid out as follows where the extent array is optional and there may be zero of more extents. An outbound header is laid out similarly. ORPCTHIS_WITHNOEXTENSIONS [ ORPC_EXTENT_ARRAY [ORPC_EXTENT]* ] */ typedef struct { COMVERSION version; // COM version number unsigned long flags; // INFO flags for presence of other data unsigned long reserved1; // set to zero LTID ltid; // logical thread id of caller unsigned long unique; // tag to indicate presence of extensions } ORPCTHIS_WITHNOEXTENSION; typedef struct { unsigned long rounded_size; // Actual number of extents. uuid_t id; // Extension identifier. unsigned long size; // Extension size. byte data[]; // Extension data. } ORPC_EXTENT; // Array of extensions. typedef struct { unsigned long rounded_size; // Actual number of extents unsigned long size; // Number of extents unsigned long unique_flag[]; // Flags to indicate presense of ORPC_EXTENTs } ORPC_EXTENT_ARRAY; typedef struct { unsigned long flags; // INFO flags for presence of other data unsigned long unique; // tag to indicate presence of extensions } ORPCTHAT_WITH_NOEXTENSIONS;