As described in earlier chapters, a COM Client is simply any piece of code that makes use of another object through that object's interfaces. In this sense, a COM Client may itself be a COM Server acting in the capacity of a client by virtue of using (or reusing) some other object.
If the client is an application, that is, an executable program as opposed to a DLL, then it must follow all the requirements for a COM Application as detailed in Chapter 4. That aside, clients have a number of ways to actually get at an object to use as discussed in a previous chapter. The client may call a specific function to create an object, it might ask an existing object to create another, or it might itself implement an object to which some other code hands yet another object's interface pointer. Not all of these objects must have CLSID.
This chapter, however, is concerned with those clients that want to create an object based on a CLSID, because at some point or another, many operations that don't directly involve a CLSID do eventually resolve to this process. For example, moniker binding internally uses a CLSID but shields clients from that fact. In any case, whatever client code uses a CLSID will generally perform the following operations in order to make use of an object:
Identify the class of object to use.
Obtain the ``class factory'' for the object class and ask it to create an uninitialized instance of the object class, returning an interface pointer to it.
Initialize the newly created object by calling an initialization member function of the ``initialization interface,'' that is, one of a generally small set of interfaces that have such functions.
Make use of the object which generally includes calling
QueryInterface()
to obtain additional working interface pointers
on the object.
The client must be prepared for the potential absence of a
desired interface.
Release()
the object when it is no longer
needed.
The following sections cover the functions and interfaces involved in each of these steps. In addition, the client may want to more closely manage the loading and unloading of server modules (DLLs or EXEs) for optimization purposes, so this chapter includes a section of such management.
As far as the client is concerned, the COM Library exists to provide fundamental implementation locator and object creation services and to handle remote procedure calls to local or remote objects (in addition to memory management services, of course). How a server facilitates these functions is the topic of Chapter 8.
Before examining the details of object creating and manipulation, realize that after the object is created and the client has its first interface pointer to that object, the client cannot distinguish an in-process object from a local object from a remote object by virtue of examining the interface pointer or any other interfaces on that object. That is, all objects appear identically to the client such that after creation, all requests made to the object's services are made by calling interface member functions. Period. There are not special exceptions that a client must make at run-time based on the distance of the object in question. The COM Library provides any underlying glue to insure that a call made to a local or remote object is, in fact, marshaled properly to the other process or the other machine, respectively. This operation is transparent to the client, who always sees any call to an object as a function call to the objects interfaces as if that object were in-process. This consistency is a key benefit for COM clients as it can treat all objects identically regardless of their actual execution context. If you are interested in understanding how this transparency is achieved, please see Chapter 9 for more details. There you will find that all clients do, in fact, always call an in-process object first, but in local and remote cases that in-process object is just a proxy that takes care of generating a remote procedure call.
Because COM does not have a strict class model, there are several ways to instantiate or to get a pointer to an interface on an object. There are, in fact, four methods through which a client obtains its first interface pointer to a given object:
Call a COM Library API function that creates an object of a pre-determined type that is, the function will only return a pointer to one specific interface for a specific object class.
Call a COM Library API function that can create an object based on a class identifier (CLSID) and that returns any type of interface pointer requested.
Call a method of some interface that creates another object (or connects to an existing one) and returns an interface pointer on that separate object.
Implement an object with an interface through which other objects pass their interface pointer to the client directly.
For information on getting pointers to other interfaces on an object once you have the first one, see Section 3.1.7.1.
There are numerous COM functions that return pointers to specific interface
implementations, such as
CoGetMalloc()
, which retrieves
a pointer to the
standard COM memory allocator.
Most of these are helper functions, which
retrieve a pointer to an COM implementation of an interface on an object,
as
does
CoGetMalloc()
.
Most of these functions are described
in the specific
area they are related to, such as storage or data transfer.
There are several functions that, given a CLSID, a client can call to
create an
object instance and get a pointer to it.
All of these functions are based
on
the function
CoGetClassObject()
, which creates a class
object and
supplies a pointer to an interface that allows you to create instances of
that
class.
While there must be information that says which system the server
resides on, there is no need for that information to be contained in the
client.
The client needs to know only the CLSID, and never the absolute path
of
the server code.
For more information, see Section
Section 7.1.1.
Among the many interface methods that return a pointer to a separate
object are
several that create and return a pointer to an enumerator object, which allows
you to determine how many items of a given type an object maintains.
COM
defines interfaces for enumerating a wide variety of items, such as strings,
several structures important in various COM technologies, monikers, and
IUnknown()
interface pointers.
The typical way to create
an
enumerator instance and get a pointer to its interface is to call a method
from
another interface.
For example, the
IDataObject()
interface
defines
two methods,
EnumDAdvise
and
EnumFormatEtc
,
that
return pointers to interfaces on two different enumeration objects.
There
are many other
examples in COM of methods that return pointers to objects.
The fourth way to get a pointer to an object is used when two objects, such as an OLE Compound Document container and server, need bi-directional communication. Each implements an object containing an interface method to which other objects can pass interface pointers. In the case of containers and servers, each object then passes its pointer to the other object. The implementing object, which is also the client of the created object, can then call the method and get the pointer that was passed.
With the increasing importance of computer networks, it has become necessary for clients and servers to interact easily and efficiently, whether they reside on the same machine or across a network. Crucial to this is the ability of a client to be able to launch a server, create an instance of the server's object, and have access to the methods of the interfaces on the object.
COM provides extensions to this basic COM process that make it virtually seamless across a network. If a client is able to identify the server through its CLSID, calling a few simple functions permit COM to do all the work of locating and launching the server, and activating the object. New subkeys have been added to the registry that allow remote servers to register their location, so the client does not require that information. For applications that want to take advantage of networking features, object creation functions are provided that allow more flexibility and efficiency.
A COM server is implemented as a COM class. A COM class is an implementation of a group of interfaces in code executed whenever you interact with a given object. There is an important distinction between a C++ class and a COM class. In C++, a class is a type. A COM class is simply a definition of the object, and carries no type, although a C++ programmer might implement it using a C++ class. COM is designed to allow a class to be used by different applications, including applications written without knowledge of that particular class's existence. Therefore, class code for a given type of object exists either in a dynamic linked library (DLL) or in another application (EXE).
Each COM class is identified by a CLSID, a unique 128-bit GUID, which the server must register. COM uses this CLSID, at the request of a client, to associate specific data with the DLL or EXE containing the code that implements the class, thus creating an instance of the object. For information on registering a server, see Section Section 8.4.
For clients and servers on the same machine, the model previously supported, the CLSID of the server is all the client ever needs. On each machine, COM maintains a database (it makes use of the system registry on Windows and Macintosh platforms) of all the CLSIDs for the servers installed on the system. This is a mapping between each CLSID and the location of the DLL or EXE that houses the code for that CLSID. COM consults this database whenever a client wants to create an instance of a COM class and use its services, so the client never needs to know the absolute location of the code on the machine.
For distributed systems, COM provides registry entries that allow a remote server to register itself for use by a client. While applications need know only a server's CLSID, because they can rely on the registry to locate the server, COM allows clients to override registry entries and to specify server locations, to take full advantage of the network (see Section Section 7.1.3.
The basic way to create an instance of a class is through a COM
class
object.
This is simply an intermediate object that supports functions
common to creating new instances of a given class.
Most class objects used
to
create objects from a CLSID support the
IClassFactory()
interface,
an interface that includes the important method
CreateInstance
.
You
implement an
IClassFactory()
interface for each class of
object that you
offer to be instantiated.
For information on implementing
IClassFactory()
,
refer to Section
Section 8.2.
Servers that support some other custom class factory interface are not required to support
IClassFactory()
specifically. However, calls to activation functions other thanCoGetClassObject()
(such asCoCreateInstanceEx()
) require that the server supportIClassFactory()
.
When a client wants to create an instance of the server's object, it
uses the
desired object's CLSID in a call to
CoGetClassObject()
.
(This call
can either be direct or implicit, through one of the object creation helper
functions.)
COM has just a few API functions on which many of the others are built.
The
most important of these is probably
CoGetClassObject()
,
which underlies
all of the instance creation functions.
This function locates the code
associated with the CLSID, and creates a class object, and supplies a pointer
to the interface requested (CoGetClassObject()
takes a
riid
param
that specifies the client's desired interface pointer).
With this pointer, the caller can create an instance of the object,
and
retrieve a pointer to a requested interface on the object.
This is usually
an
initialization interface, used to activate the object (put it in the running
state), so the client can do whatever work with the object that it wants to.
Using these basic functions, the client must also take care to release all
object pointers.
COM provides several helper functions that reduce the work
of
creating object instances.
These are described in
Instance Creation
Helper Functions
.
Another mechanism for activating object instances is through the class
moniker.
Class monikers bind to the class object of the class for which they are
created.
For more information, see
CreateClassMoniker()
.
COM uses the basic model for object creation described in COM Class Objects and CLSIDs, and adds more than one way to locate an object that may reside on another system in a network, without overburdening the client application.
COM has registry keys that permit a server to register the name of the machine on which it resides, or the machine where an existing storage is located. Thus, client applications need know only the CLSID of the server.
CoGetClassObject()
takes a
COSERVERINFO
structure, which
allows a client to specify the location of a server.
Another important value
in
this function is the
CLSCTX
enumeration, which specifies
whether the expected
object is to be run in-process, out-of-process local, or out-of-process remote.
Taken together, these two values and the values in the registry determine
how
and where the object is to be run.
Instance creation calls, when they specify
a
server location, can override a registry setting.
The algorithm COM uses for
doing this is described in the reference for the
CLSCTX
enumeration.
Remote activation depends on the security relationship between client and server. For more information, see Chapter 10.
In previous releases of COM, the primary mechanism used to create
an object instance was the
CoCreateInstance()
function.
This
function encapsulates the process of creating a class object, using that to
create a new instance and releasing the class object.
To smooth the process of instance creation on distributed systems, COM has introduced four important new instance creation mechanisms:
Class Monikers and
IClassActivator()
CoCreateInstanceEx()
CoGetInstanceFromFile()
CoGetInstanceFromIStorage()
Class monikers are a mechanism that permit you to identify the class
of
an object, and are typically used with another moniker, like a file moniker,
to
indicate the location of the object.
This permits you to bind to an object
ans
specify the server that is to be launched for that object.
Class monikers
may
also be composed to the right of monikers supporting binding to the
IClassActivator()
interface.
For more information, see
CreateClassMoniker()
.
CoCreateInstanceEx()
extends
CoCreateInstance()
to make it possible to create a single uninitialized object associated with
the given
CLSID on a specified remote machine.
In addition, rather than requesting a
single
interface and obtaining a single pointer to that interface,
CoCreateInstanceEx()
makes it possible to query for multiple
interfaces and (if available) receive pointers to them in a single round trip,
thus
permitting fewer round trips between machines.
This can make remote object
interaction much more efficient.
To do this, the function uses an array of
MULTI_QI
structures.
Creating an object through
CoCreateInstanceEx()
still
requires that the
object be initialized through a call to one of the initialization interfaces
(such as
IPersistStorage:::Load
).
The two helper functions,
CoGetInstanceFromFile()
and
CoGetInstanceFromIStorage()
encapsulate both the instance creation power of
CoCreateInstanceEx()
and
initialization, the former from a file, and the latter from a storage.
After the client has successfully created an object of a given class
it
must initialize that object.
By definition, any new object created using
IClassFactory::CreateInstance
(or variant or wrapper thereof)
is uninitialized.
Initialization generally happens through a single call to a member function
of
the ``initialization interface.'' This interface is usually the one requested
by
the client in its call to create the object, but this is not required.
Before
an object is initialized, the only calls that are guaranteed to work on the
object (besides the initializing functions themselves) are the
IUnknown()
functions (of any interface) unless otherwise explicitly specified in the
definition of an interface.
In addition,
QueryInterface()
is only guaranteed to
work for
IUnknown()
and any initialization interface, but
not guaranteed for a
non-initialization interface.
Some objects will not require initialization before they are function through all of their interfaces. Those that do require initialization will define, either explicitly through documentation of the object or implicitly through the scenarios in which the object is used, which member of which interface can be used for initialization.
For example, objects that can serialize their persistent data to a file
will
implement the
IPersistFile()
interface (see Section
Section 15.6).
The function
IPersistFile::Load()
, which instructs the
object to load its data from a file, is the initialization function and
IPersistFile()
is the initialization interface.
Other examples
are objects that
can serialize to storages or streams, where the objects implement the
initialization interfaces
IPersistStorage()
or
IPersistStream()
, respectively
(again, see
Chapter 8).
The Load functions in these interfaces are
initialization functions as is
IPersistStorage::InitNew()
,
which initializes a
new object with storage instead of loading a previously saved version.
Once an object is initialized, it is entirely up to the client to
determine what it intends to do with that object.
It is often the case that
the
initializing interface is not the ``working'' interface through which the
client
will primarily use the object.
The creation sequence only nets the client
a
single interface pointer that has a limited scope of functionality.
If the
client wishes to perform an operation outside that scope, it must call the
known interface's
QueryInterface()
function to ask for
another interface on the
same object.
For example, say a client has created and initialized an object but
now wishes
to obtain a graphical presentation, say a bitmap, from that object by calling
IDataObject::GetData
(see
Chapter 17
for details on this function).
The client
must call
QueryInterface()
to obtain an
IDataObject()
pointer before calling the function.
It is important to note that all operations on that object will occur through calls to the member functions of the object's various interfaces. Any additional API functions that the client might call to affect the object itself are usually wrapper functions of common sequences of interface function calls. There simply is no other way to affect the object other than through its interfaces.
Because a client must ask for an interface before it can possibly ask
the
object to perform the actions defined in the interface, the client cannot
ask
the object to perform an action the object does not support.
This is a primary
strength of the
QueryInterface()
function as described
in the early chapters of
this document.
Calling
QueryInterface()
for access to an
object's functionality
is not problematic nor inconvenient because the client usually makes the call
specifically at the point where the client wants to perform some action on
the
object.
That is, clients generally do not call
QueryInterface()
for all possible
interfaces after the object is created so as to have all the pointers on
hand--instead, the client calls
QueryInterface()
before
attempting to perform
some action with the object.
In practice this means that the client must be prepared for the failure
of a
call to
QueryInterface()
.
Instead of being a complete pain
to implementation,
such preparation defines a mechanism through which the client can make dynamic
choices based on the functionality of the object itself on an object-by-object
basis.
For example, consider a client application that has created a number
of objects
and it now wants to save the application's state, which includes saving the
state of each object.
Let's say the client is using structured storage for
its
native file representation, so its first choice will be to assign an individual
storage element in that file for each object.
Each object can then store
structured information itself and it indicates its ability to do by
implementing the
IPersistStorage()
interface.
However,
some object may not know
how to write to a storage but know how to write to a stream and indicate the
capability by implementing
IPersistStream()
.
Yet others
may only know how to
write information to a file themselves and thus implement
IPersistFile()
.
Finally, some objects may not know how to serialize themselves at all, but
can
provide a binary memory copy of the their native data through
IDataObject()
.
In this case the client's strategy will be as follows: if an object
supports
IPersistStorage()
, then give it an
IStorage()
instance and ask it to save its data
into it by calling
IPersistStorage::Save()
.
If that object
does not provide such
support, check if it supports
IPersistStream()
, and if
so, create a
client-controlled stream for it (in perhaps a separate client-controlled
storage element) and pass that
IStream()
pointer to the
object through
IPersistStream::Save()
.
If the object does not support
streams, then check for
IPersistFile()
.
If the object supports serialization to
a file, then have the
object write its data into a temporary file by calling
IPersistFile::Save()
, then
make a binary copy of that file in a client-controlled stream element within
a
client-controlled storage element.
If all else fails, attempt to retrieve
the
object's binary data from
IDataObject::GetData
using the
first format the
object supports, and write that binary data into a client-controlled stream
in
a client-controlled storage.
Reloading these objects would be a similar procedure, but the client
would
know, from the structure of its storage and other information it saved about
the objects itself, which method to use to reload the object from the storage.
The client wants to insure that it uses the same method to load the object
that
it did for saving it originally, that is, use the same interface instead of
querying for the best one.
The reason is that while the data was passively
stored on disk, the object that wrote that data might have been updated such
that where it once only supported
IPersistStream()
, for
example, it now supports
IPersistStorage()
.
In that case the client should ask it
to load the data using
IPersistStream::Load()
.
However, when the client goes to save the object again, it will now
successfully find that the object supports
IPersistStorage()
and can now have the
object save into a storage element instead.
(The container would also insure
that the old client-controlled stream was deleted as it is no longer in use
for
that object.) This demonstrates how an object can be updated and new interfaces
supported without
any
recompilation on the part of existing
clients
while at the same time
suddenly working with clients on a higher
level of
integration than before.
In order to remain compatible the object
must
insure that it supports the older interfaces (such as
IPersistStream()
) but is
free to add new contracts--new interfaces such as IPersistStorage--as
it wants
to provide new functionality.
The point of this example, which is also true for clients that use any
other
interfaces an object might support in other scenarios, is that the client
is
empowered to make dynamic decisions on a per-object basis through the
QueryInterface()
function.
Containers programmed to be
dynamic as such allow
object to improve independently while insuring that the container will work
as
good--and generally better--as it always has with any given object.
All of this
is due to the powerful and important
QueryInterface()
mechanism
that for all
intents and purposes is the single most important aspect of true system
component software.
The final operation required in a COM client when dealing with an object
from some other server is to free that object when the client no longer needs
it.
This is achieved by calling the
Release()
member function
of all interfaces
obtained during the course of using the object.
Recall that a function that creates or synthesizes a new interface pointer
is
responsible for calling
AddRef()
through that pointer before
returning it to the
caller of the function.
This applies to the
IClassFactory::CreateInstance
function as well as
CoCreateInstance()
(and for that matter,
CoGetClassObject()
,
too, which is why you must call
IClassFactory::Release()
after creating the
object).
Therefore, as far as the client is concerned, the object will have
a
reference count of one after creation.
The object may, in fact, have a higher
reference count if it is also being used from other clients as well, but each
client is only responsible and cognizant of the reference counts added on
its
behalf.
The other primary function that creates new interface pointers is
QueryInterface()
.
Every call the client makes to
QueryInterface()
to obtain another
interface pointer will internally generate another call to
AddRef()
in that
object, incrementing the reference count.
Therefore, in addition to calling
Release()
through the interface pointer obtained in the
creation sequence, the
client must also call
Release()
through any interface pointer
obtained from
QueryInterface()
(this is illustrated in the pseudo-code
of the previous
section).
The bottom line is that the client is responsible for matching any operation
that generates a call to
AddRef()
through a given interface
pointer with a call
to
Release()
through that same interface pointer.
It is
not necessary to call
Release()
in the opposite order of calls to
AddRef()
; it is
just necessary to match
the pairs.
Failure to do so will cause memory leaks as objects are not freed
and servers are not allowed to shut down properly.
This is no different from
forgetting to free memory obtained through
malloc()
.
Finally, although the client matches its calls to
AddRef()
and
Release()
, the
actual object may still continue to run and the server may continue to execute
as well without any objects in service.
The object will continue if other
clients are using that same object and thus have reference counts on it.
Only
when all clients have released their references will that object free itself.
The server will, of course, continue to execute as long as there is an object
to serve, but the client does have some power over keeping a server running
even without objects.
That is the purpose of Server Management functions in
COM.
As mentioned in previous sections, a client has the ability to manage
servers on the server level to keep them running even when they are not serving
any objects.
The client's primary mechanism for this is the
IClassFactory::LockServer
function described above.
By
calling this function
with the
TRUE
parameter, the client places a `lock' on
the server.
As long as
the server either has objects created
or
has one or more
locks on it,
the server will continue to execute.
When the server detects a zero object
and
zero lock condition, it can unload itself (which differs between DLL and EXE
servers, as described in
Chapter 8).
A client can place more than one lock on a server by calling
IClassFactory::LockServer
(TRUE
) more
than once.
Each call
to
LockServer(TRUE)()
must be matched with a call to
LockServer
(FALSE
)--the
server maintains a lock
count for the server as it maintains a reference count for its served objects.
But while
AddRef()
and
Release()
affect
objects,
LockServer
affects the server
itself.
LockServer
affects all servers--in-process,
local, and remote--identically.
The
client does have some additional control over in-process objects as it normally
would for other DLLs through the functions
CoLoadLibrary()
,
CoFreeUnusedLibraries()
, and
CoFreeAllLibraries()
, as described
below.
Normally only
CoFreeUnusedLibraries()
is called
from a client whereas the
others are generally used inside the COM Library to implement other API
functions.
In addition, the COM Library supplies one additional function that
has meaning in this context,
CoIsHandlerConnected()
, which
tells the
container if an object handler is currently working in association with a
local
server as described in its entry below.
The
ICatInformation()
interface provides methods
for obtaining information about the categories implemented or required by
a certain class, as well as information about the categories registered on
a given machine.
There is no need to implement this interface.
The Component Category
Manager, a system-provided COM object that can be instantiated by using
CoCreateInstance()
, implements
ICatInformation()
.
Call the methods of
ICatInformation()
to obtain a
listing of available categories, enumerate classes that belong to a particular
category, and determine if a class belongs to a specific category.
Information on using component categories can be found in Component Categories Manager Implementation.
|
Description
|
QueryInterface()
|
Returns pointers to supported interfaces. |
AddRef()
|
Increments reference count. |
Release()
|
Decrements reference count. |
|
Description
|
EnumCategories
|
Returns an enumerator for the component categories registered on the system. |
GetCategoryDesc
|
Retrieves the localized description string for a specific category ID. |
EnumClassesOfCategories
|
Returns an enumerator over the classes that implement/require a certain set of categories. |
IsClassOfCategories
|
Determines if a class implements/requires the specified categories. |
EnumImplCategoriesOfClass
|
Returns an enumerator over the CATIDs implemented by the specified class. |
EnumReqCategoriesOfClass
|
Returns an enumerator over the CATIDs required by the specified class. |
- Returns an enumerator for the component categories registered
on the system.
ICatInformation::EnumCategories()
HRESULT EnumCategories(
#include <comcat.h>
LCID lcid,
IEnumCATEGORYINFO ** ppenumCatInfo
);
[in] Identifies the requested locale for any return
szDescription
of the enumerated
CATEGORYINFO
s.
Typically, the caller
specifies
GetUserDefaultLCID()
for this parameter.
[out] Specifies the location to return an
IEnumCATEGORYINFO
interface.
This can be used to enumerate the CATIDs and localized description strings
of the component
categories registered with the system.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
ICatInformation::EnumClassesOfCategories()
,
ICatInformation::EnumImplCategoriesOfClass()
,
ICatInformation::EnumReqCategoriesOfClass()
,
ICatInformation::GetCategoryDesc()
,
ICatInformation::IsClassOfCategories()
- Returns an enumerator over the classes that implement one or more
of
rgcatidImpl.
If a class requires a category
not listed in
rgcatidReq, it is not included
in the enumeration.
ICatInformation::EnumClassesOfCategories()
HRESULT EnumClassesOfCategories(
#include <comcat.h>
ULONG cImplemented,
CATID rgcatidImpl,
ULONG cRequired,
CATID rgcatidReq,
IEnumCLSID ** ppenumCLSID
);
[in] The number of category IDs in the
rgcatidImpl
array.
This value cannot be zero.
If this value is ((ULONG
) -1),
classes are included in the enumeration, regardless of the categories they
implement.
[in] An array of category identifiers.
[in] The number of category IDs in the
rgcatidReq
array.
This value can be zero.
If this value is ((ULONG
) -1),
classes
are included in the enumeration, regardless of the categories they require.
[in] An array of category identifiers.
[out] The location in which to return an
IEnumCLSID
interface
that can be used to enumerate the CLSIDs of the classes that implement category
rcatid.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
ICatInformation::EnumCategories()
,
ICatInformation::EnumImplCategoriesOfClass()
,
ICatInformation::EnumReqCategoriesOfClass()
,
ICatInformation::GetCategoryDesc()
,
ICatInformation::IsClassOfCategories()
- Returns an enumerator for the CATIDs implemented
by the specified class.
ICatInformation::EnumImplCategoriesOfClass()
HRESULT EnumImplCategoriesOfClass(
#include <comcat.h>
REFCLSID rclsid,
IEnumCATID ** ppenumCATD
);
[in] Specifies the class ID.
[out] Specifies the location to return an
IEnumCATID
interface.
This can be used to enumerate the CATIDs that are implemented
by
rclsid.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
ICatInformation::EnumCategories()
,
ICatInformation::EnumClassesOfCategories()
,
ICatInformation::EnumReqCategoriesOfClass()
,
ICatInformation::GetCategoryDesc()
,
ICatInformation::IsClassOfCategories()
- Returns an enumerator for the CATIDs required by the specified
class.
ICatInformation::EnumReqCategoriesOfClass()
HRESULT EnumReqCategoriesOfClass(
#include <comcat.h>
REFCLSID rclsid,
IEnumCATID ** ppenumCATD
);
[in] Specifies the class ID.
[out] Specifies the location to return an
IEnumCATID
interface.
This can be used to enumerate the CATIDs that are required by
rclsid.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
ICatInformation::EnumCategories()
,
ICatInformation::EnumClassesOfCategories()
,
ICatInformation::EnumImplCategoriesOfClass()
,
ICatInformation::GetCategoryDesc()
,
ICatInformation::IsClassOfCategories()
- Retrieves the localized description string for a specific category
ID.
ICatInformation::GetCategoryDesc()
HRESULT GetCategoryDesc(
#include <comcat.h>
REFCATID rcatid,
LCID lcid,
PWCHAR * ppszDesc
);
[in] Identifies the category for which the description string is to be returned.
[in] Specifies the locale in which the resulting string is returned.
[out] A pointer to the string pointer that contains the description.
This
must be released by the caller using
CoMemTaskFree
.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
CAT_E_CATIDNOEXIST
The category ID rcatid is not registered.
CAT_E_NODESCRIPTION
There is no description string for rcatid with the specified locale.
ICatInformation::EnumCategories()
,
ICatInformation::EnumClassesOfCategories()
,
ICatInformation::EnumImplCategoriesOfClass()
,
ICatInformation::EnumReqCategoriesOfClass()
,
ICatInformation::IsClassOfCategories()
- Determines if a class implements one or more categories.
If the
class
requires a category not listed in
rgcatidReq,
it is not included in the enumeration.
ICatInformation::IsClassOfCategories()
HRESULT IsClassOfCategories(
#include <comcat.h>
REFCLSID rclsid,
ULONG cImplemented,
CATID rgcatidImpl,
ULONG cRequired,
CATID rgcatidReq
);
[in] The class ID of the relevent class to query.
[in] The number of category IDs in the
rgcatidImpl
array.
This value cannot be zero.
If this value is ((ULONG
)
-1), the implemented categories are not tested.
[in] An array of category identifiers.
[in] The number of category IDs in the
rgcatidReq
array.
This value can be zero.
If this value is ((ULONG
)
-1), the required categories are not tested.
[in] An array of category identifiers.
S_OK
rclsid is of category rcatid.
S_FALSE
rclsid is not of category rcatid.
ICatInformation::EnumCategories()
,
ICatInformation::EnumClassesOfCategories()
,
ICatInformation::EnumImplCategoriesOfClass()
,
ICatInformation::EnumReqCategoriesOfClass()
,
ICatInformation::GetCategoryDesc()
The
IMultiQI()
interface enables a client to query
an object
proxy, or handler, for multiple interfaces, using a single RPC call.
By using
this interface, instead of relying on separate calls to
IUnknown::QueryInterface()
, clients can reduce the number
of
RPC calls that have to cross thread, process, or machine boundaries and, therefore,
the amount of time required to obtain the requeseted interface pointers.
You never have to implement this interface because there is no situation
in which it is required.
COM server applications that rely on COM's standard
remoting support get the interface for free because COM implements it on every
object proxy.
The only situation in which you might want to implement this
interface yourself is if you are writing a custom marshaler that handles
interface remoting.
Even here, implementing
IMultiQI()
yourself is not
recommended, particularly if your object is aggregatable.
When more than one interface pointer is sought, client applications
should
QueryInterface()
for
IMultiQI()
and use it if available.
IUnknown Methods
|
Description
|
QueryInterface()
|
Returns pointers to supported interfaces. |
AddRef()
|
Increments reference count. |
Release()
|
Decrements reference count. |
IMultiQI Methods
|
Description
|
QueryMultipleInterfaces
|
Queries for multiple interfaces. |
- Fills a caller-provided array of structures with pointers to multiple
interfaces.
Calling this method is equivalent to issuing a series of separate
IMultiQI::QueryMultipleInterfaces()
QueryInterface()
calls except that you do not incur the
overhead of a corresponding number of RPC calls.
In multithreaded applications
and distributed environments, keeping RPC calls to a minimum is essential
for
optimal performance.
HRESULT QueryMultipleInterfaces(
#include <objidl.h>
ULONG cMQIs,
MULTI_QI * pMQIs
);
The
QueryMultipleInterfaces
method takes as input
an array of
MULTI_QI
structures.
Each structure specifies an interface
IID
and contains two additional blank fields for receiving an interface pointer
and
return value.
This method obtains as many requested interface pointers as possible directly from the object proxy. For each interface not implemented on the proxy, the method calls the server to obtain a pointer. Upon receiving an interface pointer from the server, the method builds a corresponding interface proxy and returns its pointer along with pointers to the interfaces it already implements.
Notes to Callers
A caller should begin by querying the object proxy for the
IMultiQI()
interface.
If the object proxy returns a pointer
to this
interface, the caller should then create a
MULTI_QI
structure
for each
interface it wants to obtain.
Each structure should specify an interface IID
and set its
pItf
member to
NULL
.
Failure
to set the
pItf
member
to
NULL
will cause the object proxy to ignore the structure.
On return,
QueryMultipleInterfaces
writes the requested
interface
pointer and a return value into each
MULTI_QI
structure
in the client's
array.
The
pItf
field receives the pointer; the
hr
field receives the return value.
If the value returned from a call to
QueryMultipleInterfaces
is
S_OK
,
then pointers were returned for all requested interfaces.
If the return value
is
E_NOINTERFACE
, then pointers were returned for none
of the requested
interfaces.
If the return value is
S_FALSE
, then pointers
to one or more
requested interfaces were not returned.In this event, the client should check
the
hr
field of each
MULTI_QI
structure
to determine
which interfaces were acquired and which were not.
If a client knows ahead of time that it will be using several of an
object's
interfaces, it can call
QueryMultipleInterfaces
up front
and then,
later, if a
QueryInterface()
is done for one of the interfaces
already
acquired through
QueryMultipleInterfaces
, no RPC call will
be
necessary.
On return, the caller should check the
hr
field
of each
MULTI_QI
structure to determine which interface pointers were and were not returned.
The client is responsible for releasing each of the acquired interfaces
by
calling
IUnknown::Release()
.
[in] Pointer to the number of elements in an array of
MULTI_QI
structures, each of which contains the IID of a single interface.
[in, out] Pointer to the first
MULTI_QI
strucutre
in the array.
S_OK
Pointers were returned to all requested interfaces.
S_FALSE
Pointers were returned to some, but not all, of the requested interfaces.
E_NOINTERFACE
Pointers were returned to none of the requested interfaces.
- Creates a single uninitialized object of the class associated with a specified
CLSID.
Call
CoCreateInstance()
CoCreateInstance()
when you want to create
only
one object on the local system.
To create a single object on a remote system,
call
CoCreateInstanceEx()
.
To create multiple objects based
on a single CLSID, refer to the
CoGetClassObject()
function.
STDAPI CoCreateInstance((
#include <objbase.h>
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID * ppv
);
The
CoCreateInstance()
helper function provides a
convenient
shortcut by connecting to the class object associated with the specified CLSID,
creating an uninitialized instance, and releasing the class object.
As such,
it
encapsulates the following functionality:
CoGetClassObject(rclsid, dwClsContext, NULL, IID_IClassFactory, &pCF); hresult = pCF->CreateInstance(pUnkOuter, riid, ppvObj) pCF->Release();
It is convenient to use
CoCreateInstance()
when you
need to create only
a single instance of an object on the local machine.
If you are creating an
instance on remote machine, call
CoCreateInstanceEx()
.
When you are
creating multiple instances, it is more efficient to obtain a pointer to the
class object's
IClassFactory()
interface and use its methods
as needed.
In
the latter case, you should use the
CoGetClassObject()
function.
In the
CLSCTX
enumeration, you can specify the type
of server
used to manage the object.
The constants can be
CLSCTX_INPROC_SERVER
,
CLSTCTX_INPROC_HANDLER
,
CLSCTX_LOCAL_SERVER
,
or any combination of these
values.
The constant
CLSCTX_ALL
is defined as the combination
of all three.
For
more information about the use of one or a combination of these constants,
refer to
CLSCTX
.
[in] CLSID associated with the data and code that will be used to create the object.
[in] If
NULL
, indicates that the object is not being
created
as part of an aggregate.
If non-NULL
, pointer to the aggregate
object's
IUnknown()
interface (the controlling
IUnknown()
).
[in] Context in which the code that manages the newly created object
will run.
The values are taken from the enumeration
CLSCTX
.
[in] Reference to the identifier of the interface to be used to communicate with the object.
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer.
S_OK
An instance of the specified object class was successfully created.
REGDB_E_CLASSNOTREG
A specified class is not registered in the registration database.
Also
can indicate
that the type of server you requested in the
CLSCTX
enumeration
is not registered or the values for the server types in the registry are corrupt.
CLASS_E_NOAGGREGATION
This class cannot be created as part of an aggregate.
CoGetClassObject()
,
IClassFactory::CreateInstance()
,
CoCreateInstanceEx()
,
CLSCTX
,
Instance Creation
Helper Functions
- Creates an instance of a specific class on a specific machine.
CoCreateInstanceEx()
HRESULT CoCreateInstanceEx((
#include <objbase.h>
REFCLSID rclsid,
IUnknown * punkOuter,
DWORD dwClsCtx,
COSERVERINFO * pServerInfo,
ULONG cmq,
MULTI_QI pResults
);
CoCreateInstanceEx()
creates a single uninitialized
object associated with
the given CLSID on a specified remote machine.
This is an extension of the
function
CoCreateInstance()
, which creates an object on
the local
machine only.
In addition, rather than requesting a single interface and
obtaining a single pointer to that interface,
CoCreateInstanceEx()
makes
it possible to specify an array of structures, each pointing to an interface
identifier (IID) on input, and, on return, containing (if available) a pointer
to the requested interface and the return value of the
QueryInterface()
call for that interface.
This permits fewer round trips between machines.
The
CoCreateInstanceEx()
helper function encapsulates
three calls: first,
to
CoGetClassObject()
to connect to the class object associated
with the specified CLSID, specifying the machine location of the class; second,
to
IClassFactory::CreateInstance()
to create an uninitialized
instance, and finally, to
IClassFactory::Release()
, to
release the class
object.
The object so created must still be initialized through a call to one
of the
initialization interfaces (such as
IPersistStorage:::Load
).
The
two helper functions,
CoGetInstanceFromFile()
and
CoGetInstanceFromIStorage()
encapsulate both the instance
creation
and initialization from the obvious sources.
[in] CLSID of the object to be created.
[in] When non-NULL
, indicates the instance is being
created
as part of an aggregate, and
punkOuter
is to be used
as the
new instance's controlling
IUnknown()
.
Aggregation is currently
not supported cross-process or cross-machine.
When instantiating an object
out of
process,
CLASS_E_NOAGGREGATION
will be returned if
punkOuter
is non-NULL
.
[in] Values taken from the
CLSCTX
enumeration.
[in] Machine on which to instantiate the object.
May be
NULL
,
in which case the object is instantiated on the current machine or at the
machine
specified in the registry under the class's
RemoteServerName
named-value, according to the interpretation of the
dwClsCtx
parameter.
See Section
Section 7.9.1
for details).
[in] Number of
MULTI_QI
structures in
pResults.
Must be greater than zero.
Array of
MULTI_QI
structures.
Each structure has
three members:
the identifier for a requested interface (pIID), the
location
to return the interface pointer (pItf) and the return
value of
the call to
QueryInterface()
(hr).
This function supports the standard return value
E_INVALIDARG
, as well
as the following:
S_OK
Indicates success.
CO_S_NOTALLINTERFACES
At least one, but not all of the interfaces requested in the
pResults
array were successfully retrieved.
The
hr
field of each
of the
MULTI_QI
structures in
pResults
indicates
with
S_OK
or
E_NOINTERFACE
whether the specific
interface
was returned.
E_NOINTERFACE
None of the interfaces requested in the pResults array were successfully retrieved.
CoGetInstanceFromFile()
,
CoGetInstanceFromIStorage()
,
CLSCTX
,
COSERVERINFO
,
Instance
Creation
Helper Functions
- Frees all the DLLs that have been loaded with the
CoFreeAllLibraries()
CoLoadLibrary()
function (called internally by
CoGetClassObject()
), regardless
of
whether they are currently in use.
This function is usually not called directly,
because
CoUninitialize()
and
OleUninitialize()
call it internally.
void CoFreeAllLibraries((
#include <objbase.h>
);
To unload libraries,
CoFreeAllLibraries()
uses a
list of loaded
DLLs for each process that the COM library maintains.
The
CoUninitialize()
function calls
CoFreeAllLibraries
internally, so COM applications usually have no need to call this
function
directly.
CoLoadLibrary()
,
CoFreeLibrary()
,
CoFreeUnusedLibraries()
,
CoGetClassObject()
,
CoUninitialize()
- Frees a library that, when loaded, was specified to be freed explicitly.
CoFreeLibrary()
void CoFreeLibrary((
#include <objbase.h>
HINSTANCE hInst
);
The
CoFreeLibrary
function should be called to free
a library
that is to be freed explicitly.
This is established when the library is loaded
with the
bAutoFree
parameter of
CoLoadLibrary()
set to
FALSE
.
It is
illegal to free a library explicitly when the corresponding
CoLoadLibrary
call specifies that it be freed automatically (the
bAutoFree
parameter is set to
TRUE
).
[in] Handle to the library module to be freed, as returned by
CoLoadLibrary()
.
CoFreeAllLibraries()
,
CoFreeUnusedLibraries()
,
CoLoadLibrary()
- Unloads any DLLs that are no longer in use and that, when loaded,
were
specified to be freed automatically.
CoFreeUnusedLibraries()
void CoFreeUnusedLibraries((
#include <objbase.h>
);
Applications can call
CoFreeUnusedLibraries
periodically
to free
resources.
It is most efficient to call it either at the top of a message
loop
or in some idle-time task.
DLLs that are to be freed automatically have been
loaded with the
bAutoFree
parameter of the
CoLoadLibrary()
function set to
TRUE
.
CoFreeUnusedLibraries()
internally calls
DllCanUnloadNow <
>
for
DLLs that implement
and export that function.
CoFreeLibrary()
,
CoFreeUnusedLibraries()
,
CoLoadLibrary()
,
DLLCanUnloadNow
- Provides a pointer to an interface on a class object associated with a
specified CLSID.
CoGetClassObject()
CoGetClassObject()
locates, and if
necessary, dynamically loads the executable code required to do this.
STDAPI CoGetClassObject((
#include <objbase.h>
REFCLSID rclsid,
DWORD dwClsContext,
COSERVERINFO * pServerInfo,
REFIID riid,
LPVOID * ppv
);
A class object in COM is an intermediate object that supports an
interface that permits operations common to a group of objects.
The objects
in
this group are instances derived from the same object definition represented
by
a single CLSID.
Usually, the interface implemented on a class object is
IClassFactory()
, through which you can create object instances
of a
given definition (class).
A call to
CoGetClassObject()
creates, initializes,
and gives the caller
access (through a pointer to an interface specified with the
riid
parameter) to the class object.
The class object is the one associated with
the
CLSID that you specify in the
rclsid
parameter.
The details
of how the
system locates the associated code and data within a given machine are
transparent to the caller, as is the dynamic loading of any code that is not
already loaded.
If the class context is
CLSCTX_REMOTE_SERVER
, indicating
remote activation is
required, the
COSERVERINFO
structure provided in the
pServerInfo
parameter allows you to specify the machine
on which the
server is located.
For information on the algorithm used to locate a remote
server when
pServerInfo
is
NULL
, refer
to the
CLSCTX
enumeration.
There are two places to find a CLSID for a given class:
The registry holds an association between CLSIDs and file suffixes, and between CLSIDs and file signatures for determining the class of an object.
When an object is saved to persistent storage, its CLSID is stored with its data.
To create and initialize embedded or linked COM document objects, it
is
not necessary to call
CoGetClassObject()
directly.
Instead,
call one of
the
OleCreate
or
OleCreate
XXX
helper functions.
These functions encapsulate the entire object instantiation and initialization
process, and call, among other functions,
CoGetClassObject()
.
The
riid
parameter specifies the interface the
client will use to
communicate with the class object.
In most cases, this interface is
IClassFactory()
.
This provides access to the
IClassFactory::CreateInstance()
method, through which the
caller
can then create an uninitialized object of the kind specified in its
implementation.
All classes registered in the system with a CLSID must
implement
IClassFactory()
.
In rare cases, however, you may want to specify some other interface
that
defines operations common to a set of objects.
For example, in the way COM
implements monikers, the interface on the class object is
IParseDisplayName()
, used to transform the display name
of an
object into a moniker.
The
dwClsContext
parameter specifies the execution
context, allowing one
CLSID to be associated with different pieces of code in different execution
contexts.
The
CLSCTX
enumeration, defined in
compobj.h
, specifies
the available context flags.
CoGetClassObject()
consults
(as appropriate
for the context indicated) both the registry and the class objects that are
currently registered by calling the
CoRegisterClassObject()
function.
To release a class object, use the class object's
Release()
method.
The
function
CoRevokeClassObject()
is to be used only to remove
a class
object's CLSID from the system registry.
Call
CoGetClassObject()
directly when you want to
create multiple objects
through a class object for which there is a CLSID in the system registry.
You
can also retrieve a class object from a specific remote machine.
Most class
objects implement the
IClassFactory()
interface.
You would
then call
IClassFactory::CreateInstance()
to create an uninitialized
object.
It is not always necessary to go through this process.
To create a single
object, call instead the either the
CoCreateInstanceEx()
function,
which allows you to create an instance on a remote machine.
This replaces
the
CoCreateInstance()
function, which can still be used to
create an
instance on a local machine.
Both functions encapsulate connecting to the
class
object, creating the instance, and releasing the class object.
Two other
functions,
CoGetInstanceFromFile()
and
CoGetInstanceFromIStorage()
, provide both instance creation
on a
remote system, and object activation.
COM also provides many other ways to
create an object in the form of numerous helper functions and interface methods
whose function is to create objects of a single type and provide a pointer
to
an interface on that object.
[in] CLSID associated with the data and code that you will use to create the objects.
[in] Context in which the executable code is to be run.
To enable a
remote activation,
CLSCTX_REMOTE_SERVER
must be included.
For more information
on the
context values and their use, see the
CLSCTX
enumeration.
[in] Pointer to machine on which to instantiate the class object.
May
be
NULL
, in which case the class object is instantiated on
the current machine or at the machine specified under the class's
RemoteServerName
key in the registry, according to the
interpretation of the
dwClsCtx
parameter (see
Section
Section 7.9.1
for details).
[in] Reference to the identifier of the interface, which will be supplied
in
ppv
on successful return.
This interface will be
used to communicate with the class object.
Typically this value is
IID_IClassFactory
, although other values--such as
IID_IClassFactory2
which supports a form of licensing--are
allowed.
All COM-defined interface IIDs are defined in the OLE header files
as
IID_
interfacename, where
interfacename
is the name of the interface.
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer.
S_OK
Location and connection to the specified class object was successful.
REGDB_E_CLASSNOTREG
CLSID is not properly registered. Can also indicate that the value you specified in dwClsContext is not in the registry.
E_NOINTERFACE
Either the object pointed to by
ppv
does not support
the
interface identified by
riid, or the
QueryInterface
operation on the class object returned
E_NOINTERFACE
.
REGDB_E_READREGDB
Error reading the registration database.
CO_E_DLLNOTFOUND
In-process DLL or handler DLL not found (depends on context).
CO_E_APPNOTFOUND
EXE not found (CLSCTX_LOCAL_SERVER
only).
E_ACCESSDENIED
General access failure (returned from
LoadLib/CreateProcess
).
CO_E_ERRORINDLL
EXE has error in image.
CO_E_APPDIDNTREG
EXE was launched, but it didn't register class object (may or may not have shut down).
CoCreateInstanceEx()
,
CoRegisterClassObject()
,
CoRevokeClassObject()
,
CLSCTX
,
Creating an Object through a Class Object
- Creates a new object and initializes it from a file using
CoGetInstanceFromFile()
IPersistFile::Load()
.
HRESULT CoGetInstanceFromFile((
#include <objbase.h>
COSERVERINFO * pServerInfo,
CLSID * pclsid,
Iunknown * punkOuter,
DWORD dwClsCtx,
OLECHAR * szName,
ULONG cmq,
MULTI_QI * rgmqResults
);
CoGetInstanceFromFile()
creates a new object and
initializes it from a
file using
IPersistFile::Load()
.
The result of this function
is similar to
creating an instance with a call to
CoCreateInstanceEx()
,
followed
by an initializing call to
IPersistFile::Load()
, with the
following
important distinctions:
Fewer network round trips are required by this function when instantiating an object on a remote machine.
In the case where
dwClsCtx
is set to
CLSCTX_REMOTE_SERVER
and
pServerInfo
is
NULL
, if the class
is registered with the
ActivateAtStorage
sub-key or has no associated registry
information, this function will instantiate an object on the machine where
szName
resides, providing the least possible network
traffic.
For
example, if
szName
specified ``\\myserver\users\johndo\file'',
the object
would be instantiated on the ``myserver'' machine, and the object would access
the file directly.
[in] Pointer to a
COSERVERINFO
structure that specifies
the machine on which to instantiate the object and the authentication setting
to be used.
May be
NULL
, in which case the object is
instantiated (1) on the current machine, (2) at the machine specified under
the
RemoteServerName
named-value for the class in the
registry, or (3) at the machine where the
szName
file
resides if the
ActivateAtStorage
named-value is specified
for the class in the registry or there is no local registry information.
[in] Pointer to the class of the object to create.
May be
NULL
,
in which case there is a call to
GetClassFile()
, using
szName
as its parameter to get the class of the object
to
be instantiated.
[in] When non-NULL
, indicates the instance is being
created
as part of an aggregate, and
punkOuter
is to be used
as the
pointer to the new instance's controlling
IUnknown()
.
Aggregation is currently not supported cross-process or cross-machine.
When
instantiating an object out of process,
CLASS_E_NOAGGREGATION
will be returned if
punkOuter
is non-NULL
.
[in] Values taken from the
CLSCTX
enumeration.
[in] Flags specifying how the file is to be opened.
Values are taken
from the
STGM
enumeration.
[in] File to initialize the object with using
IPersistFile::Load()
.
May not be
NULL
.
[in] Number of
MULTI_QI
structures in
rgmqResults.
Must be greater than zero.
[in] Array of
MULTI_QI
structures.
Each structure
has three
members: the identifier for a requested interface (pIID
),
the
location to return the interface pointer (pItf) and the
return value of the call to
QueryInterface()
(hr).
This function supports the standard return value
E_INVALIDARG
, as well
as the following:
S_OK
Indicates success.
CO_S_NOTALLINTERFACES
At least one, but not all of the interfaces requested in the
rgmqResults
array were successfully retrieved.
The
hr
field of each of the
MULTI_QI
structures in
rgmqResults
indicates with
S_OK
or
E_NOINTERFACE
whether or not the specific interface
was returned.
E_NOINTERFACE
None of the interfaces requested in the rgmqResults array were successfully retrieved.
CoCreateInstanceEx()
,
CoGetInstanceFromIStorage()
,
CLSCTX
,
Instance Creation Helper Functions
- Creates a new object and initializes it from a storage object
through an
internal call to
CoGetInstanceFromIStorage()
IPersistStorage::Load()
.
HRESULT CoGetInstanceFromIStorage((
#include <objbase.h>
COSERVERINFO * pServerInfo,
CLSID * pclsid,
Iunknown * punkOuter,
DWORD dwClsCtx,
Istorage * pstg,
ULONG cmq,
MULTI_QI * rgmqResults
);
CoGetInstanceFromIStorage()
creates a new object
and initializes it from a
storage object through a call to
IPersistStorage::Load()
.
This
function is similar to creating an instance using
CoCreateInstanceEx()
followed by a call to
IPersistStorage::Load
, with the following important distinctions:
Fewer network round trips are required by this function when instantiating remotely.
In the case where
dwClsCtx
is set to
CLSCTX_REMOTE_SERVER
and
pServerInfo
is
NULL
, if the class
is registered with the
ActivateAtStorage
named value or has no associated registry
information, this function will instantiate an object on the same machine
where
the storage object pointed to by
pstg
resides, providing
the least
possible network traffic.
For example, if
pstg
were obtained
through a
call to
StgCreateDocfile()
, specifying
``\\myserver\users\johndo\file'', the object would be instantiated on the
``myserver'' machine, and the object would access the storage object directly.
[in] Pointer to a
COSERVERINFO
structure that specifies
the machine on which to instantiate the object and the authentication setting
to be used.
May be
NULL
, in which case the object is either
instantiated (1) on the current machine, (2) at the machine specified under
the
RemoteServerName
named-value for the class in the registry,
or (3) at the machine where the storage object pointed to by
pstg
is located if the class is registered with
ActivateAtStorage
specified or has no local registry information.
[in] Pointer to the class identifier (CLSID) of the object to be created.
May
be
NULL
, in which case there is a call to
IStorage:Stat
to find the class of the object.
[in] When non-NULL
, indicates the instance is being
created
as part of an aggregate, and
punkOuter
is to be used
as the
pointer to the new instance's controlling
IUnknown()
.
Aggregation
is currently not supported cross-process or cross-machine.
When instantiating
an
object out of process,
CLASS_E_NOAGGREGATION
will be returned
if
punkOuter
is non-NULL
.
Values taken from the
CLSCTX
enumeration.
Pointer to storage to initialize the object with using
IPersistStorage::Load()
.
May not be
NULL
.
Number of
MULTI_QI
structures in
rgmqResults.
Must be greater than zero.
Array of
MULTI_QI
structures.
Each structure has
three
members: the identifier for a requested interface (pIID
),
the location to return the interface pointer (pItf) and
the return value of the call to
QueryInterface()
(hr).
This function supports the standard return value
E_INVALIDARG
, as well
as the following:
S_OK
Indicates success.
CO_S_NOTALLINTERFACES
At least one, but not all of the interfaces requested in the
rgmqResults
array were successfully retrieved.
The
hr
field of each of the
MULTI_QI
structures
in
rgmqResults
indicates with
S_OK
or
E_NOINTERFACE
whether the specific interface pointer
was retrieved.
E_NOINTERFACE
None of the interfaces requested in the rgmqResults array were successfully retrieved.
CoCreateInstanceEx()
,
CoGetInstanceFromFile()
,
CLSCTX
,
Instance Creation Helper Functions
- Returns the CLSID of an object that can emulate the specified object.
CoGetTreatAsClass()
HRESULT CoGetTreatAsClass((
#include <objbase.h>
REFCLSID clsidOld,
LPCLSID pclsidNew
);
CoGetTreatAsClass()
returns the
TreatAs
entry in the registry for
the specified object.
The
TreatAs
entry, if set, is the
CLSID of a
registered object (an application) that can emulate the object in question.
The
TreatAs
entry is set through a call to the
CoTreatAsClass()
function.
Emulation allows an application to open and edit an object of a
different format, while retaining the original format of the object.
Objects
of
the original CLSID are activated and treated as objects of the second CLSID.
When the object is saved, this may result in loss of edits not supported by
the
original format.
If there is no
TreatAs
entry for the specfied
object,
this function returns the CLSID of the original object (clsidOld).
[in] CLSID of the object that can be emulated (treated as) an object with a different CLSID.
[out] Pointer to where the CLSID that can emulate
clsidOld
objects
is retrieved.
This parameter cannot be
NULL
.
If there is
no emulation
information for
clsidOld
objects, the
clsidOld
parameter is supplied.
S_OK
A new CLSID was successfully returned.
S_FALSE
No emulation information for the clsidOld parameter and the pclsidNew parameter is set to clsidOld.
REGDB_E_READREGDB
An error reading the registry.
This function can also return any of the error values returned by the
CLSIDFromString()
function.
- Determines whether a remote object is connected to the corresponding in-process
object.
CoIsHandlerConnected()
BOOL CoIsHandlerConnected((
#include <objbase.h>
LPUNKNOWN pUnk
);
The
CoIsHandlerConnected()
function determines the
status of a
remote object.
You can use it to determine when to release a remote object.
You
specify the remote object by giving the function a pointer to its controlling
IUnknown()
interface (the
pUnk
parameter).
A
TRUE
returned from the
function indicates either that the specified object is not remote, or that
it
is remote and is still connected to its remote handler.
A
FALSE
returned from
the function indicates that the object is remote but is no longer connected
to
its remote handler; in this case, the caller should respond by releasing the
object.
[in] Pointer to the controlling
IUnknown()
interface
on the remote object.
TRUE
The object is not remote or that it is remote and is still connected to its remote handler.
FALSE
The object is remote and is invalid (no longer connected to its remote handler).
- Loads a specific DLL into the caller's process.
The
CoLoadLibrary()
CoGetClassObject()
function calls
CoLoadLibrary()
internally; applications
should not call
it directly.
HINSTANCE CoLoadLibrary((
#include <objbase.h>
LPOLESTR lpszLibName,
BOOL bAutoFree
);
The
CoLoadLibrary()
function is called internally
by the
CoGetClassObject()
function when the class context (CLSCTX
)
indicates a DLL.
CoLoadLibrary()
loads a DLL specified
by the
lpszLibName
parameter into the process that called
CoGetClassObject()
.
Containers should not call
CoLoadLibrary()
directly.
Internally, a reference count is kept on the loaded DLL, by using
CoLoadLibrary()
to increment the count and the
CoFreeLibrary
function to decrement it.
[in] Pointer to the name of the library to be loaded.
The use of this
name is
the same as in the Win32 function
LoadLibrary
.
[in] If
TRUE
, indicates that this library is freed
when it
is no longer needed, through a call to either the
CoFreeUnusedLibraries()
or
CoUninitialize()
functions.
If
FALSE
,
the library
should be explicitly freed with the
CoFreeLibrary()
function.
Handle of the loaded library.
NULL
Library could not be loaded.
CoFreeAllLibraries()
,
CoFreeLibrary()
,
CoFreeUnusedLibraries()
,
CoGetClassObject()
- Establishes or removes an emulation, in which objects of one class are treated
as objects of a different class.
CoTreatAsClass()
STDAPI CoTreatAsClass((
#include <objbase.h>
REFCLSID clsidOld,
REFCLSID clsidNew
);
This function sets the
TreatAs
entry in the registry
for the
specified object, allowing the object to be emulated by another application.
Emulation allows an application to open and edit an object of a different
format, while retaining the original format of the object.
After this entry
is
set, whenever any function like
CoGetClassObject()
specifies
the
object's original CLSID (clsidOld), it is transparently
forwarded to the
new CLSID (clsidNew), thus launching the application
associated with the
TreatAs
CLSID.
When the object is saved, it can be saved
in its native
format, which may result in loss of edits not supported by the original format.
You would call
CoTreatAsClass()
in two situations
if your application
supports emulation:
In response to an end-user request (through a conversion dialog box) that a specified object be treated as an object of a different class (an object created under one application be run under another application, while retaining the original format information).
In a setup program, to register that one class of objects be treated as objects of a different class.
An example of the first case is that an end user might wish to edit
a
spreadsheet created by one application using a different application that
can
read and write the spreadsheet format of the original application.
For an
application that supports emulation,
CoTreatAsClass()
can
be called to
implement a Treat As option in a conversion dialog box.
An example of the use of
CoTreatAsClass()
in a setup
program would be in
an updated version of an application.
When the application is updated, the
objects created with the earlier version can be activated and treated as
objects of the new version, while retaining the previous format information.
This would allow you to give the user the option to convert when they save,
or
to save it in the previous format, possibly losing format information not
available in the older version.
To ensure that existing emulation information is removed when you install
an
application, your setup programs should call
CoTreatAsClass()
,
setting the
clsidNew
parameter to
CLSID_NULL
to
remove any existing emulation for
the classes they install.
If there is no CLSID assigned to the
AutoTreatAs
key in the registry,
setting
clsidNew
and
clsidOld
to
the same value removes the
TreatAs
entry, so there is no emulation.
If there is a
CLSID assigned to
the
AutoTreatAs
key, that CLSID is assigned to the
TreatAs
key.
The
CoTreatAsClass()
function does not validate whether
an appropriate
registry entry for
clsidNew
currently exists.
[in] CLSID of the object to be emulated.
[in] CLSID of the object that should emulate the original object.
This
replaces any existing emulation for
clsidOld.
Can be
CLSID_NULL
, in which case any existing emulation for
clsidOld
is removed.
This function supports the standard return value
E_INVALIDARG
, as well
as the following:
S_OK
The emulation was successfully established or removed.
REGDB_E_CLASSNOTREG
The clsidOld parameter is not properly registered in the registration database.
REGDB_E_READREGDB
Error reading from registration database.
REGDB_E_WRITEREGDB
Error writing to registration database.
To optimize network performance, most remote activation functions take
an
array of
MULTI_QI
structures rather than just a single
IID as input and a
single pointer to the requested interface on the object as output, as do
local machine activation functions.
This allows a set of pointers to interfaces
to be returned from the same object in a single round-trip to the server.
In
network scenarios, requesting multiple interfaces at the time of object
construction can save considerable time over using a number of calls to the
QueryInterface()
method for unique interfaces, each of
which would require a round-trip to the server.
typedef struct _MULTI_QI {
const IID* pIID;
IUnknown()
* pItf;
HRESULT hr;
} MULTI_QI;
[in] Pointer to an interface identifier.
[out] Pointer to the interface requested in
pIID.
Must be set to
NULL
on entry.
[out] Return value of the
QueryInterface()
call made
to satisfy the
request for the interface requested in
pIID.
Common return
values
are
S_OK
and
E_NOINTERFACE
.
Must be
set to zero
on entry.
CoGetInstanceFromFile()
,
CoGetInstanceFromIStorage()
,
CoCreateInstanceEx()
Values from the
CLSCTX
enumeration are used in activation
calls to indicate the
execution contexts in which an object is to be run.
These values are also
used
in calls to
CoRegisterClassObject()
to indicate the set
of
execution contexts in which a class object is to be made available for requests
to construct instances.
typedef enum tagCLSCTX { CLSCTX_INPROC_SERVER = 1, CLSCTX_INPROC_HANDLER = 2, CLSCTX_LOCAL_SERVER = 4 CLSCTX_REMOTE_SERVER = 16 } CLSCTX; #define CLSCTX_SERVER (CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER) #define CLSCTX_ALL (CLSCTX_INPROC_HANDLER | CLSCTX_SERVER)
CLSCTX_INPROC_SERVER
The code that creates and manages objects of this class runs in the same process as the caller of the function specifying the class context.
CLSCTX_INPROC_HANDLER
The code that manages objects of this class is an in-process handler. This is a DLL that runs in the client process and implements client-side structures of this class when instances of the class are accessed remotely.
CLSCTX_LOCAL_SERVER
The EXE code that creates and manages objects of this class is loaded in a separate process space (runs on same machine but in a different process).
CLSCTX_REMOTE_SERVER
A remote machine context.
The
LocalServer32
or
LocalService
code that creates and manages objects of this
class is run on a different machine.
CLSCTX_SERVER
Indicates server code, whether in-process, local, or remote.
This definition
ORs
CLSCTX_INPROC_SERVER
,
CLSCTX_LOCAL_SERVER
,
and
CLSCTX_REMOTE_SERVER
.
CLSCTX_ALL
Indicates all class contexts.
This definition ORs
CLSCTX_INPROC_HANDLER
and
CLSCTX_SERVER
.
Values from the
CLSCTX
enumeration are used in activation
calls
(CoCreateInstance()
,
CoCreateInstanceEx()
,
CoGetClassObject()
, etc.) to indicate the preferred execution
context--in-process, local, or remote--in which an object is to
be run.
They
are also used in calls to
CoRegisterClassObject()
to indicate
the set of
execution contexts in which a class object is to be made available for requests
to construct instances (IClassFactory::CreateInstance()
).
To indicate that more than one context is acceptable, you can string multiple values together with Boolean ORs. The contexts are tried in the order in which they are listed.
The following table shows how other COM functions and methods that call
CoGetClassObject()
use the
CLSCTX
values:
Function Called
|
Context Flag
Used
|
CoUnMarshalInterface
|
CLSCTX_INPROC_HANDLER Unmarshaling needs the form of the class designed for remote access. |
IMoniker::BindToObject() ,
for a file moniker created through a call to
CreateFileMoniker
|
In this case, uses CLSCTX_SERVER internally
to
create the instance after calling
GetClassFile()
to determine
the class to be instantiated. |
The
CLSCTX_REMOTE_SERVER
value is added to the
CLSCTX
enumeration for distributed COM.
The
CLSCTX_SERVER
and
CLSCTX_ALL
constants are further updated to include the
CLSCTX_REMOTE_SERVER
value.
Given a set of
CLSCTX
flags,
dwClsCtx,
the
execution context to be used depends on the availability of registered class
codes and
other parameters according to the following algorithm:
The first part of the processing determines whether
CLSCTX_REMOTE
SERVER
should be specified as follows:
If the call specifies either
an explicit
COSERVERINFO
structure indicating
a machine
different from the current machine, or
there is no explicit
COSERVERINFO
structure
specified, but the specified
class is registered with either the
RemoteServerName
or
ActivateAtStorage
named-value.
then
CLSCTX_REMOTE_SERVER
is implied and is added
to
dwClsCtx.
The
second case allows applications written prior to the release of distributed
COM
to be the configuration of classes for remote activation to be used by client
applications available prior to DCOM and the
CLSCTX_REMOTE_SERVER
flag.
The
cases in which there would be no explicit
COSERVERINFO
structure are:
The value is specified as
NULL
It is not one of the function parameters,
as would be the case in calls to
CoCreateInstance()
or
CoGetClassObject()
in existing applications
If the explicit
COSERVERINFO
parameter
indicates the current machine,
CLSCTX_REMOTE_SERVER
is removed (if present) from
dwClsCtx.
The rest of the processing proceeds by looking at the value(s) of dwClsCtx in the following sequence.
If
dwClsCtx
includes
CLSCTX_REMOTE_SERVER
and no
COSERVERINFO
parameter is specified, if the activation request indicates a persistent state
from which to initialize the object (with
CoGetInstanceFromFile()
,
CoGetInstanceFromIStorage()
, or, for a file moniker, in
a call to
IMoniker::BindToObject()
) and the class has an
ActivateAtStorage
sub-key
or no class registry information whatsoever,
the request to
activate and initialize is forwarded to the machine where the persistent state
resides.
(Refer to the remote activation functions listed in the See Also
section for details.)
If
dwClsCtx
includes
CLSCTX_INPROC_SERVER
, the class code in the DLL
found under the class's
InprocServer32
key is used if this
key
exists.
The class code will run within the same process as the caller.
If
dwClsCtx
includes
CLSCTX_INPROC_HANDLER
, the class code in the DLL found
under the class's
InprocHandler32
key is used if this key
exists.
The class code will run within the same process as the caller.
If
dwClsCtx
includes
CLSCTX_LOCAL_SERVER
, the class code in the
Win32 service found under the class's
LocalService
key
is used if
this key exists.
If no Win32 service is specified, but an EXE is specified
under that same key, the class code associated with that EXE is used.
The
class
code (in either case) will be run in a separate service process on the same
machine as the caller.
If
dwClsCtx
is set to
CLSCTX_REMOTE_SERVER
and an additional
COSERVERINFO
parameter to the function specifies a particular
remote machine, a
request to activate is forwarded to this remote machine with
dwClsCtx
modified to be
CLSCTX_LOCAL_SERVER
.
The class code will
run in its own process
on this specific machine, which must be different from that of the caller.
Finally, if
dwClsCtx
includes
CLSCTX_REMOTE_SERVER
and no
COSERVERINFO
parameter is specified, if a machine name
is given under the
class's
RemoteServerName
named-value, the request to activate
is
forwarded to this remote machine with
dwClsCtx
modified
to be
CLSCTX_LOCAL_SERVER
.
The class code will run in its own
process on this
specific machine, which must be different from that of the
caller.
CoCreateInstance()
,
CoGetClassObject()
,
CoRegisterClassObject()
,
CoGetInstanceFromFile()
,
CoGetInstanceFromIStorage()
,
CoCreateInstanceEx()
,
COSERVERINFO
structure, Creating an Object through a Class
Object, Registering a Running EXE Server