In COM, clients communicate with objects solely through the use of vtable-based interface instances. The state of the object is manipulated by invoking functions on those interfaces. For each interface method, the object provides an implementation that does the appropriate manipulation of the object internals.
Interface remoting provides the infrastructure and mechanisms to allow a method invocation to return an interface pointer to an object that is in a different process, perhaps even on a different machine. The infrastructure that performs the remoting of interfaces is transparent to both the client and the object server. Neither the client or object server is necessarily aware that the other party is in fact in a different process.
This chapter first explains how interface remoting works giving mention
to the interfaces and COM API functions involved.
The specifications for the
interfaces and the API functions themselves are given later in this chapter.
There is also a brief discussion about concurrency management at the end of
the chapter that involves an interface called
IMessageFilter()
.
The crux of the problem to be addressed in interface remoting can be stated as follows:
``Given an already existing remoted-interface connection between a client process and a server process, how can a method invocation through that connection return a new interface pointer so as to create a second remoted-interface connection between the two processes?''
We state the problem in this way so as to avoid for the moment the issue of how an initial connection is made between the client and the server process; we will return to that later.
Let's look at an example.
Suppose we have an object in a server process
which supports an interface
IFoo
, and that interface of
the object (and
IUnknown()
) has sometime in the past been
remoted to a client process through some means not here specified.
In the
client process, there is an object proxy which supports the exact same interfaces
as does the original server object, but whose
implementations
of methods in those interfaces are special, in that they forward calls they
receive on to calls on the real method implementations back in the server
object.
We say that the method implementations in the object proxy
marshal
the data, which is then conveyed to the server process,
where it is
unmarshaled.
That is, ``marshaling'' refers
to the packaging up of method arguments for transmission to a remote process;
``unmarshaling'' refers to the unpackaging of this data at the receiving end.
Notice that in a given call, the method arguments are marshaled and unmarshaled
in one direction, while the return values are marshaled and unmarshaled in
the other direction.
For concreteness, let us suppose that the
IFoo
interface
is defined as follows:
interface IFoo : IUnknown()
{
HRESULT ReturnABar([out]IBar **ppBar);
};
If in the client process
pFoo
->ReturnABar
(&pBar
) is invoked, then the object proxy
will forward this call on to the
IFoo::ReturnABar
method
in the server object, which will do whatever this method is supposed to do
in order to come up with some appropriate
IBar*
.
The server
object is then required to return this
IBar*
back to the
client process.
The act of doing this will end up creating a second connection
between the two processes:
It is the procedure by which this second connection is established which is the subject of our discussion here. This process involves two steps:
On the server side, the
IBar*
is packaged
or marshaled into a data packet.
The data packet is conveyed by some means to the client process, where the data it contains is unmarshaled to create the new object proxy.
[Footnote 43]
The term ``marshaling'' is a general one that is applied in the
industry to the packaging of any particular data type, not just interface
pointers, into a data packet for transmission through an RPC infrastructure.
Each different data type has different rules for how it is to marshaled: integers
are to be stored in a certain way, strings are to be stored in a certain way,
etc.
[Footnote 43]
Likewise, marshaled interface
pointers are to be stored in a certain way; the Component Object Model function
CoMarshalInterface
contains the knowledge of how this is to be done
(note
that we
will in this document not mention further any kind of marshaling other than
marshaling of interface pointers; that subject is well-explored in existing
RPC systems).
[Footnote 44]
The process begins with the code doing the marshaling of the returned
IBar*
interface.
This code has in hand a pointer to an interface
that it knows in fact to be an
IBar*
and that it wishes
to marshal.
To do so it calls
CoMarshalInterface
.
The first
step in
CoMarshalInterface
involves finding out whether
the object of which this is an interface in fact supports
custom
object marshaling
(often simply referred to as ``custom marshaling'').
Custom
object
marshaling is a mechanism that permits an object to be in control of creation
of remote object proxies to itself.
In certain situations, custom object marshaling
can be used to create a more efficient object proxy than would otherwise be
the case.
[Footnote 44]
Use of custom marshaling
is completely optional on the object's part; if the object chooses not to
support custom marshaling, then
standard interface marshaling
is used to marshal the
IBar*
.
Standard interface marshaling
uses a system-provided object proxy implementation (called the proxy manager)
in the client process.
This standard implementation is a generic piece of
code, in that it can be used as the object proxy for any interface on any
object.
However, the act of marshaling (and unmarshaling) method arguments
and return values is inherently interface-specific, since it is highly sensitive
to the semantics and data types used in the particular methods in question.
To accommodate this, the standard implementation dynamically loads in interface-specific
pieces of code as needed in order to do the parameter marshaling.
We shall discuss in great detail in a moment how standard interface marshaling works. First, however, we shall review custom object marshaling, as this provides a solid framework in which standard marshaling can be better understood.
[Footnote 45] Imagine that we are presently in a piece of code whose job it is to marshal an interface pointer that it has in hand. For clarity, in what follows we'll refer to this piece of code as the ``original marshaling stub.'' The general case is that the original marshaling stub does not statically [Footnote 45] know the particular interface identifier (IID) to which the pointer conforms; the IID may be passed to this code as a second parameter. This is a common paradigm in the Component Object Model. Extant examples of this paradigm include:
IUnknown::QueryInterface(REFIID riid, void** ppvObject); IOleItemContainer::GetObject(..., REFIID riid, void** ppvObject); IClassFactory::CreateInstance(..., REFIID riid, void** ppvNewlyCreatedObject);
Let us assume the slightly less general case where the marshaling stub
in fact does know a little bit about the IID: that the interface in fact derives
from
IUnknown()
.
This is a requirement for remoting: it
is not possible to remote interfaces which are not derived from
IUnknown()
.
To find out whether the object to which it has an interface supports
custom marshaling, the original marshaling stub simply does a
QueryInterface
for the interface
IMarshal()
.
That is, an object
signifies that it wishes to do custom marshaling simply by implementing the
IMarshal()
interface.
IMarshal()
is defined as
follows:
[ local, object, uuid(00000003-0000-0000-C000-000000000046) ] interfaceIMarshal()
:IUnknown()
{ HRESULT GetUnmarshalClass ( [in] REFIID riid, [in, unique] void *pv, [in] DWORD dwDestContext, [in, unique] void *pvDestContext, [in] DWORD mshlflags, [out] CLSID *pCid); HRESULT GetMarshalSizeMax ([in] REFIID riid, [in, unique] void *pv, [in] DWORD dwDestContext, [in, unique] void *pvDestContext, [in] DWORD mshlflags, [out] DWORD *pSize); HRESULT MarshalInterface ([in, unique]IStream()
*pStm, [in] REFIID riid, [in, unique] void *pv, [in] DWORD dwDestContext, [in, unique] void *pvDestContext, [in] DWORD mshlflags); HRESULT UnmarshalInterface ( [in, unique]IStream()
*pStm, [in] REFIID riid, [out] void **ppv); HRESULT ReleaseMarshalData ( [in, unique]IStream()
*pStm); HRESULT DisconnectObject ( [in] DWORD dwReserved); }
[Footnote 46] The idea is that if the object says ``Yes, I do want to do custom marshaling'' that the original marshaling stub will use this interface in order to carry out the task. The sequence of steps that carry this out is:
Using
GetUnmarshalClass
, the original marshaling
stub asks the object which kind of (i.e.: which class of) proxy object it
would like to have created on its behalf in the client process.
(optional on the part of the marshaling stub) Using
GetMarshalSizeMax
, the stub asks the object how big of a marshaling
packet it will need.
When asked, the object
will
return
an upper bound on the amount of space it will need.
[Footnote 46]
The marshaling stub allocates a marshaling packet of appropriate
size, then creates an
IStream*
which points into the buffer.
Unless in the previous step the marshaling stub asked the object for an upper
bound on the space needed, the
IStream*
must be able to
grow its underlying buffer dynamically as
IStream::Write()
calls are made.
The original marshaling stub asks the object to marshal its
data using
MarshalInterface
.
We will discuss the methods of this interface in detail later in this chapter.
At this point, the contents of the memory buffer pointed to by the
IStream*
together with the class tag returned in step (1) comprises
all the information necessary in order to be able to create the proxy object
in the client process.
It is the nature of remoting and marshaling that ``original
marshaling stubs''
such as
we have been discussing know how to communicate with the client process; recall
that we are assuming that an initial connection between the two processes
had already been established.
The marshaling stub now communicates to the
client process, by whatever means is appropriate, the class tag and the contents
of the memory that contains the marshaled interface pointer.
In the client
process, the proxy object is created as an instance of the indicated class
using the standard COM instance creation paradigm.
IMarshal()
is used as the initialization interface; the initialization method is
IMarshal::UnmarshalInterface
.
The unmarshaling process looks something
like the following:
void ExampleUnmarshal(CLSID& clsidProxyObject, IStream* pstm, IID& iidOriginallyMarshalled, void** ppvReturn) { IClassFactory* pcf; IMarshal* pmsh; CoGetClassObject(clsidProxyObject, CLSCTX_INPROC_HANDLER, NULL, IID_IClassFactory, (void**)&pcf); pcf->CreateInstance(NULL, IID_IMarshal, (void**)pmsh); pmsh->UnmarshalInterface(pstm, iidOriginallyMarshalled, ppvReturn); pmsh->ReleaseMarshalData(pstm) pmsh->Release(); pcf->Release(); }
There are several important reasons why an object may choose to do custom marshaling.
It permits the server implementation, transparently to the client, to be in complete control of the nature of the invocations that actually transition across the network. In designing component architectures, one often runs into a design tension between the interface which for simplicity and elegance one wishes to exhibit to client programmers and the interface that is necessary to achieve efficient invocations across the network. The former, for example, might naturally wish to operate in terms of small-grained simple queries and responses, whereas the latter might wish to batch requests for efficient retrieval. The client and the network interfaces are in design tension; custom marshaling is the crucial hook that allows us to have our cake and eat it too by giving the server implementor the ability to tune the network interface without affecting the interface seen by its client. When the object does custom marshaling, the client loses any ``COM provided'' communication to the original object. If the proxy wants to ``keep in touch'', it has to connect through some other means (RPC, Named pipe...) to the original object. Custom Object Marshaling can not be done on a per interface basic, because object identity is lost! Custom Object Marshaling is a sophisticated way for an object to pass a copy of an existing instance of itself into another execution context.
Some objects are of the nature that once they have been created, they are immutable: their internal state does not subsequently change. Many monikers are an example of such objects. These sorts of objects can be efficiently remoted by making independent copies of themselves in client processes. Custom marshaling is the mechanism by which they can do that, yet have no other party be the wiser for it.
Objects which already are proxy objects can use custom marshaling to avoid creating proxies to proxies; new proxies are instead short-circuited back to the original server. This is both an important efficiency and an important robustness consideration.
Object implementations whose whole state is kept in shared memory can often be remoted to other process on the same machine by creating an object in the client that talks directly to the shared memory rather than back to the original object. This can be a significant performance improvement, since access to the remoted object does not result in context switches. The present Microsoft Compound File implementation is an example of objects using this kind of custom marshaling.
[Footnote 47] [Footnote 48] If the object being marshaled [Footnote 47] chooses not to implement custom object marshaling, a ``default'' or ``standard'' object marshaling technique is used. An important part of this standard marshaling technique involves locating and loading the interface-specific pieces of code that are responsible for marshaling and unmarshaling remote calls to instances of that interface. We call these interface-specific pieces of code used in standard marshaling and unmarshaling ``interface proxies'' and ``interface stubs'' respectively. [Footnote 48] (It is important not to confuse interface proxies with the object proxy, which relates to the whole representative in the client process, rather than just one interface on that representative. We apologize for the subtleties of the terminology.)
The following figure gives a slightly simplified view of how the standard client- and server-side structures cooperate.
When an interface of type
IFoo
needs to be remoted,
a system registry is consulted under a key derived from
IID_IFoo
to locate a class id that implements the interface proxy and interface
stub for the given interface.
Both the interface proxies and the interface
stubs for a given interface must be implemented by the same class.
This class
is typically either a system provided class that can provide marshaling support
for any interface described in a type library or a class generated by the
IDL compiler.
The IDL compiler is a tool whose input is a description of the function signatures and semantics of the interface, written in some ``interface description language,'' often known as ``IDL.'' However, while highly recommended and encouraged for accuracy's sake, the use of such a tool is by no means required; interface proxies and stubs are merely Component Object Model components which are used by the RPC infrastructure, and as such, can be written in any manner desired so long as the correct external contracts are upheld. From a logical perspective, it is ultimately the programmer who is the designer of a new interface who is responsible for ensuring that all interface proxies and stubs that ever exist agree on the representation of their marshaled data. The programmer has the freedom to achieve this by whatever means he sees fit, but with that freedom comes the responsibility for ensuring the compatibility.
[Footnote 49]
In the figure, the ``stub manager'' is ``conceptual'' in the sense
that while it useful in this documentation to have a term to refer to the
pieces of code and state on in the server-side RPC infrastructure which service
the remoting of a given object, there is no direct requirement that the code
and state take any particular well-specified form.
[Footnote 49]
In contrast, on the client side, there is an identifiable piece of state and
associated behavior which appears to the client code to be the one, whole
object.
The term ``proxy manager'' is used to refer to the COM Library provided
code that manages the client object identity, etc., and which dynamically
loads in interface proxies as needed (per
QueryInterface()
calls).
The proxy manager implementation is intimate with the client-side
RPC channel implementation, and the server-side RPC channel implementation
is intimate with the stub manager implementation.
Interface proxies are created by the client-side COM Library infrastructure using a code sequence resembling the following:
clsid = LookUpInRegistry(key derived from iid) CoGetClassObject(clsid, CLSCTX_SERVER, NULL, IID_IPSFactoryBuffer, &pPSFactory)); pPSFactory->CreateProxy(pUnkOuter, riid, &pProxy, &piid);
Interface stubs are created by the server-side RPC infrastructure using a code sequence resembling:
clsid = LookUpInRegistry(key derived from iid) CoGetClassObject(clsid, CLSCTX_SERVER, NULL, IID_IPSFactoryBuffer, &pPSFactory)); pPSFactory->CreateStub(iid, pUnkServer, &pStub);
In particular, notice that the class object is talked-to with
IPSFactoryBuffer()
interface rather than the more common
IClassFactory()
.
The interfaces mentioned here are as follows:
interfaceIPSFactoryBuffer()
:IUnknown()
{ HRESULT CreateProxy(pUnkOuter, iid, ppProxy, ppv); HRESULT CreateStub(iid, pUnkServer, ppStub); }; interfaceIRpcChannelBuffer()
:IUnknown()
{ HRESULT GetBuffer(pMessage, riid); HRESULT SendReceive(pMessage, pStatus); HRESULT FreeBuffer(pMessage); HRESULT GetDestCtx(pdwDestCtx, ppvDestCtx); HRESULT IsConnected(); }; interfaceIRpcProxyBuffer()
:IUnknown()
{ HRESULT Connect(pRpcChannelBuffer); void Disconnect(); }; interfaceIRpcStubBuffer()
:IUnknown()
{ HRESULT Connect(pUnkServer); void Disconnect(); HRESULT Invoke(pMessage, pChannel); IRPCStubBuffer* IsIIDSupported(iid); ULONG CountRefs(); HRESULT DebugServerQueryInterface(ppv); void DebugServerRelease(pv); };
Suppose an interface proxy receives a method invocation on one of its
interfaces (such as
IFoo
,
IBar
, or
IBaz
in the above figure).
The interface proxy's implementation
of this method first obtains a marshaling packet from its RPC channel using
IRpcChannelBuffer::GetBuffer
.
The process of marshaling the arguments
will copy data into the buffer.
When marshaling is complete, the interface
proxy invokes
IRpcChannelBuffer::SendReceive
to send the
method invocation across the ``wire'' to the corresponding interface stub.
When
IRpcChannelBuffer::SendReceive
returns, the contents
of buffer into which the arguments were marshaled will have been replaced
by the return values marshaled from the interface stub.
The interface proxy
unmarshals the return values, invokes
IRpcChannelBuffer::FreeBuffer
to free the buffer, then returns the return values to the original
caller of the method.
It is the implementation of
IRpcChannelBuffer::SendReceive
that actually sends the request over to the server process.
It
is only the channel who knows or cares how to identify the server process
and object within that process to which the request should be sent; this encapsulation
allows the architecture we
are describing here to function for a variety of different kinds of channels:
intra-machine channels, inter-machine channels (i.e.: across the network),
etc.
The channel implementation knows how to forward the request onto the
appropriate stub manager object in the appropriate process.
From the
perspective of this specification, the
channel and the stub manager are intimate with each other (and intimate with
the proxy manager, for that matter).
Through this intimacy, eventually the
appropriate interface stub receives an
IRpcStubBuffer::Invoke
call.
The stub unmarshals the arguments from the provided buffer, invokes
the indicated method on the server object, and marshals the return values
back into a new buffer, allocated by a call to
IRpcChannelBuffer::GetBuffer
.
The stub manager and the channel then cooperate to ferry the return
data packet back to the interface proxy, who is still in the middle of
IRpcChannelBuffer::SendReceive
.
IRpcChannelBuffer::SendReceive
returns to the proxy, and we proceed as just described above.
When created, interface proxies are always aggregated into the larger
object proxy: at interface-proxy-creation time, the proxy is given the
IUnknown*
to which it should delegate its
QueryInterface
, etc., calls, as per the usual aggregation rules.
When connected,
the interface proxy is also given (with
IRpcProxyBuffer::Connect
) a pointer to an
IRpcChannelBuffer()
interface instance.
It is through this pointer
that the interface proxy actually sends calls to the server process.
Interface
proxies bring a small twist to the normal everyday aggregation scenario.
In
aggregation, each interface supported by an aggregateable object is classified
as either ``external'' or ``internal.'' External interfaces are the norm.
They are the ones whose instances are exposed directly to the clients of the
aggregate as whole.
It is
always
the case that a
QueryInterface
that requests an external interface of an aggregated
object should be delegated by the object to its controlling unknown (ditto
for
AddRef
and
Release
).
Internal interfaces,
on the other hand, are never exposed to outside clients.
Instead, they are
solely for the use of the controlling unknown in manipulating the aggregated
object.
QueryInterface
for internal interfaces should
never
be delegated to the controlling unknown (ditto again).
In
the common uses of aggregation, the
IUnknown()
interface
on the object is the only internal interface.
The twist that interface proxies
bring is that
IRpcProxyBuffer()
is
also
an internal interface.
Interface stubs, by contrast with interface proxies, are not aggregated,
since there is no need that they appear to some external client to be part
of a larger whole.
When connected, an interface stub is given (with
IRpcStubBuffer::Connect
) a pointer to the server object to which
they should
forward
invocations that they receive.
A given interface proxy instance can if it chooses to do so service
more than one interface.
For example, in the above figure, one interface proxy
could have chosen to service
both
IFoo and IBar.
To accomplish
this, in addition to installing itself under the appropriate registry entries,
the proxy should support
QueryInterface()
ing from one supported
interface (and from
IUnknown()
and
IRpcProxyBuffer()
) to the other interfaces, as usual.
When the Proxy Manager in
a given object proxy finds that it needs the interface proxy for some new
interface that it doesn't already have, before it goes out to the registry
to load in the appropriate code using the code sequence described above, it
first does a
QueryInterface
for the new interface id (IID)
on all of its
existing
interface proxies.
If one of them
supports the interface, then it is used rather than loading a new interface
proxy.
Interface stub instances, too, can service more than one interface on
a server object.
However, the extent to which they can do so is quite restricted:
a given interface stub instance may support one or more interfaces only if
that set of interfaces has in fact a strict single-inheritance relationship.
In short, a given interface stub needs to know how to interpret a given method
number that it is asked to invoke without at that same time also being told
the interface id (IID) in which that method belongs; the stub must already
know
the relevant IID.
The IID which an interface stub is initially
created to service is passed as parameter to
IPSFactoryBuffer::CreateStub
.
After
creation, the interface stub may from time to time be asked using
IRpcStubBuffer::IsIIDSupported
if it in fact would also like be
used to service another IID.
If the stub also supports the second IID, then
it should
return the appropriate
IRpcStubBuffer*
for that IID; otherwise, the stub buffer should
return
NULL
.
This permits the stub manager in certain cases
to optimize the loading of interface stubs.
Both proxies and stubs will at various times have need to allocate or
free memory.
Interface proxies, for example, will need to allocate memory
in which to return out parameters to their caller.
In this respect interface
proxies and
interface
stubs are just normal Component Object Model components, in that they should
use the standard task allocator; see
CoGetMalloc()
.
See
also the earlier
discussion
regarding specific rules for passing
in
,
out
,
and
in out
pointers.
On Microsoft Windows platforms, the ``key derived from IID'' under which the registry is consulted to learn the proxy/stub class is as follows:
Interfaces {IID} ProxyStubClsid32 = {CLSID}
Here {CLSID} is a shorthand for any class id; the actual value of the
unique id is put between the {}'s; e.g.
{DEADBEEF
-DEAD-BEEF-C000-000000000046};
all digits are upper case hex and there can be no spaces.
This string format
for a unique id (without the {}'s) is the same as The Open Group's DCE
TM
standard and is the result of the
StringFromCLSID()
routine.
{IID} is a shorthand for an interface id; this is
similar to {CLSID};
StringFromIID()
can be used to produce this string.
Handler marshaling is a third variation on marshaling, one closely related to standard marshaling. Colloquially, one can think of it as a middle ground between raw standard marshaling and full custom marshaling.
In handler marshaling, the object specifies that it would like to have
some amount of client-side state; this is designated by the class returned
by
IStdMarshalInfo::GetClassForHandler
.
However, this handler
class rather than
fully taking over the remoting to the object instead aggregates in the default
handler, which carries out the remoting in the standard manner as described
above.
In the architecture described here, nothing has yet to be said about representation or format standards for the data that gets placed in marshaling packets. There is a good reason for this. In the Component Object Model architecture, the only two parties that have to agree on what goes into a marshaling packet are the code that marshals the data into the packet and the code that unmarshals it out again: the interface proxies and the interface stubs. So long as we are dealing only with intra-machine procedure calls (i.e.: non-network), then we can reasonably assume that pairs of interface proxies and stubs are always installed together on the machine. In this situation, we have no need to specify a packet format standard; the packet format can safely be a private matter between the two piece of code.
However, once a network is involved, relying on the simultaneous installation of corresponding interface proxies and stubs (on different machines) is no longer a reasonable thing to do. Thus, when the a method invocation is in fact remoted over a network, it is strongly recommended that the data marshaled into the packet to conform to a published standard (NDR), though, as pointed out above, it is technically the interface-designer's responsibility to achieve this correspondence by whatever means he sees fit.
Earlier we said we would later discuss how an initial remoting connection is established between two processes. It is now time to have that discussion.
The real truth of the matter is that the initial connection is established by some means outside of the architecture that we have been discussing here. The minimal that is required is some primitive communication channel between the two processes. As such, we cannot hope to discuss all the possibilities. But we will point out some common ones.
One common approach is that initial connections are established just like other connections: an interface pointer is marshaled in the server process, the marshaled data packet is ferried the client process, and it is unmarshaled. The only twist is that the ferrying is done by some means other than the RPC mechanism which we've been describing. There are many ways this could be accomplished. The most important, by far is one where the marshaled data is passed as an out-parameter from an invocation on a well-known endpoint to a Service Control Manager.
The
IMarshal()
interface enables an COM object to
define and manage the marshaling of its interface pointers.
The alternative
is to use COM's default implementation, the preferred choice in all but a
few special cases (see ``When to Implement'').
``Marshaling'' is the process of packaging data into packets for transmission to a different process or machine. ``Unmarshaling'' is the process of recovering that data at the receiving end. In any given call, method arguments are marshaled and unmarshaled in one direction, while return values are marshaled and unmarshaled in the other.
Although marshaling applies to all data types, interface pointers require special handling. The fundamental problem is how client code running in one address space can correctly dereference a pointer to an interface on an object residing in a different address space. COM's solution is for a client application to communicate with the original object through a surrogate object, or proxy, which lives in the client's process. The proxy holds a reference to an interface on the original object and hands the client a pointer to an interface on itself. When the client calls an interface method on the original object, its call is actually going to the proxy. Therefore, from the client's point of view, all calls are in-process.
On receiving a call, the proxy marshals the method arguments and, through some means of interprocess communication, such as RPC, passes them along to code in the server process, which unmarshals the arguments and passes them to the original object. This same code marshals return values for transmission back to the proxy, which unmarshals the values and passes them to the client application.
IMarshal()
provides methods for creating, initializing,
and managing a proxy in a client process; it does not dictate how the proxy
should communicate with the original object.
COM's default implementation
of
IMarshal()
uses RPC.
When you implement this interface
yourself, you are free to choose any method of interprocess communication
you deem to be appropriate for your application--shared memory, named
pipe, window handle, RPC--in short, whatever works.
Implement
IMarshal()
only
when
you believe that you can realize significant optimizations to COM's default
implementation.
In practice, this will rarely be the case.
However, there
are occasions where implementing
IMarshal()
may be preferred:
The objects you are writing keep their state in shared memory.
In this
case, both the original process and the client process uses proxies that refer
to the shared memory.
This type of custom marshaling is possible only if the
client process is on the same machine as the original process.
COM-provided
implementations of
IStorage()
and
IStream()
are
examples of this type of custom marshaling.
The objects you are writing are immutable, that is, their
state does not
change after creation.
Instead of forwarding method calls to the original
objects, you simply create copies of those objects in the client process.
This
technique avoids the cost of switching from one process to another.
Some
monikers are examples of immutable objects; if you are implementing your own
moniker class, you should evaluate the costs and benefits of implementing
IMarshal()
on your moniker objects.
Objects that themselves are proxy objects can use custom marshaling to avoid creating proxies to proxies. Instead, the existing proxy can refer new proxies back to the original object. This capability is important for the sake of both efficiency and robustness.
Your server application wants to manage how calls are made across the network without affecting the interface exposed to clients. For example, if an end user were making changes to a database record, the server might want to cache the changes until the user has committed them all, at which time the entire transaction would be forwarded in a single packet. Using a custom proxy would enable the caching and batching of changes in this way.
When you choose to implement
IMarshal()
, you must
do so for both
your original object and the proxy you create for it.
When implementing the
interface on either object or proxy, you simply return
E_NOTIMPL
for the
methods that are not implemented.
COM uses your implementation of
IMarshal()
in the
following manner: When
it's necessary to create a remote interface pointer to your object (that is,
when a pointer to your object is passed as an argument in a remote function
call), COM queries your object for the
IMarshal()
interface.
If your
object implements it, COM uses your
IMarshal()
implementation
to create
the proxy object.
If your object does not implement
IMarshal()
,
COM uses
its default implementation.
How you choose to structure the proxy is entirely up to you.
You can
write the
proxy to use whatever mechanisms you deem appropriate for communicating with
the original object.
You can also create the proxy as either a stand-alone
object or as part of a larger aggregation such as a handler.
However you choose
to structure the proxy, it must implement
IMarshal()
to
work at all.
You
must also generate a CLSID for the proxy to be returned by your implementation
of
IMarshal::GetUnmarshalClass()
on the original object.
COM calls this interface as part of system-provided marshaling support.
COM's calls are wrapped in calls to
CoMarshalInterface()
and
CoUnmarshalInterface()
.
Your code typically will not need
to call
this interface.
Special circumstances where you might choose to do so are
discussed in the ``Notes to Callers'' section for each method.
IUnknown Methods
|
Description
|
QueryInterface()
|
Returns pointers to supported interfaces. |
AddRef()
|
Increments reference count. |
Release()
|
Decrements reference count. |
|
Description
|
GetUnmarshalClass
|
Returns CLSID of unmarshaling code. |
GetMarshalSizeMax
|
Returns size of buffer needed during marshaling. |
MarshalInterface
|
Marshals an interface pointer. |
UnmarshalInterface
|
Unmarshals an interface pointer. |
ReleaseMarshalData
|
Destroys a marshaled data packet. |
DisconnectObject
|
Severs all connections. |
- Forcibly releases all external connections to an object.
The object's
server
calls the object's implementation of this method prior to shutting down.
IMarshal::DisconnectObject()
HRESULT DisconnectObject(
#include <objidl.h>
DWORD dwReserved
);
This method is implemented on the object, not the proxy.
The usual case in which this method is called occurs when an end user
forcibly closes a COM server that has one or more running objects that
implement
IMarshal()
.
Prior to shutting down, the server
calls the
CoDisconnectObject()
helper function to sever external
connections
to
all
its running objects.
For each object that implements
IMarshal()
, however, this function calls
IMarshal::DisconnectObject()
, so that each object that
manages its
own marshaling can take steps to notify its proxy that it is about to shut
down.
As part of its normal shutdown code, a server should call the
CoDisconnectObject()
function, which in turn calls
IMarshal::DisconnectObject()
, on each of its running objects
that
implements
IMarshal()
.
The outcome of any implementation of this method should be to enable
a proxy to
respond to all subsequent calls from its client by returning
RPC_E_DISCONNECTED
or
CO_E_OBJECTNOTCONNECTED
rather than attempting to forward
the calls on to
the original object.
It is up to the client, of course, to destroy the proxy.
If you are implementing this method for an immutable object, such as
a moniker,
your implementation doesn't need to do anything because such objects are
typically copied whole into the client's address space.
Therefore, they have
neither a proxy nor a connection to the original object.
For more information
on marshaling immutable objects, see
IMarshal()
, ``When
to Implement.''
[in] Reserved for future use; must be zero.
To ensure compatibility
with
future use,
DisConnectObject
must not
check for zero.
The method supports the standard return value
E_FAIL
,
as well as the
following:
S_OK
The object was disconnected successfully.
- Returns an upper bound on the number of bytes needed to marshal
the
specified interface pointer on the specified object.
IMarshal::GetMarshalSizeMax()
HRESULT GetMarshalSizeMax(
#include <objidl.h>
REFIID riid,
void * pv,
DWORD dwDestContext,
void * pvDestContext,
DWORD mshlflags,
ULONG * pSize
);
This method is called indirectly, in a call to
CoGetMarshalSizeMax()
, by whatever code in the server process
is
responsible for marshaling a pointer to an interface on an object.
This
marshaling code is usually a stub generated by COM for one of several
interfaces that can marshal a pointer to an interface implemented on an
entirely different object.
Examples include the
IClassFactory()
and
IOleItemContainer()
interfaces.
For purposes of discussion,
the
code responsible for marshaling a pointer is here called the ``marshaling
stub.''
To create a proxy for an object, COM requires two pieces of information from the original object: the amount of data to be written to the marshaling stream and the proxy's CLSID.
The marshaling stub obtains these two pieces of information with successive
calls to
CoGetMarshalSizeMax()
and
CoMarshalInterface()
.
The marshaling stub, through a call to
CoGetMarshalSizeMax()
, calls
the object's implementation of this method to preallocate the stream buffer
that will be passed to
IMarshal::MarshalInterface()
.
You do not explicitly call this method if you are:
Implementing existing COM interfaces, or
Defining your own custom interfaces, using the Microsoft Interface Definition Language (MIDL).
In both cases, the MIDL-generated stub automatically makes the call.
If you are
not
using MIDL to define your own interface
your marshaling stub does not have to call
GetMarshalSizeMax
, though doing so is highly recommended.
An object
knows better than an interface stub what the maximum size of a marshaling
data
packet is likely to be.
Therefore, unless you are providing an automatically
growing stream that is so efficient that the overhead of expanding it is
insignificant, you should call this method even when implementing your own
interfaces.
The value returned by this method is only guaranteed to be valid as long as the internal state of the object being marshaled does not change. Therefore, the actual marshaling should be done immediately after this function returns, or the stub runs the risk that the object, because of some change in state, might require more memory to marshal than it originally indicated.
Your implementation of
MarshalInterface
will use
this buffer to
write marshaling data into the stream.
If the buffer is too small, the
marshaling operation will fail.
Therefore, the value returned by this method
must
be a conservative estimate of the amount of data
that will, in
fact, be needed to marshal the interface.
Violation of this requirement should
be treated as a catastrophic error.
In a subsequent call to
IMarshal::MarshalInterface()
,
your
IMarshal()
implementation cannot rely on the caller actually having called
GetMarshalSizeMax
beforehand.
It must still be wary of
STG_E_MEDIUMFULL
errors returned by the stream and be prepared to handle them gracefully.
To ensure that your implementation of
GetMarshalSizeMax
will continue to
work properly as new destination contexts are supported in the future, delegate
marshaling to COM's default implementation for all
dwDestContext
values
that your implementation does not understand.
To delegate marshaling to COM's
default implementation, call the
CoGetStandardMarshal()
function.
[in] Reference to the identifier of the interface to be marshaled.
[in] Interface pointer to be marshaled; can be
NULL
.
[in] Destination context where the specified interface is
to be unmarshaled.
Values for
dwDestContext
come from the enumeration
MSHCTX
.
Currently, unmarshaling can occur either in another
apartment of the current process (MSHCTX_INPROC) or in another process on
the
same computer as the current process (MSHCTX_LOCAL
).
[in] Reserved for future use; must be
NULL
.
[in] Flag indicating whether the data to be marshaled is to
be transmitted
back
to the client process--the normal case--or written to a global table,
where it can be retrieved by multiple clients.
Valid values come from the
MSHLFLAGS
enumeration.
[out] Pointer to the upper bound on the amount of data to be written to the marshaling stream.
The method supports the standard return value
E_FAIL
,
as well as the
following:
S_OK
The maximum size was returned successfully.
E_NOINTERFACE
The specified interface was not supported.
CoGetMarshalSizeMax()
,
IMarshal::MarshalInterface()
- Returns the CLSID that COM uses to locate the DLL containing the
code for
the corresponding proxy.
COM loads this DLL to create an uninitialized
instance of the proxy.
IMarshal::GetUnmarshalClass()
HRESULT GetUnmarshalClass(
#include <objidl.h>
REFIID riid,
void * pv,
DWORD dwDestContext,
void * pvDestContext,
DWORD mshlflags,
CLSID * pCid
);
This method is called by whatever code in the server process may be
responsible for marshaling a pointer to an interface on an object.
This
marshaling code is usually a stub generated by COM for one of several
interfaces that can marshal a pointer to an interface implemented on an
entirely different object.
Examples include the
IClassFactory()
and
IOleItemContainer()
interfaces.
For purposes of this discussion,
the code responsible for marshaling a pointer is called the ``marshaling
stub.''
To create a proxy for an object, COM requires two pieces of information from the original object: the amount of data to be written to the marshaling stream and the proxy's CLSID.
The marshaling stub obtains these two pieces of information with successive
calls to
CoGetMarshalSizeMax()
and
CoMarshalInterface()
.
The marshaling stub calls the object's implementation of this method to obtain the CLSID to be used in creating an instance of the proxy. The client, upon receiving the CLSID, loads the DLL listed for it in the system registry.
You do not explicitly call this method if you are:
Implementing existing COM interfaces, or
Defining your own interfaces using the Microsoft Interface Definition Language (MIDL).
In both cases, the stub automatically makes the call.
If you are not using MIDL to define your own interface, your stub must call this method, either directly or indirectly, to get the CLSID that the client-side COM Library needs to create a proxy for the object implementing the interface.
If the caller has a pointer to the interface to be marshaled, it should,
as a
matter of efficiency, use the
pv
parameter to pass that
pointer.
In this
way, an implementation that may use such a pointer to determine the appropriate
CLSID for the proxy does not have to call
IUnknown::QueryInterface()
on itself.
If a caller does
not have a
pointer to the interface to be marshaled, it can pass
NULL
.
COM calls
GetUnmarshalClass
to obtain the CLSID to
be used for
creating a proxy in the client process.
The CLSID to be used for a proxy is
normally
not
that of the original object (see ``Notes
to Implementors''
for the exception), but one you will have generated (using the
GUIDGEN.EXE
tool
supplied with the Win32 SDK) specifically for your proxy object.
Implement this method for each object that provides marshaling for one or more of its interfaces. The code responsible for marshaling the object writes the CLSID, along with the marshaling data, to a stream; COM extracts the CLSID and data from the stream on the receiving side.
If your proxy implementation consists simply of copying the entire original object into the client process, thereby eliminating the need to forward calls to the original object, the CLSID returned would be the same as that of the original object. This strategy, of course, is advisable only for objects that are not expected to change.
If the
pv
parameter is
NULL
and your implementation needs an interface
pointer, it can call
IUnknown::QueryInterface()
on the
current
object to get it.
The
pv
parameter exists merely to improve
efficiency.
To ensure that your implementation of
GetUnmarshalClass
continues to
work properly as new destination contexts are supported in the future, delegate
marshaling to COM's default implementation for all
dwDestContext
values
that your implementation does not handle.
To delegate marshaling to COM's
default implementation, call the
CoGetStandardMarshal()
function.
[in] Reference to the identifier of the interface to be marshaled.
[in] Pointer to the interface to be marshaled; can be
NULL
if the caller does not have a pointer to the desired interface.
[in] ``Destination context'' where the specified interface
is to be
unmarshaled.
Values for
dwDestContext
come from the enumeration
MSHCTX
.
Currently, unmarshaling can occur either in another
apartment of the current process (MSHCTX_INPROC) or in another process on
the
same computer as the current process (MSHCTX_LOCAL
).
[in] Reserved for future use; must be
NULL
.
[in] Whether the data to be marshaled is to be transmitted
back to the
client
process--the normal case--or written to a global table, where it
can
be retrieved by multiple clients.
Valid values come from the
MSHLFLAGS
enumeration.
[out] Pointer to the CLSID to be used to create a proxy in the client process.
Returns
S_OK
if successful; otherwise,
S_FALSE
.
- Writes into a stream the data required to initialize a proxy object
in some client process.
IMarshal::MarshalInterface()
HRESULT MarshalInterface(
#include <objidl.h>
IStream * pStm,
REFIID riid,
void * pv,
DWORD dwDestContext,
void * pvDestContext,
DWORD mshlflags
);
This method is called indirectly, in a call to
CoMarshalInterface()
, by whatever code in the server process
may be
responsible for marshaling a pointer to an interface on an object.
This
marshaling code is usually a stub generated by COM for one of several
interfaces that can marshal a pointer to an interface implemented on an
entirely different object.
Examples include the
IClassFactory()
and
IOleItemContainer()
interfaces.
For purposes of this discussion,
the code
responsible for marshaling a pointer is called the ``marshaling stub.''
Normally, rather than calling
IMarshal::MarshalInterface()
directly, your marshaling stub instead should call the
CoMarshalInterface()
function, which contains a call to
this method.
The
stub makes this call to command an object to write its marshaling data into
a
stream.
The stub then either passes the marshaling data back to the client
process or writes it to a global table, where it can be unmarshaled by multiple
clients.
The stub's call to
CoMarshalInterface()
is normally
preceded by a
call to
CoGetMarshalSizeMax()
, to get the maximum size
of the
stream buffer into which the marshaling data will be written.
You do not explicitly call this method if you are:
Implementing existing COM interfaces, or
Defining your own interfaces using the Microsoft Interface Definition Language (MIDL).
In both cases, the MIDL-generated stub automatically makes the call.
If you are not using MIDL to define your own interface, your marshaling
stub
must call this method, either directly or indirectly.Your stub implementation
should call
MarshalInterface
immediately after its previous
call to
IMarshal::GetMarshalSizeMax()
returns.
Because the value
returned
by
GetMarshalSizeMax
is guaranteed to be valid only so
long as the
internal state of the object being marshaled does not change, a delay in
calling
MarshalInterface
runs the risk that the object
will require a
larger stream buffer than originally indicated.
If the caller has a pointer to the interface to be marshaled, it should,
as a
matter of efficiency, use the
pv
parameter to pass that
pointer.
In this
way, an implementation that may use such a pointer to determine the appropriate
CLSID for the proxy does not have to call
IUnknown::QueryInterface()
on itself.
If a caller does
not have a
pointer to the interface to be marshaled, it can pass
NULL
.
Your implementation of
IMarshal::MarshalInterface()
must write to
the stream whatever data is needed to initialize the proxy on the receiving
side.
Such data would include a reference to the interface to be marshaled,
a
MSHLFLAGS
value specifying whether the data should be returned
to the
client process or written to a global table, and whatever is needed to connect
to the object, such as a named pipe, handle to a window, or pointer to an
RPC
channel.
Your implementation should not assume that the stream is large enough
to hold
all the data.
Rather, it should gracefully handle a
STG_E_MEDIUMFULL
error.
Just before exiting, your implementation should position the seek pointer
in
the stream immediately after the last byte of data written.
If the
pv
parameter is
NULL
and your implementation needs an interface
pointer, it can call
IUnknown::QueryInterface()
on the
current
object to get it.
The
pv
parameter exists merely to improve
efficiency.
To ensure that your implementation of
MarshalInterface
continues to work
properly as new destination contexts are supported in the future, delegate
marshaling to COM's default implementation for all
dwDestContext
values
that your implementation does not handle.
To delegate marshaling to COM's
default implementation, call the
CoGetStandardMarshal()
helper
function.
Using the
MSHLFLAGS
enumeration, callers can specify
whether an
interface pointer is to be marshaled back to a single client or written to
a
global table, where it can be unmarshaled by multiple clients.
You must make
sure that your object can handle calls from the multiple proxies that might
be
created from the same initialization data.
[in] Pointer to the stream to be used during marshaling.
[in] Reference to the identifier of the interface to be marshaled.
This
interface must be derived from the
IUnknown()
interface.
[in] Pointer to the interface pointer to be marshaled; can
be
NULL
if the caller does not have a pointer to
the desired interface.
[in] Destination context where the specified interface is
to be unmarshaled.
Values for
dwDestContext
come from the enumeration
MSHCTX
.
Currently, unmarshaling can occur either in another
apartment of the current process (MSHCTX_INPROC) or in another process on
the
same computer as the current process (MSHCTX_LOCAL
).
[in] Reserved for future use; must be zero.
[in] Whether the data to be marshaled is to be transmitted
back to the
client
process--the normal case--or written to a global table, where it
can
be retrieved by multiple clients.
Valid values come from the
MSHLFLAGS
enumeration.
The method supports the standard return value
E_FAIL
,
as well as the
following:
S_OK
The interface pointer was marshaled successfully.
E_NOINTERFACE
The specified interface is not supported.
STG_E_MEDIUMFULL
The stream is full.
CoGetStandardMarshal()
,
IMarshal::GetMarshalSizeMax()
,
IMarshal::GetUnmarshalClass()
,
IMarshal::UnmarshalInterface()
,
Writing a Custom
Interface
- Destroys a marshaled data packet.
IMarshal::ReleaseMarshalData()
HRESULT ReleaseMarshalData(
#include <objidl.h>
IStream * pStm
);
If an object's marshaled data packet does not get unmarshaled in the
client process space, and the packet is no longer needed.
The client calls
ReleaseMarshalData
on the proxy's
IMarshal()
implementation to
instruct the object to destroy the data packet.
The call occurs within the
CoReleaseMarshalData()
function.
The data packet serves
as an
additional reference on the object, and releasing the data is like releasing
an
interface pointer by calling
IUnknown::Release()
.
If the marshaled data packet somehow does not arrive in the client process,
or
ReleaseMarshalData
is not successfully re-created in the
proxy, COM can
call this method on the object itself.
You will rarely if ever have occasion to call this method yourself.
A
possible exception would be if you were to implement
IMarshal()
on a class
factory for a class object on which you are also implementing
IMarshal()
.
In this case, if you are marshaling the object to a table, where it can be
retrieved by multiple clients, you might, as part of your unmarshaling routine,
call
ReleaseMarshalData
to release the data packet for
each proxy.
If your implementation stores state information about marshaled data packets, you can use this method to release the state information associated with the data packet represented by pStm. Your implementation should also position the seek pointer in the stream past the last byte of data.
[in] Pointer to a stream that contains the data packet to be destroyed.
The method supports the standard return value
E_FAIL
,
as well as the
following:
S_OK
The data packet was released successfully.
IStream()
errorsThis function can also return any of the stream-access error
values
for the
IStream()
interface.
CoUnMarshalInterface
,
CoReleaseMarshalData()
- Initializes a newly created proxy and returns an interface pointer
to that proxy.
IMarshal::UnmarshalInterface()
HRESULT UnmarshalInterface(
#include <objidl.h>
IStream * pStm,
REFIID riid,
void ** ppv
);
The COM library in the process where unmarshaling is to occur calls the proxy's implementation of this method.
You do not call this method directly.
There are, however, some
situations in which you might call it indirectly through a call to
CoUnmarshalInterface()
.
For example, if you are implementing
a
stub, your implementation would call
CoUnmarshalInterface()
when the stub
receives an interface pointer as a parameter in a method call.
The proxy's implementation should read the data written to the stream
by
the original object's implementation of
IMarshal::MarshalInterface()
and use that data to initialize
the
proxy object whose CLSID was returned by the marshaling stub's call to the
original object's implementation of
IMarshal::GetUnmarshalClass()
.
To return the appropriate interface pointer, the proxy implementation
can
simply call
IUnknown::QueryInterface()
on itself, passing
the
riid
and
ppv
parameters.
However,
your implementation of
UnmarshalInterface
is free to create a different object
and, if
necessary, return a pointer to it.
Just before exiting, even if exiting with an error, your implementation should reposition the seek pointer in the stream immediately after the last byte of data read.
[in] Pointer to the stream from which the interface pointer is to be unmarshaled.
[in] Reference to the identifier of the interface to be unmarshaled.
[out] Indirect pointer to the interface.
The method supports the standard return value
E_FAIL
,
as well as the
following:
S_OK
The interface pointer was unmarshaled successfully.
E_NOINTERFACE
The specified interface was not supported.
IMarshal::GetUnmarshalClass()
,
IMarshal::MarshalInterface()
COM uses its own internal implementation of the
IMarshal()
interface to marshal any object that does not provide its own implementation.
COM makes this determination by querying the object for
IMarshal()
.
If the interface is missing, COM defaults to its internal implementation.
COM's default implementation of
IMarshal()
uses a
generic proxy for each
object, and creates individual stubs and proxies, as they are needed, for
each
interface implemented on the object.
This mechanism is necessary because COM
cannot know in advance what particular interfaces a given object may implement.
Developers who do not use COM's default marshaling, electing instead to write
their own proxy and marshaling routines, know at compile time all the
interfaces to be found on their objects and therefore understand exactly what
marshaling code is required.
COM, in providing marshaling support for all
objects, must do so at run time.
The interface proxy resides in the client process; the interface stub, in the server. Together, each pair handles all marshaling for its interface. The job of each interface proxy is to marshal arguments and unmarshal return values and out parameters that are passed back and forth in subsequent calls to its interface. The job of each interface stub is to unmarshal function arguments and pass them along to the original object, then marshal the return values and out parameters that the object returns.
Proxy and stub communicate by means of an RPC (remote procedure call)
channel,
which utilizes the system's RPC infrastructure for interprocess communication.
The RPC channel implements a single interface
IRpcChannelBuffer,
an
internal interface to which both interface proxies and stubs hold a pointer.
The proxy and stub call the interface to obtain a marshaling packet, send
the
data to their counterpart, and destroy the packet when they are done.
The
interface stub also holds a pointer to the original object.
For any given interface, the proxy and stub are both implemented as instances of the same class, which is listed for each interface in the system registry under the label ProxyStubClsid32 (or ProxyStubClsid on 16-bit systems). This entry maps the interface's IID to the CLSID of its proxy and stub objects. When COM needs to marshal an interface, it looks in the system registry to obtain the appropriate CLSID. The server identified by this CLSID implements both the interface proxy and interface stub.
Most often, the class to which this CLSID refers is automatically generated by a tool whose input is a description of the function signatures and semantics of a given interface, written in some interface description language. While using such a language is highly recommended and encouraged for accuracy's sake, doing so is by no means required. Proxies and stubs are merely Component Object Model components used by the RPC infrastructure and, as such, can be written in any manner desired so long as the correct external contracts are upheld. The programmer who designs a new interface is responsible for ensuring that all interface proxies and stubs that ever exist agree on the representation of their marshaled data.
When created, interface proxies are always aggregated into a larger
proxy,
which represents the object as a whole.
This object proxy also aggregates
COM's
generic proxy object, which is known as the
proxy manager.
The proxy
manager implements two interfaces:
IUnknown()
and
IMarshal.
All of the other interfaces that may be implemented on an object are exposed
in
its object proxy through the aggregation of individual interface proxies.
A
client holding a pointer to the object proxy ``believes'' it holds a pointer
to
the actual object.
A proxy representing the object as a whole is required in the client process so that a client can distinguish calls to the same interfaces implemented on entirely different objects. Such a requirement does not exist in the server process, however, where the object itself resides, because all interface stubs communicate only with the objects for which they were created. No other connection is possible.
Interface stubs, by contrast with interface proxies, are not aggregated, because there is no need that they appear to some external client to be part of a larger whole. When connected, an interface stub is given a pointer to the server object to which it should forward method invocations that it receives. Although it is useful to refer conceptually to a ``stub manager,'' meaning whatever pieces of code and state in the server-side RPC infrastructure that service the remoting of a given object, there is no direct requirement that the code and state take any particular, well-specified form.
The first time a client requests a pointer to an interface on a particular
object, COM loads an
IClassFactory()
stub in the server
process and
uses it to marshal the first pointer back to the client.
In the client process,
COM loads the generic proxy for the class factory object and calls its
implementation of
IMarshal()
to unmarshal that first pointer.
COM then
creates the first interface proxy and hands it a pointer to the RPC channel.
Finally, COM returns the
IClassFactory()
pointer to the
client, which uses
it to call
IClassFactory::CreateInstance()
, passing it
a reference
to the interface.
Back in the server process, COM now creates a new instance of the object, along with a stub for the requested interface. This stub marshals the interface pointer back to the client process, where another object proxy is created, this time for the object itself. Also created is a proxy for the requested interface, a pointer to which is returned to the client. With subsequent calls to other interfaces on the object, COM will load the appropriate interface stubs and proxies as needed.
When a new interface proxy is created, COM hands it a pointer to the
proxy
manager's implementation of
IUnknown()
, to which it delegates
all
QueryInterface()
calls.
Each interface proxy implements
two interfaces of
its own: the interface it represents and
IRpcProxyBuffer
.
The interface
proxy exposes its own interface directly to clients, which can obtain its
pointer by calling
QueryInterface()
on the proxy manager.
Only COM,
however, can call
IRpcProxyBuffer,
which it uses to connect
and
disconnect the proxy to the RPC channel.
A client cannot query an interface
proxy to obtain a pointer to the
IRpcProxyBuffer()
interface.
On the server side, each interface stub implements
IRpcStubBuffer()
, an
internal interface.
The server code acting as a stub manager calls
IRpcStubBuffer::Connect()
and passes the interface stub
the
IUnknown()
pointer of its object.
When an interface proxy receives a method invocation, it obtains a marshaling
packet from its RPC channel through a call to
IRpcChannelBuffer::GetBuffer()
.
The process of marshaling
the arguments
will copy data into the buffer.
When marshaling is complete, the interface
proxy invokes
IRpcChannelBuffer::SendReceive()
to send
the marshaled
packet to the corresponding interface stub.
When
IRpcChannelBuffer::SendReceive()
returns, the buffer into
which the
arguments were marshaled will have been replaced by a new buffer containing
the
return values marshaled from the interface stub.
The interface proxy unmarshals
the return values, invokes
IRpcChannelBuffer::FreeBuffer()
to free the
buffer, then returns the return values to the original caller of the method.
It is the implementation of
IRpcChannelBuffer::SendReceive()
that actually
sends the request to the server process and that knows how to identify the
server process and, within that process, the object to which the request should
be sent.
The channel implementation also knows how to forward the request
on to
the appropriate stub manager in that process.
The interface stub unmarshals
the
arguments from the provided buffer, invokes the indicated method on the server
object, and marshals the return values back into a new buffer, allocated by
a
call to
IRpcchannelBuffer::GetBuffer
.
The channel then
transmits the
return data packet back to the interface proxy, which is still in the middle
of
IRpcchannelBuffer::SendReceive
, which returns to the interface
proxy.
A particular instance of an interface proxy can be used to service more
than
one interface, so long as two conditions are met.
First, the IIDs of the
affected interfaces must be mapped to the the appropriate
ProxyStubClsid
in the system registry.
Second, the interface proxy must support calls to
QueryInterface()
from one supported interface to the other
interfaces, as
usual, as well as from
IUnknown()
and
IRpcProxyBuffer()
.
A single instance of an interface stub can also service more than one interface, but only if that set of interfaces has a strict single-inheritance relationship. This restriction exists because the stub can direct method invocations to multiple interfaces only where it knows in advance which methods are implemented on which interfaces.
Both proxies and stubs will at various times have need to allocate or
free
memory.
Interface proxies, for example, will need to allocate memory in which
to return out parameters to their caller.
In this respect, interface proxies
and interface stubs are just normal COM components, in that they should use
the
standard task allocator (see CoGetMalloc)
.
You should use COM's default implementation of
IMarshal()
except in
those very few cases where your application has special requirements that
COM's
default implementation does not address, or where you can achieve optimizations
over the marshaling code COM has provided.
For examples of such special cases,
see
IMarshal()
, ``When to Implement.''
IPSFactoryBuffer()
is the interface through which
proxies
and stubs are created.
It is used to create proxies and stubs that support
IRpcProxyBuffer()
and
IRpcStubBuffer()
respectively.
Each proxy / stub DLL must support
IPSFactory()
interface on the
class object accessible through its
DllGetClassObject()
entry point.
As was
described above, the registry is consulted under a key derived from the IID
to be remoted in order to learn the proxy/stub class that handles the remoting
of the indicated interface.
The class object for this class is retrieved,
asking
for this interface.
A proxy or a stub is then instantiated as appropriate.
interface IPSFactoryBuffer : IUnknown { HRESULT CreateProxy(pUnkOuter, iid, ppProxy, ppv); aHRESULT CreateStub(iid, pUnkServer, ppStub); };
- Create a new interface proxy object.
This function returns both
an
IPSFactoryBuffer::CreateProxy()
IRpcProxy()
instance and an instance of the interface which the proxy is being created
to
service in the first place.
The newly created proxy is initially in the
unconnected state.
HRESULT IPSFactoryBuffer::CreateProxy((
IUnknown * pUnkOuter,
REFIID iid,
IRpcProxyBuffer ** ppProxy,
void ** ppv
);
The controlling unknown of the aggregate in which the proxy is being created.
The interface id which the proxy is being created to service, and of which an instance should be returned through ppv.
On exit, contains the new
IRpcProxyBuffer()
instance.
On exit, contains an interface pointer of type indicated by iid.
S_OK
E_OUTOFMEMORY
E_NOINTERFACE
E_UNEXPECTED
- Create a new interface stub object.
The stub is created in the
connected state on the object indicated by
pUnkServer.
IPSFactoryBuffer::CreateStub()
HRESULT IPSFactoryBuffer::CreateStub((
REFIID iid,
IUnknown * pUnkServer,
IRpcStubBuffer ** ppStub
);
If
pUnkServer
is non-NULL
, then
before this function
returns the stub must verify (by using
QueryInterface()
)
that the server
object in fact supports the interface indicated by
iid.
If it does
not, then this function should fail with the error
E_NOINTERFACE
.
The interface that the stub is being created to service
The server object that is being remoted.
The stub should delegate
incoming calls (see
IRpcStubBuffer::Invoke()
) to the appropriate
interface on this object.
pUnkServer
may legally be
NULL
, in which case the caller is responsible for later calling
IRpcStubBuffer::Connect()
before using
IRpcStubBuffer::Invoke()
.
The place at which the newly create stub is to be returned.
S_OK
E_OUTOFMEMORY
E_NOINTERFACE
E_UNEXPECTED
IRpcChannelBuffer()
is the interface through which
interface proxies send calls through to the corresponding interface stub.
This interface is implemented by the RPC infrastructure.
The infrastructure
provides an instance of this interface to interface proxies in
IRpcProxyBuffer::Connect()
.
The interface proxies hold on to this
instance and use it each time they receive an incoming call.
interface IRpcChannelBuffer : IUnknown { HRESULT GetBuffer(pMessage, riid); HRESULT SendReceive(pMessage, pStatus); HRESULT FreeBuffer(pMessage); HRESULT GetDestCtx(pdwDestCtx, ppvDestCtx); HRESULT IsConnected(); };
[Footnote 50]
Common to several of the methods in
IRpcChannelBuffer()
is a
data structure of type
RPCOLEMESSAGE
.
This structure is
defined as is show below.
The structure is to be packed so that there are no holes in its memory layout.
typedef struct RPCOLEMESSAGE { void * reserved1; RPCOLEDATAREP dataRepresentation; // in NDR transfer syntax: info about endianness, etc. void * pvBuffer; // memory buffer used for marshalling ULONG cbBuffer; // size of the marshalling buffer aULONG iMethod; // the method number being invoked void * reserved2[5]; ULONG rpcFlags; } on the ultimate destination machine MESSAGE; [Footnote 50]
The most significant member of this structure is
pvBuffer
.
It is through the memory
buffer to which
pvBuffer
points that marshaled method arguments
are
transferred.
cbBuffer
is used to indicate the size of the
buffer.
iMethod
is
indicates a particular method number within the interface being invoked.
The
IID of that interface is identified through other means: on the client side
as
a parameter to
GetBuffer
, and on the server side as part
of the internal
state of each interface stub.
At all times all reserved values in this structure are to be initialized
to
zero by non-RPC-infrastructure parties (i.e.: parties other than the channel
/
RPC runtime implementor) who allocate
RPCOLEMESSAGE
structures.
However, the
RPC channel (more generally, the RPC runtime infrastructure) is free to modify
these reserved fields.
Therefore, once initialized, the reserved fields must
be
ignored by the initializing code; they cannot be relied on to remain as zero.
Further, there are very carefully specified rules as to what values in these
structures may or may not be modified at various times and by which parties.
In
almost all cases, aside from actually reading and writing data from the
marshaling buffer, which is done by proxies and stubs, only the channel may
change these fields.
See the individual method descriptions for details.
Readers familiar with the connection-oriented DCE protocol may notice that the ``transfer syntax'' used for marshaling the arguments, the particular set of rules and conventions according to which data is marshaled, is not explicitly called out. Architecturally speaking, it is only the interface proxy for a given interface and its corresponding interface stub that cares at all about what set of marshaling rules is in fact used. However, in the general case these interface proxies and stubs may be installed on different machines with a network in the middle, be written by different development organizations on different operating systems, etc. Accordingly, in cases where the author of an interface proxy for a given IID cannot guarantee that all copies of the corresponding interface stub are in fact always revised and updated in synchrony with his interface proxy, a well-defined convention should be used for the transfer syntax. Indeed, formal transfer syntax standards exist for this purpose. The one most commonly used is known as ``Network Data Representation'' (NDR), originally developed by Apollo Corporation and subsequently enhanced and adopted by The Open Group as part of their Distributed Computing Environment (DCE).
When NDR transfer syntax is used (and whether it
is
in use or not is
implicitly known by the proxy or stub), the member
dataRepresentation
provides
further information about the rules by which data in the buffer is marshaled.
NDR is a ``multi-canonical'' standard, meaning that rather than adopting one
standard for things like byte-order, character set, etc., multiple standards
(a
fixed set of them) are accommodated.
Specifically, this is accommodated by
a
``reader make right'' policy: the writer / marshaler of the data is free to
write
the data in any of the supported variations and the reader / unmarshaler is
expected to be able to read any of them.
The particular data type in use is
conveyed in an
RPCOLEDATAREP
structure, which is defined
as follows.
Note that
this structure, too, is packed; the size of the entire structure is exactly
four bytes.
The actual layout of the structure in all cases always corresponds
to the data representation value as defined in the DCE standard; the particular
structure shown here is equivalent to that layout in Microsoft's and other
common compilers.
typedef RPCOLEDATAREP { UINT uCharacterRep : 4; // least signficant nibble of first byte UINT uByteOrder : 4; // most signficant nibble of first byte BYTE uFloatRep; BYTE uReserved; BYTE uReserved2; } RPCOLEDATAREP;
The values which may legally be found in these fields are as shown in Table . Further information on the interpretation of this field can be found in the NDR Transfer Syntax standards documentation.
Field Name
|
Meaning of Field
|
Value in field
|
Interpretation
|
uCharacterRep |
determines interpretation of single-byte-character valued and single-byte-string valued entities | 0 1 | ASCII EBCDIC |
uByteOrder |
integer and floating point byte order | 0 1 | Big-endian (Motorola) Little-endian (Intel) |
uFloatRep |
representation of floating point numbers | 0 1 2 3 | IEEE VAX Cray IBM |
- This method returns a buffer into which data can be marshaled
for subsequent
transmission over the wire.
It is used both by interface proxies and by interface
stubs, the former to marshal the incoming arguments for transmission to the
server,
and the latter to marshal the return values back to the client.
IRpcChannelBuffer::GetBuffer()
HRESULT IRpcChannelBuffer::GetBuffer((
RPCOLEMESSAGE * pMessage,
REFIID iid
);
[Footnote 51]
Upon receipt of an incoming call from the client of the proxy object, interface
proxies use
GetBuffer
to get a buffer into which they can
marshaling the
incoming arguments.
A new buffer must be obtained for every call operation;
old
buffers cannot be reused by the interface proxy.
The proxy needs to ask for
and
correctly manage a new buffer even if he himself does not have arguments to
marshal (i.e.: a void argument list).
[Footnote 51]
Having marshaled the arguments, the interface proxy then calls
SendReceive
to actually invoke the operation.
Upon return from
SendReceive
,
the
buffer no longer contains the marshaled arguments but instead contains the
marshaled
return values (and out parameter values).
The interface proxy unmarshals these
values, calls
FreeBuffer
to free the buffer, then returns
to
its calling client.
On the server side (in interface stubs), the sequence is somewhat different.
The server side will not be explored further here; see instead the description
of
IRpcStubBuffer::Invoke()
for details.
On the client side, the
RPCOLEMESSAGE
structure argument
to
GetBuffer
has
been allocated and initialized by the caller (or by some other party on the
caller's behalf).
Interface proxies are to initialize the members of this
structure as follows.
Member Name
|
Value to initalize to
|
reserved members | as always, reserved values must be initialized to zero / NULL. |
pvBuffer | must be NULL. |
cbBuffer | the size in bytes that the channel should allocate for the buffer; that is, the maximum size in bytes needed to marshal the arguments. The interface proxy will have determined this information by considering the function signature and the particular argument values passed in. It is explicitly legal to have this value be zero, indicating that that the caller does not himself require a memory buffer. |
iMethod | the zero-based method number in the interface iid which is being invoked |
dataRepresentation | if NDR transfer syntax is being used, then this indicates the byte order, etc., by which the caller will marshal data into the returned buffer. |
rpcFlags | The caller should pass zero (this structure is handled internally). |
[Footnote 52]
If the
GetBuffer
function is successful, then upon function
exit
pvBuffer
will
have been changed by the channel to point to a memory buffer of (at least)
cbBuffer
bytes in size into which the method arguments
can now be marshaled (if
cbBuffer
was zero,
pvBuffer
may or may
not be
NULL
).
The reserved fields in the
RPCOLEMESSAGE
structure may or may not have been changed
by the channel.
However, neither the
cbBuffer
nor
iMethod
fields of
RPCOLEMESSAGE
will have been changed; the channel treats
these as read-only.
[Footnote 52]
Furthermore, until such time
as the now-allocated memory buffer is subsequently freed (see
SendReceive
and
FreeBuffer
), no party other than the channel
may modify any of the data accessible from
pMessage
with
the lone exceptions of the data pointed to by
pvBuffer
and the member
cbBuffer
, which may be modified only in
limited ways; see below.
A message structure initialized as discussed above.
The interface identifier of the interface being invoked.
S_OK
E_OUTOFMEMORY
E_UNEXPECTED
- Cause an invocation to be sent across to the server process.
The
caller will have first obtained access to a transmission packet in which to
marshal the arguments by calling
IRpcChannelBuffer::SendReceive()
IRpcChannelBuffer::GetBuffer()
.
The same
pMessage
structure passed as an argument
into that function is passed here to the channel a second time.
HRESULT IRpcChannelBuffer::SendReceive((
RPCOLEMESSAGE * pMessage,
ULONG * pStatus
);
In the intervening time period, the method arguments will have been
marshaled into the buffer pointed to by
pMessage
->pvBuffer
.
However, the pvBuffer pointer parameter must on entry
to
SendReceive
be exactly as it was when returned from
GetBuffer
.
That is, it must point to the start of the memory buffer.
The caller should in addition set
pMessage
->cbBuffer
to the number of bytes actually written into the buffer (zero is
explicitly a legal value).
No other values accessible from
pMessage
may be different than they were on exit from
GetBuffer
.
Upon
successful
exit from
SendReceive
, the incoming buffer pointed to by
pvBuffer
will have been freed by the channel.
In its place will be found a buffer containing
the marshaled return values / out parameters from the interface stub:
pMessage
->pvBuffer
points to the new buffer,
and
pMessage
->cbBuffer
indicates the
size thereof.
If there are no such return values, then
pMessage
->cbBuffer
is set to zero, while
pMessage
->pvBuffer
may or may not be
NULL
.
[Footnote 53]
On
error
exit from
SendReceive
,
[Footnote 53]
the incoming buffer pointed to by
pvBuffer
may or may not have been freed.
If it has been freed, then
on error exit
pMessage
->pvBuffer
is
set to
NULL
and
pMessage
->cbBuffer
is set to zero.
If in contrast,
pMessage
->pvBuffer
is on error exit not
NULL
, then that
pointer, the data to which it points, and the value
pMessage
->cbBuffer
will contain exactly as they did on entry; that is, the
marshaled arguments will not have been touched.
Thus, on error exit from
SendReceive
, in no case are any marshaled
return values
passed back; if a marshaling buffer is in fact returned, then
it contains the marshaled
arguments
as they were on entry.
The exact cases on error exit when the incoming buffer has or has not been freed needs careful attention. There are three cases:
The channel implementation knows with certainty either that
all of the incoming data was successfully unmarshaled or that if any errors
occurred during unmarshaling that the interface stub correctly cleaned up.
In practical terms, this condition is equivalent to the stub manager having
actually called
IRpcStubBuffer::Invoke()
on the appropriate
interface stub.
In this case, on exit from
SendReceive
the incoming
arguments will
always
have been freed.
The channel implementation knows with certainty the situation in case 1) has not occurred.
In this case, on exit from
SendReceive
, the incoming
arguments will
never
have been freed.
The channel implementation does not know with certainty that either of the above two cases has occurred.
In this case, on exit from
SendReceive
, the incoming
arguments will
always
have been freed.
This is a possible
resource leakage (due to, for example,
CoReleaseMarshalData()
calls that never get made), but it safely avoids freeing resources that should
not be freed.
If
pMessage
->pvBuffer
is returned
as non-NULL
, then the caller is responsible for subsequently
freeing it; see
FreeBuffer
.
A returned non-NULL
pMessage
->pvBuffer
may in
general legally be (and will commonly be, the success case) different than
the (non-NULL
) value on entry; i.e.: the buffer may be
legally be reallocated.
Further, between the return from
SendReceive
and the subsequent freeing call no data accessible from
pMessage
may be modified, with the possible exception of the data
actually in the memory buffer.
Upon successful exit from
SendReceive
, the
pMessage
->dataRepresentation
field will have
been modified to contain whatever was returned by the interface stub in field
of the same name value on exit to
IRpcStubBuffer::Invoke()
.
This is particularly important when NDR transfer syntax is used, as
dataRepresentation
indicates critical things (such as byte order)
which apply to the marshaled return / out values.
Upon error exit from
SendReceive
,
pMessage
->dataRepresentation
is undefined.
Message structure containing info to transmit to server.
May legally be NULL.
If non-NULL, then if either 1) an RPC-infrastructure-detected
server-object fault (e.g.: a server object bug caused an exception which was
caught by the RPC infrastructure) or 2) an RPC communications failure occurs,
then at this location a status code is written which describes what happened.
In the two error cases, the errors
E_RPCFAULT
and
E_RPCSTATUS
are (respectively) returned (and are always returned
when these errors occur, irrespective of the
NULL
-ness
of
pStatus
).
S_OK
E_RPCFAULT
E_RPCSTATUS
- Free a memory buffer in
pMessage->pvBuffer
that was previously allocated by the channel.
IRpcChannelBuffer::FreeBuffer()
HRESULT IRpcChannelBuffer::FreeBuffer((
RPCOLEMESSAGE * pMessage
);
At various times the RPC channel allocates a memory buffer and returns
control of same to a calling client.
Both
GetBuffer
and
SendReceive
do so, for example.
FreeBuffer
is
the means by which said calling client informs the channel that it is done
with the buffer.
On function entry, the buffer which is to be freed is
pMessage->pvBuffer, which explicitly
may or may not be
NULL
.
If
pMessage->pvBuffer
is non-NULL, then
FreeBuffer
frees
the buffer, NULLs the pointer, and returns
NOERROR
; if
pMessage->pvBuffer
is
NULL
, then
FreeBuffer
simply returns
NOERROR
(i.e.: passing
NULL
is
not
an error).
Thus, on function exit,
pMessage->pvBuffer
is always
NULL
.
Note that
pMessage->cbBuffer
is never looked
at or changed.
There are strict rules as to what data accessible from
pMessage
may have been modified in the intervening time between
the time the buffer was allocated and the call to
FreeBuffer
.
In short, very little modification is permitted; see above and below for precise
details.
Pointer to structure containing pointer to buffer to free.
S_OK
E_UNEXPECTED
- Return the destination context for this RPC channel.
The destination
context here is as specified in the description of the
IRpcChannelBuffer::GetDestCtx()
IMarshal()
interface.
HRESULT IRpcChannelBuffer::GetDestCtx((
DWORD * pdwDestCtx,
void ** ppvDestCtx
);
the place at which the destination context is to be returned.
May be
NULL
.
If non-NULL
,
then this is the place at which auxiliary information associated with certain
destination contexts will be returned.
Interface proxies may not hold on to
this returned pointer in their internal state; rather, they must assume that
a subsequent call to
IRpcChannel::Call
may in fact invalidate
a previously returned destination context.
[Footnote 54]
S_OK
E_OUTOFMEMORY
E_UNEXPECTED
- Answers as to whether the RPC channel is still connected to the
other side.
A negative reply is definitive: the connection to server end has
definitely been terminated.
A positive reply is tentative: the server end
may or may not be still up.
Interface proxies can if they wish use this method
as an optimization by which they can quickly return an error condition.
IRpcChannelBuffer::IsConnected()
HRESULT IRpcChannelBuffer::IsConnected((
);
S_OK
S_FALSE
No error values may be returned.
IRpcProxyBuffer()
interface is the interface by which
the client-side infrastructure (i.e.
the proxy manager) talks to the interface
proxy instances that it manages.
When created, proxies are aggregated into
some larger object as per the normal creation process (where
pUnkOuter
in
IPSFactoryBuffer::CreateProxy
is non-NULL).
The controlling unknown will then
QueryInterface
to the interface that it wishes to expose from the interface proxy.
interface IRpcProxyBuffer : IUnknown { virtual HRESULT Connect(pRpcChannelBuffer) = 0; virtual void Disconnect() = 0; };
- Connect the interface proxy to the indicated RPC channel.
The
proxy should hold on to the channel,
IRpcProxyBuffer::Connect()
AddRef
ing it as per
the usual rules.
If the proxy is currently connected, then this call fails
(with
E_UNEXPECTED
); call
Disconnect
first if in doubt.
HRESULT IRpcProxyBuffer::Connect((
IRpcChannelBuffer * pRpcChannelBuffer
);
The RPC channel that the interface proxy is to use to effect
invocations to the server object.
May not be
NULL
.
S_OK
E_OUTOFMEMORY
E_NOINTERFACE
E_UNEXPECTED
- Informs the proxy that it should disconnect itself from any RPC
channel that it may currently be holding on to.
This will involve
IRpcProxyBuffer::Disconnect()
Release
ing the
IRpcChannel
pointer to counteract
the
AddRef
done in
IRpcProxy::Connect
.
Note that this function does not return a value.
void IRpcProxyBuffer::Disconnect((
);
IRpcStubBuffer()
is the interface used on the server
side by the RPC runtime infrastructure (herein referred to loosely as the
``channel'') to communicate with interface stubs that it dynamically loads
into a server process.
interface IRpcStubBuffer : IUnknown { virtual HRESULT Connect(pUnkServer) = 0; virtual void Disconnect() = 0; virtual HRESULT Invoke(pMessage, pChannel) = 0; virtual IRpcStubBuffer* IsIIDSupported(iid) = 0; virtual ULONG CountRefs() = 0; virtual HRESULT DebugServerQueryInterface(ppv) = 0; virtual void DebugServerRelease(pv) = 0; };
- Informs the interface stub of server object to which it is now
to be connected, and to which it should forward all subsequent
IRpcStubBuffer::Connect()
Invoke
operations.
The stub will have to
QueryInterface
on
pUnkServer
to obtain access to appropriate interfaces.
The stub will of course follow the normal
AddRef
rules
when it stores pointers to the server object in its internal state.
HRESULT IRpcStubBuffer::Connect((
IUnknown * pUnkServer
);
If the stub is currently connected, then this call fails with
E_UNEXPECTED
.
The new server object to which this stub is now to be connected.
S_OK
E_OUTOFMEMORY
E_NOINTERFACE
E_UNEXPECTED
- Informs the stub that it should disconnect itself from any server
object that it may currently be holding on to.
Notice that this function does
not return a value.
IRpcStubBuffer::Disconnect()
void IRpcStubBuffer::Disconnect((
);
- Invoke the
pMessage->iMethodth method
in the server object interface instance to which this interface stub is currently
connected.
The RPC runtime infrastructure (the ``channel'') calls this method
on the appropriate interface stub upon receipt of an incoming request from
some remote client.
IRpcStubBuffer::Invoke()
HRESULT IRpcStubBuffer::Invoke((
RPCOLEMESSAGE * pMessage,
IRpcChannelBuffer * pChannel
);
On entry, the members of
pMessage
are set as follows:
Member Name
|
Value on entry to Invoke
|
reserved members | indeterminate. These members are neither to be read nor to be changed by the stub. |
pvBuffer | points to a buffer which contains the marshaled incoming arguments. In the case that there are no such arguments (i.e.: cbBuffer == 0), pvBuffer may be NULL, but will not necessarily be so. |
cbBuffer | the size in bytes of the memory buffer to which pvBuffer points. If pvBuffer is NULL, then cbBuffer will be zero (but the converse is not necessarily true, as was mentioned in pvBuffer). |
iMethod | the zero-based method number in the interface which is being invoked |
dataRepresentation | if NDR transfer syntax is being used, then this indicates the byte order, etc., according to which the data in pvBuffer has been marshaled. |
rpcFlags | indeterminate. Neither to be read nor to be changed by the stub. |
The stub is to do the following:
Unmarshal the incoming arguments,
Invoke the designated operation in the server object,
Ask the channel to allocate a new buffer for the return values and out values,
Marshal the return values and out values into the buffer, then
Return successfully (i.e.:
NOERROR
) from
Invoke.
[Footnote 55]
Errors may of course occur at various places in this process.
[Footnote 55]Such errors will cause the stub to return
an error from Invoke rather than
NOERROR
.
In cases where
such an error code is returned, it is the stub's responsibility to have cleaned
up any data and other resources allocated by the unmarshaling and marshaling
processes or returned as out values from the server object.
However, the stub
is
not
responsible for invoking
FreeBuffer
to free the actual marshaling buffer (i.e.: it is illegal for the stub to
do so); rather, on error return from Invoke the caller of Invoke will ignore
pvBuffer, and will also free it if non-NULL
.
Having made that general statement as to the exit conditions of Invoke, let
us examine its operation in greater detail.
If the stub cannot deal with the indicated
dataRepresentation, it is to return
RPC_E_SERVER_INVALIDDATAREP
.
If it understands the data representation, the stub is to then unmarshal the
arguments from the buffer provided in
pMessage->pvBuffer, the size of which is passed in
pMessage->cbBuffer.
If the argument data
cannot be completely unmarshaled, the server is to free any partially unmarshaled
data, then return
RPC_E_SERVER_CANTUNMARSHALDATA
from Invoke.
If the data is successfully completely unmarshaled, then the interface stub is to invoke the designated method in the designated interface on the server object. Note that the incoming pvBuffer memory buffer is at this time still valid, and that therefore the stub may if it wishes and if appropriate for the argument and data representations in question pass to the server object pointers which point directly into this buffer. The memory allocation and data copying that is thus avoided can at times be a significant performance optimization.
[Footnote 56]
[Footnote 57]
[Footnote 58]
Once the invocation of the server object returns, the stub is
to marshal the return value and out parameters returned from the server back
to the client.
It does so irrespective of whether the server object invocation
returned an error or success code; that is, the stub marshals back to the
client whatever the server object returned.
[Footnote 56]
The stub gets a reply buffer into which to do this marshaling by calling
pChannel->GetBuffer, passing in the
pMessage
structure that it received in Invoke.
Before calling
GetBuffer
, the stub is to set the
cbBuffer
member to the size that it requires for the to-be-allocated reply buffer.
Zero is explicitly a legal value for
cbBuffer,
and the stub must
always
call
GetBuffer
(more precisely, to be clear about the error case: the stub must always call
GetBuffer
if the server object method has actually been invoked)
[Footnote 57]
to allocate a reply buffer, even if the
stub itself does not require one (such as would be the case if for a void-returning
function with no out parameters).
The stub must also set
dataRepresentation
as appropriate for the standard by which it intends to marshal
the returning values (or would marshal them if there were some).
[Footnote 58]
Aside from
cbBuffer,
dataRepresentation
and possibly the contents of the bytes inside
the memory buffer, on entry to
GetBuffer
no other data
accessible from
pMessage
may be different than
they were on entry to Invoke.
Before it allocates a reply buffer, the call to
GetBuffer
has the side effect of freeing the memory buffer to which
pvBuffer
presently points.
Thus, the act by the interface stub of allocating
a reply buffer for the return values necessarily terminates access by the
stub to the incoming marshaled arguments.
If
GetBuffer
successfully allocates a reply buffer
(see GetBuffer for a description of how the stub determines this), then the
stub is to marshal the return value and returned out parameters into the buffer
according to the rules of the transfer syntax.
Once this is complete, the
stub is to set the
cbBuffer
member to the number
of bytes it actually marshaled (if it marshaled nothing, then it must explicitly
set this to zero (but see also
GetBuffer
)), and then return
NOERROR
from Invoke.
If an error occurs during the unmarshaling of the incoming arguments
or the marshaling of the return values, then the interface stub is responsible
for correctly freeing any resources consumed by the marshaled data.
See in
particular CoReleaseMarshalData.
See also the discussion of this topic in
IRpcChannelBuffer::SendRecieve
.
Channel-allocated message structure.
The channel to use for buffer management, etc.
S_OK
- Answer whether this stub is designed to handle the unmarshaling
of the indicated interface.
IRpcStubBuffer::IsIIDSupported()
IRpcStubBuffer* IRpcStubBuffer::IsIIDSupported((
REFIID iid
);
If the stub buffer supports the specified IID, then it should return
an appropriate
IRpcStubBuffer*
for that interface.
Otherwise,
it should return
NULL
.
When presented with the need to remote a new IID on a given object, the RPC runtime typically calls this function on all the presently-connected interface stubs in an attempt to locate one that can handle the marshaling for the request before it goes to the trouble of creating a new stub.
As in IPSFactoryBuffer::CreateStub
, if this stub
is presently connected to a server object, then not only must this function
verify that the
stub
can handle the requested interface
id, but it must also verify (using QueryInterface) that the connected server
object in fact supports the indicated interface (depending on the IID and
previous interface servicing requests, it may have already done so).
A common special case is the following: interface stubs which are designed to only support one interface id (as most are designed to do) can simply check if iid designates the one interface that they handle. If not, return FALSE. Otherwise, then if connected check that the server object supports the interface. Otherwise return TRUE.
The interface that the caller wishes to know if the stub can
handle.
iid
is never to be
IID_IUnknown
.
- Return the total number of references that this stub interface
instance has on the server object.
IRpcStubBuffer::CountRefs()
ULONG IRpcStub::CountRefs(
);
integer value
The number of such references.
- This function exists in order to facilitate the support of debuggers
which wish to provide transparency when single-stepping, etc., across remote
invocations on objects.
As such, the semantics of this function are a little
strange in order to avoid the unnecessarily disturbing the state of the actual
server object.
IRpcStubBuffer::DebugServerQueryInterface()
HRESULT IRpcStubBuffer::DebugServerQueryInterface((
void ** ppv
);
If the stub is not presently connected then set
*ppv
to
NULL
(per the usual error-case convention) and return
E_UNEXPECTED
.
If connected but this stub does not support the indicated
interface (in the sense expressed in
IsIIDSupported
), then
(set
*ppv
to
NULL
and) return
E_NOINTERFACE
instead.
Otherwise, return the interface pointer on the connected server object
which would be used by an immediate subsequent invocation of
Invoke
on this interface stub
DebugServerQueryInterface
is analogous to invoking QueryInterface on the server itself with the important
difference that the caller will later call
DebugServerRelease
to indicate that he is done with the pointer instead of releasing the returned
pointer himself.
It is required that
DebugServerRelease
be called before the interface stub itself is destroyed or, in fact, before
it is disconnected.
In the vast majority of interface stub implementations,
DebugServerQueryInterface
can therefore be implemented simply by returning an internal state
variable inside the interface stub itself without doing an AddRef on the server
or otherwise running any code in the actual server object.
In such implementations,
DebugServerRelease
will be a completely empty no-op.
The other rational
implementation is one where
DebugServerQueryInterface
does
a QueryInterface on the server object and
DebugServerRelease
does a corresponding Release, but as this actually runs server code, the former
implementation is highly preferred if at all achievable.
The place at which the interface pointer is to be returned.
S_OK
E_NOINTERFACE
E_UNEXPECTED
- Indicate that an interface pointer returned previously from
IRpcStubBuffer::DebugServerRelease()
DebugServerQueryInterface
is no longer needed by the caller.
In
most implementations,
DebugServerRelease
is a completely
empty no-op; see the description of
DebugServerQueryInterface
for details.
void IRpcStubBuffer::DebugServerRelease((
pv
);
The
IStdMarshalInfo()
interface returns the CLSID
identifying the handler to be used in the destination process during standard
marshaling.
An object that uses COM's default implementation of
IMarshal()
does not provide its own proxy but, by implementing
IStdMarshalInfo()
, can nevertheless specify a handler to be loaded
in the client process.
Such a handler would typically handle certain requests
in-process and use OLE's default marshaling to delegate others back to the
original object.
To create an instance of an object in some client process, COM must
first determine whether the object uses default marshaling or its own implementation.
If the object uses default marshaling, COM then queries the object to determine
whether it uses a special handler or, simply, COM's default proxy.
To get
the CLSID of the handler to be loaded, COM queries the object for the
IStdMarshalInfo()
interface and then the
IPersist()
interface.
If neither interface is supported, a standard handler is used.
If you are writing a server application that supports class emulation
(that is, if your server can manipulate objects of another type in response
to the Activate As option in the Convert dialog box), you must implement the
IStdMarshalInfo()
interface in order to return the CLSID of the handler
to be used for the object.
Note that your handler must aggregate the default handler.
You typically don't call this interface yourself. COM queries for this interface when performing standard marshaling.
IUnknown Methods
|
Description
|
QueryInterface()
|
Returns pointers to supported interfaces. |
AddRef()
|
Increments reference count. |
Release()
|
Decrements reference count. |
|
Description
|
GetClassForHandler
|
Obtains the class identifier of the object handler in the destination process. |
- Retrieves the CLSID of the object handler to be used in the destination
process during standard marshaling.
IStdMarshalInfo::GetClassForHandler()
HRESULT GetClassForHandler(
#include <objidl.h>
DWORD dwDestContext,
void * pvDestContext,
CLSID * pClsid
);
[in] Destination context, that is, the process in which the
unmarshaling will be done.
The legal values for
dwDestContext
are taken from the enumeration
MSHCTX
.
For information
on the
MSHCTX
enumeration, see the ``Data Structures''
section.
[in] Reserved for future use; must be
NULL
.
[out] Pointer to the handler's CLSID.
This method supports the standard return values
E_INVALIDARG
,
E_OUTOFMEMORY
, and
E_UNEXPECTED
,
as well as the following:
S_OK
The CLSID was retrieved successfully.
- Returns an upper bound on the number of bytes needed to marshal the specified
interface pointer to the specified object.
CoGetMarshalSizeMax()
STDAPI CoGetMarshalSizeMax((
#include <objbase.h>
ULONG * pulSize,
REFIID riid,
IUnknown * pUnk,
DWORD dwDestContext,
LPVOID pvDestContext,
DWORD mshlflags
);
This function performs the following tasks:
Queries the object for an
IMarshal()
pointer
or, if the object does not implement
IMarshal()
, gets a
pointer to COM's standard marshaler.
Using whichever pointer is obtained in the preceding step,
calls
IMarshal::GetMarshalSizeMax()
.
Adds to the value returned by the call to
GetMarshalSizeMax
the size of the marshaling data header and, possibly, that of the
proxy CLSID to obtain the maximum size in bytes of the amount of data to be
written to the marshaling stream.
You do not explicitly call this function unless you are implementing
IMarshal()
, in which case your marshaling stub should call this function
to get the correct size of the data packet to be marshaled.
The value returned by this method is guaranteed to be valid only as long as the internal state of the object being marshaled does not change. Therefore, the actual marshaling should be done immediately after this function returns, or the stub runs the risk that the object, because of some change in state, might require more memory to marshal than it originally indicated.
[out] Pointer to the upper-bound value on the size, in bytes, of the data packet to be written to the marshaling stream; a value of zero means that the size of the packet is unknown.
[in] Reference to the identifier of the interface whose pointer
is to be marshaled.
This interface must be derived from the
IUnknown()
interface.
[in] Pointer to the interface to be marshaled.
This interface
must be derived from the
IUnknown()
interface.
[in] Destination context where the specified interface is
to be unmarshaled.
Values for
dwDestContext
come from
the enumeration
MSHCTX
.
[in] Reserved for future use; must be
NULL
.
[in] Flag indicating whether the data to be marshaled is to
be transmitted back to the client process the normal case or written to a
global table, where it can be retrieved by multiple clients.
Values come from
the enumeration
MSHLFLAGS
.
This function supports the standard return value
E_UNEXPECTED
, as well as the following:
S_OK
The upper bound was returned successfully.
CO_E_NOTINITIALIZED
The
CoInitialize()
function was not called
on the current thread before this function was called.
CoMarshalInterface()
,
IMarshal::GetMarshalSizeMax()
- Creates a default, or standard, marshaling object in either the client process
or the server process, depending on the caller, and returns a pointer to that
object's
CoGetStandardMarshal()
IMarshal()
implementation.
STDAPI CoGetStandardMarshal((
#include <objbase.h>
REFIID riid,
IUnknown * pUnk,
DWORD dwDestContext,
LPVOID pvDestContext,
DWORD mshlflags,
LPMARSHAL * ppMarshal
);
The
CoGetStandardMarshal()
function creates a default,
or standard, marshaling object in either the client process or the server
process, as may be necessary, and returns that object's
IMarshal()
pointer to the caller.
If you implement
IMarshal()
,
you may want your implementation to call
CoGetStandardMarshal()
as a way of delegating to COM's default implementation any destination contexts
that you don't fully understand or want to handle.
Otherwise, you can ignore
this function, which COM calls as part of its internal marshaling procedures.
When the COM library in the client process receives a marshaled interface
pointer, it looks for a CLSID to be used in creating a proxy for the purposes
of unmarshaling the packet.
If the packet does not contain a CLSID for the
proxy, COM calls
CoGetStandardMarshal,
passing a
NULL
pUnk
value.
This function creates a standard
proxy in the client process and returns a pointer to that proxy's implementation
of
IMarshal()
.
COM uses this pointer to call
CoUnmarshalInterface()
to retrieve the pointer to the requested interface.
If your COM server application's implementation of
IMarshal()
calls
CoGetStandardMarshal()
, you should pass
both the IID of (riid), and a pointer to (pUnk), the interface being requested.
This function performs the following tasks:
Determines whether
pUnk
is
NULL
.
If
pUnk
is
NULL
, creates
a standard interface proxy in the client process for the specified
riid
and returns the proxy's
IMarshal()
pointer.
If
pUnk
is not
NULL
,
checks to see if a marshaler for the object already exists, creates a new
one if necessary, and returns the marshaler's
IMarshal()
pointer.
[in] Reference to the identifier of the interface whose pointer
is to be marshaled.
This interface must be derived from the
IUnknown()
interface.
[in] Pointer to the interface to be marshaled.
[in] Destination context where the specified interface is
to be unmarshaled.
Values for
dwDestContext
come from
the enumeration
MSHCTX
.
Currently, unmarshaling can occur
either in another apartment of the current process (MSHCTX_INPROC
) or in another process on the same computer as the current process
(MSHCTX_LOCAL
).
[in] Reserved for future use; must be
NULL
.
[in] Flag indicating whether the data to be marshaled is to
be transmitted back to the client process the normal case or written to a
global table, where it can be retrieved by multiple clients.
Valid values
come from the
MSHLFLAGS
enumeration.
[out] Indirect address of
IMarshal*
pointer
variable that receives the interface pointer to the standard marshaler.
This function supports the standard return values
E_FAIL
,
E_OUTOFMEMORY
and
E_UNEXPECTED
, as well as the
following:
S_OK
The
IMarshal()
instance was returned successfully.
CO_E_NOTINITIALIZED
The
CoInitialize()
or
OleInitialize
function was not called on the current thread before this function
was called.
- This function returns the CLSID of the DLL that implements the proxy and stub
for the specified interface
CoGetPSClsid()
WINOLEAPI CoGetPSClsid((
#include <objbase.h>
REFIID riid,
CLSID * pclsid
);
The
CoGetPSClsid()
function looks at the
HKEY_CLASSES_ROOT
\Interfaces\{ string form of
riid
}\ProxyStubClsid32
key in the registry to determine the
CLSID of the DLL to load in order to create the proxy and stub for the interface
specified by
riid.
This function also returns the CLSID
for any interface IID registered by
CoRegisterPSClsid()
within the current process.
[in] The interface whose proxy/stub CLSID is to be returned.
[out] Where to store the proxy/stub CLSID for the interface specified by riid.
S_OK
The proxy/stub CLSID was successfully returned.
E_INVALIDARG
One of the parameters is invalid.
E_OUTOFMEMORY
There is insufficient memory to complete this operation.
- Marshals an
CoMarshalHresult()
HRESULT
to the specified stream, from which
it can be unmarshaled using the
CoUnmarshalHresult
function.
STDAPI CoMarshalHresult(
#include <objbase.h>
IStream * pStm,
HRESULT hresult
);
An
HRESULT
is process-specific, so an
HRESULT
that is valid in one process might not be valid in another.
If
you are writing your own implementation of
IMarshal()
and
need to marshal an
HRESULT
from one process to another,
either as a parameter or a return code, you must call this function.
In other
circumstances, you will have no need to call this function.
This function perfoms the following tasks:
Writes an
HRESULT
to a stream.
Returns an
IStream()
pointer to that stream.
[in] Pointer to the marshaling stream.
[in]
HRESULT
in the originating process.
This function supports the standard return values
E_OUTOFMEMORY
and
E_UNEXPECTED
, as well as the following:
S_OK
The
HRESULT
was marshaled successfully.
STG_E_INVALIDPOINTER
Bad pointer passed in for pStm.
STG_E_MEDIUMFULL
The medium is full.
- Writes into a stream the data required to initialize a proxy object in some
client process.
The COM library in the client process calls the
CoMarshalInterface()
CoUnmarshalInterface()
function to extract the data and initialize
the proxy.
CoMarshalInterface()
can marshal only interfaces
derived from
IUnknown()
.
STDAPI CoMarshalInterface((
#include <objbase.h>
IStream * pStm,
REFIID riid,
IUnknown * pUnk,
DWORD dwDestContext,
void * pvDestContext,
DWORD mshlflags
);
The
CoMarshalInterface()
function marshals the interface
referred to by
riid
on the object whose
IUnknown()
implementation is pointed to by
pUnk.
To
do so, the
CoMarshalInterface()
function performs the following
tasks:
Queries the object for a pointer to the
IMarshal()
interface.
If the object does not implement
IMarshal()
, meaning that it relies on COM to provide marshaling support,
CoMarshalInterface()
gets a pointer to COM's default implementation
of
IMarshal()
.
Gets the CLSID of the object's proxy by calling
IMarshal::GetUnmarshalClass()
, using whichever
IMarshal()
interface pointer has been returned.
Writes the CLSID of the proxy to the stream to be used for marshaling.
Marshals the interface pointer by calling
IMarshal::MarshalInterface()
.
If you are implementing existing COM interfaces or defining your own
interfaces using the Microsoft Interface Definition Language (MIDL), the MIDL-generated
proxies and stubs call
CoMarshalInterface()
for you.
If
you are writing your own proxies and stubs, your proxy code and stub code
should each call
CoMarshalInterface()
to correctly marshal
interface pointers.
Calling
IMarshal()
directly from your
proxy and stub code is not recommended.
If you are writing your own implementation of
IMarshal()
,
and your proxy needs access to a private object, you can include an interface
pointer to that object as part of the data you write to the stream.
In such
situations, if you want to use COM's default marshaling implementation when
passing the interface pointer, you can call
CoMarshalInterface()
on the object to do so.
[in] Pointer to the stream to be used during marshaling.
[in] Reference to the identifier of the interface to be marshaled.
This interface must be derived from the
IUnknown()
interface.
[in] Pointer to the interface to be marshaled; can be
NULL
if the caller does not have a pointer to the desired interface.
This interface must be derived from the
IUnknown()
interface.
[in] Destination context where the specified interface is
to be unmarshaled.
Values for
dwDestContext
come from
the enumeration
MSHCTX
.
Currently, unmarshaling can occur
either in another apartment of the current process (MSHCTX_INPROC
) or in another process on the same computer as the current process
(MSHCTX_LOCAL
).
[in] Reserved for future use; must be
NULL
.
[in] Flag specifying whether the data to be marshaled is to
be transmitted back to the client process the normal case or written to a
global table, where it can be retrieved by multiple clients.
Values come from
the
MSHLFLAGS
enumeration.
This function supports the standard return values
E_FAIL
,
E_OUTOFMEMORY
, and
E_UNEXPECTED
, as well as the
following:
S_OK
The interface pointer was marshaled successfully.
CO_E_NOTINITIALIZED
The
CoInitialize()
or
OleInitialize
function was not called on the current thread before this function
was called.
IStream()
errorsThis function can also return any of the stream-access error
values returned by the
IStream()
interface.
CoUnmarshalInterface()
,
IMarshal::MarshalInterface()
- Enables a downloaded DLL to register its custom interfaces within its running
process so that the marshaling code will be able to marshal those interfaces.
CoRegisterPSClsid()
WINOLEAPI CoRegisterPSCLsid(
#include <objbase.h>
REFIID riid,
REFCLSID rclsid
);
Normally the code responsible for marshaling an interface pointer into
the current running process reads the
HKEY_CLASSES_ROOT
\Interfaces
section of the registry to obtain the CLSID of the DLL containing the
ProxyStub
code to be loaded.
To obtain the
ProxyStub
CLSIDs for an existing interface, the code calls the
CoGetPSClsid()
function.
In some cases, however, it may be desirable or necessary for an in-process
handler or in-process server to make its custom interfaces available without
writing to the registry.
A DLL downloaded across a network may not even have
permission to access the local registry, and because the code originated on
another machine, the user, for security purposes, may want to run it in a
restricted environment.
Or a DLL may have custom interfaces that it uses to
talk to a remote server and may also include the
ProxyStub
code for those interfaces.
In such cases, a DLL needs an alternative way to
register its interfaces.
CoRegisterPSClsid()
, used in conjunction
with
CoRegisterClassObject()
, provides that alternative.
A DLL would normally call
CoRegisterPSClsid()
as
shown in the following code fragment:
HRESULT RegisterMyCustomInterface(DWORD *pdwRegistrationKey) { HRESULT hr = CoRegisterClassObject(CLSID_MyProxyStubClsid, pIPSFactoryBuffer, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE pdwRegistrationKey); if(SUCCEEDED)(hr)) { hr = CoRegisterPSClsid(IID_MyCustomInterface, CLSID_MyProxyStubClsid); } return hr; }
[in] Points to the IID of the interface to be registered.
[in] Points to the CLSID of the DLL that contains the proxy/stub code for the custom interface specified by riid.
S_OK
The custom interface was successfully registered.
E_INVALIDARG
One of the parameters is invalid.
E_OUTOFMEMORY
There is insufficient memory to complete this operation.
CoGetPSClsid()
,
CoRegisterClassObject()
- Destroys a previously marshaled data packet.
CoReleaseMarshalData()
STDAPI CoReleaseMarshalData((
#include <objbase.h>
IStream * pStm
);
The
CoReleaseMarshalData()
function performs the
following tasks:
The function reads a CLSID from the stream.
If COM's default marshaling implementation is being used,
the function gets an
IMarshal()
pointer to an instance
of the standard unmarshaler.
If custom marshaling is being used, the function
creates a proxy by calling the
CoCreateInstance()
function,
passing the CLSID it read from the stream, and requesting an
IMarshal()
interface pointer to the newly created proxy.
Using whichever
IMarshal()
interface pointer
it has acquired, the function calls
IMarshal::ReleaseMarshalData()
.
You typically do not call this function.
The only situation in which
you might need to call this function is if you use custom marshaling (write
and use your own implementation of
IMarshal()
).
Examples
of when
CoReleaseMarshalData()
should be called include
the following situations:
An attempt was made to unmarshal the data packet, but it failed.
A marshaled data packet was removed from a global table.
As an analogy, the data packet can be thought of as a reference to the
original object, just as if it were another interface pointer being held on
the object.
Like a real interface pointer, that data packet must be released
at some point.
The use of
IMarshal::ReleaseMarshalData()
to release data packets is analogous to the use of
IUnknown::Release()
to release interface pointers.
Note that you do not need to call
CoReleaseMarshalData()
after a successful call of the
CoUnmarshalInterface()
function;
that function releases the marshal data as part of the processing that it
does.
[in] Pointer to the stream that contains the data packet to be destroyed.
This function supports the standard return values
E_FAIL
,
E_INVALIDARG
,
E_OUTOFMEMORY
, and
E_UNEXPECTED
, as well as the following:
S_OK
The data packet was successfully destroyed.
STG_E_INVALIDPOINTER
An
IStream()
error dealing with the
pStm
parameter.
CO_E_NOTINITIALIZED
The
CoInitialize()
or
OleInitialize
function was not called on the current thread before this function
was called.
IMarshal::ReleaseMarshalData()
- Unmarshals an HRESULT type from the specified stream.
CoUnmarshalHresult()
STDAPI CoUnmarshalHresult(
#include <objbase.h>
LPSTREAM pStm,
HRESULT * phresult
);
You do not explicitly call this function unless you are performing custom
marshaling (that is, writing your own implementation of
IMarshal()
), and your implementation needs to unmarshal an
HRESULT
.
You must use
CoUnmarshalHresult
to unmarshal
HRESULT
s previously marshaled by a call to the
CoMarshalHresult
function.
This function performs the following tasks:
Reads an
HRESULT
from a stream.
Returns the
HRESULT
.
[in] Pointer to the stream from which the
HRESULT
is to be unmarshaled.
[out] Pointer to the unmarshaled
HRESULT
.
This function supports the standard return values
E_OUTOFMEMORY
and
E_UNEXPECTED
, as well as the following:
S_OK
The
HRESULT
was unmarshaled successfully.
STG_E_INVALIDPOINTER
pStm is an invalid pointer.
- Initializes a newly created proxy using data written into the stream by a
previous call to the
CoUnmarshalInterface()
CoMarshalInterface()
function, and
returns an interface pointer to that proxy.
STDAPI CoUnmarshalInterface((
#include <objbase.h>
IStream * pStm,
REFIID riid,
void ** ppv
);
The
CoUnmarshalInterface()
function performs the
following tasks:
Reads from the stream the CLSID to be used to create an instance of the proxy.
Gets an
IMarshal()
pointer to the proxy
that is to do the unmarshaling.
If the object uses COM's default marshaling
implementation, the pointer thus obtained is to an instance of the generic
proxy object.
If the marshaling is occurring between two threads in the same
process, the pointer is to an instance of the in-process free threaded marshaler.
If the object provides its own marshaling code,
CoUnmarshalInterface()
calls the
CoCreateInstance()
function, passing
the CLSID it read from the marshaling stream.
CoCreateInstance()
creates an instance of the object's proxy and returns an
IMarshal()
interface pointer to the proxy.
Using whichever
IMarshal()
interface pointer
it has acquired, the function then calls
IMarshal::UnmarshalInterface()
and, if appropriate,
IMarshal::ReleaseMarshalData()
.
The primary caller of this function is COM itself, from within interface
proxies or stubs that unmarshal an interface pointer.
There are, however,
some situations in which you might call
CoUnmarshalInterface()
.
For example, if you are implementing a stub, your implementation would call
CoUnmarshalInterface()
when the stub receives an interface pointer
as a parameter in a method call.
[in] Pointer to the stream from which the interface is to be unmarshaled.
[in] Reference to the identifier of the interface to be unmarshaled.
[out] Address of pointer variable that receives the interface
pointer requested in
riid.
Upon successful return,
*
ppvcontains the requested interface pointer
for the unmarshaled interface.
This function supports the standard return value
E_FAIL
,
as well as the following:
S_OK
The interface pointer was unmarshaled successfully.
STG_E_INVALIDPOINTER
pStm is an invalid pointer.
CO_E_NOTINITIALIZED
The
CoInitialize()
function was not called
on the current thread before this function was called.
CO_E_OBJNOTCONNECTED
The object application has been disconnected from the remoting
system (for example, as a result of a call to the
CoDisconnectObject()
function).
REGDB_E_CLASSNOTREG
An error occurred reading the registration database.
E_NOINTERFACE
The final
QueryInterface()
of this function
for the requested interface returned
E_NOINTERFACE
.
CoCreateInstance()
errorsAn error occurred when creating the handler.
CoMarshalInterface()
,
IMarshal::UnmarshalInterface()
The
EXTCONN
enumeration specifies the type of external
connection existing on an embedded object.
Currently, the only supported type
is
EXTCONN_STRONG
, meaning that the external connection
is a link.
This
EXTCONN
constant is used in the
IExternalConnection::AddConnection()
and
IExternalConnection::ReleaseConnection()
methods.
typedef enum tagEXTCONN { EXTCONN_STRONG = 0X0001, EXTCONN_WEAK = 0X0002, EXTCONN_CALLABLE = 0X0004 } EXTCONN;
EXTCONN_STRONG
If this value is specified, the external connection must keep
the object alive until all strong external connections are cleared through
IExternalConnection::ReleaseConnection()
.
EXTCONN_WEAK
This value is currently not used.
EXTCONN_CALLABLE
This value is currently not used.
The
MSHCTX
enumeration constants specify the destination
context, which is the process in which the unmarshaling is to be done.
These
flags are used in the
IMarshal()
and
IStdMarshalInfo()
interfaces and in the
CoMarshalInterface()
and
CoGetStandardMarshal()
functions.
MSHCTX
is defined in
WTYPES.IDL
and in
WTYPES.H
].
typedef enum tagMSHCTX { MSHCTX_LOCAL = 0, MSHCTX_NOSHAREDMEM = 1, MSHCTX_DIFFERENTMACHINE = 2, MSHCTX_INPROC = 3 } MSHCTX;
MSHCTX_LOCAL
The unmarshaling process is local and has shared memory access with the marshaling process.
MSHCTX_NOSHAREDMEM
The unmarshaling process does not have shared memory access with the marshaling process.
MSHCTX_DIFFERENTMACHINE
The unmarshaling process is on a different machine. The marshaling code cannot assume that a particular piece of application code is installed on that machine.
MSHCTX_INPROC
The unmarshaling will be done in another apartment in the same process. If your object supports multiple threads, your custom marshaler can pass a direct pointer instead of creating a proxy object.
CoGetStandardMarshal()
,
CoMarshalInterface()
,
IMarshal()
,
IStdMarshalInfo()
The
MSHLFLAGS
enumeration constants determine why
the marshaling is to be done.
These flags are used in the
IMarshal()
interface and the
CoMarshalInterface()
and
CoGetStandardMarshal()
functions.
MSHFLAGS
is defined in
WTYPES.IDL
and
WTYPES.H
].
typedef enum tagMSHLFLAGS { MSHLFLAGS_NORMAL = 0, MSHLFLAGS_TABLESTRONG = 1, MSHLFLAGS_TABLEWEAK = 2 } MSHLFLAGS;
MSHLFLAGS_NORMAL
The marshaling is occurring because an interface pointer is
being passed from one process to another.
This is the normal case.
The data
packet produced by the marshaling process will be unmarshaled in the destination
process.
The marshaled data packet can be unmarshaled just once, or not at
all.
If the receiver unmarshals the data packet successfully, the
CoReleaseMarshalData()
function is automatically called on the data
packet as part of the unmarshaling process.
If the receiver does not or cannot
unmarshal the data packet, the sender must call the
CoReleaseMarshalData()
function on the data packet.
MSHLFLAGS_TABLESTRONG
The marshaling is occurring because the data packet is to
be stored in a globally accessible table from which it can be unmarshaled
one or more times, or not at all.
The presence of the data packet in the table
counts as a strong reference to the interface being marshaled, meaning that
it is sufficient to keep the object alive.
When the data packet is removed
from the table, the table implementer must call the
CoReleaseMarshalData()
function on the data packet.
MSHLFLAGS_TABLESTRONG
is used by the
RegisterDragDrop
function when registering a window as a drop target.
This keeps
the window registered as a drop target no matter how many times the end user
drags across the window.
The
RevokeDragDrop
function calls
CoReleaseMarshalData()
.
MSHLFLAGS_TABLEWEAK
The marshaling is occurring because the data packet is to
be stored in a globally accessible table from which it can be unmarshaled
one or more times, or not at all.
However, the presence of the data packet
in the table acts as a weak reference to the interface being marshaled, meaning
that it is not sufficient to keep the object alive.
When the data packet is
removed from the table, the table implementer must call the
CoReleaseMarshalData()
function on the data packet.
MSHLFLAGS_TABLEWEAK
is typically used when registering
an object in the Running Object Table (ROT).
This prevents the object's entry
in the ROT from keeping the object alive in the absence of any other connections.
See
IRunningObjectTable::Register()
for more information.
CoGetStandardMarshal()
,
CoMarshalInterface()
,
CoReleaseMarshalData()