7    COM Clients

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:

  1. Identify the class of object to use.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

7.1    Getting a Pointer to an Object

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:

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.

7.1.1    Creating an Object through a Class Object

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.

7.1.2    COM Class Objects and CLSIDs

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 than CoGetClassObject() (such as CoCreateInstanceEx()) require that the server support IClassFactory().

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().

7.1.3    Locating a Remote Object

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.

7.1.4    Instance Creation Helper Functions

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 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.

7.2    Initializing the Object

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.

7.3    Managing the Object

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.

7.4    Releasing the Object

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.

7.5    Server Management

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.

7.6    Client Related Interface Descriptions

7.6.1    ICatInformation

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.

When to Implement

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().

When to Use

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.

Table 7-1:  ICatInformation Methods in VTable Order

IUnknown() Methods Description
QueryInterface() Returns pointers to supported interfaces.
AddRef() Increments reference count.
Release() Decrements reference count.

Table 7-2:  ICatInformation Methods

ICatInformation() Methods 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.

See Also

ICatRegister()  

ICatInformation::EnumCategories()

NAME

ICatInformation::EnumCategories() - Returns an enumerator for the component categories registered on the system.

Synopsis

#include <comcat.h>

HRESULT EnumCategories(
        LCID lcid,
        IEnumCATEGORYINFO ** ppenumCatInfo );

Parameters

lcid

[in] Identifies the requested locale for any return szDescription of the enumerated CATEGORYINFOs. Typically, the caller specifies GetUserDefaultLCID() for this parameter.

ppenumCatInfo

[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.

Return Values

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.

See Also

ICatInformation::EnumClassesOfCategories(), ICatInformation::EnumImplCategoriesOfClass(), ICatInformation::EnumReqCategoriesOfClass(), ICatInformation::GetCategoryDesc(), ICatInformation::IsClassOfCategories()  

ICatInformation::EnumClassesOfCategories()

NAME

ICatInformation::EnumClassesOfCategories() - 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.

Synopsis

#include <comcat.h>

HRESULT EnumClassesOfCategories(
        ULONG cImplemented,
        CATID rgcatidImpl,
        ULONG cRequired,
        CATID rgcatidReq,
        IEnumCLSID ** ppenumCLSID );

Parameters

cImplemented

[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.

rgcatidImpl

[in] An array of category identifiers.

cRequired

[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.

rgcatidReq

[in] An array of category identifiers.

ppenumCLSID

[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.

Return Values

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.

See Also

ICatInformation::EnumCategories(), ICatInformation::EnumImplCategoriesOfClass(), ICatInformation::EnumReqCategoriesOfClass(), ICatInformation::GetCategoryDesc(), ICatInformation::IsClassOfCategories()  

ICatInformation::EnumImplCategoriesOfClass()

NAME

ICatInformation::EnumImplCategoriesOfClass() - Returns an enumerator for the CATIDs implemented by the specified class.

Synopsis

#include <comcat.h>

HRESULT EnumImplCategoriesOfClass(
        REFCLSID rclsid,
        IEnumCATID ** ppenumCATD );

Parameters

rclsid

[in] Specifies the class ID.

ppenumCATD

[out] Specifies the location to return an IEnumCATID interface. This can be used to enumerate the CATIDs that are implemented by rclsid.

Return Values

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.

See Also

ICatInformation::EnumCategories(), ICatInformation::EnumClassesOfCategories(), ICatInformation::EnumReqCategoriesOfClass(), ICatInformation::GetCategoryDesc(), ICatInformation::IsClassOfCategories()  

ICatInformation::EnumReqCategoriesOfClass()

NAME

ICatInformation::EnumReqCategoriesOfClass() - Returns an enumerator for the CATIDs required by the specified class.

Synopsis

#include <comcat.h>

HRESULT EnumReqCategoriesOfClass(
        REFCLSID rclsid,
        IEnumCATID ** ppenumCATD );

Parameters

rclsid

[in] Specifies the class ID.

ppenumCATD

[out] Specifies the location to return an IEnumCATID interface. This can be used to enumerate the CATIDs that are required by rclsid.

Return Values

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.

See Also

ICatInformation::EnumCategories(), ICatInformation::EnumClassesOfCategories(), ICatInformation::EnumImplCategoriesOfClass(), ICatInformation::GetCategoryDesc(), ICatInformation::IsClassOfCategories()  

ICatInformation::GetCategoryDesc()

NAME

ICatInformation::GetCategoryDesc() - Retrieves the localized description string for a specific category ID.

Synopsis

#include <comcat.h>

HRESULT GetCategoryDesc(
        REFCATID rcatid,
        LCID lcid,
        PWCHAR * ppszDesc );

Parameters

rcatid

[in] Identifies the category for which the description string is to be returned.

lcid

[in] Specifies the locale in which the resulting string is returned.

ppszDesc

[out] A pointer to the string pointer that contains the description. This must be released by the caller using CoMemTaskFree.

Return Values

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.

See Also

ICatInformation::EnumCategories(), ICatInformation::EnumClassesOfCategories(), ICatInformation::EnumImplCategoriesOfClass(), ICatInformation::EnumReqCategoriesOfClass(), ICatInformation::IsClassOfCategories()  

ICatInformation::IsClassOfCategories()

NAME

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.

Synopsis

#include <comcat.h>

HRESULT IsClassOfCategories(
        REFCLSID rclsid,
        ULONG cImplemented,
        CATID rgcatidImpl,
        ULONG cRequired,
        CATID rgcatidReq );

Parameters

rclsid

[in] The class ID of the relevent class to query.

cImplemented

[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.

rgcatidImpl

[in] An array of category identifiers.

cRequired

[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.

rgcatidReq

[in] An array of category identifiers.

Return Values

S_OK

rclsid is of category rcatid.

S_FALSE

rclsid is not of category rcatid.

See Also

ICatInformation::EnumCategories(), ICatInformation::EnumClassesOfCategories(), ICatInformation::EnumImplCategoriesOfClass(), ICatInformation::EnumReqCategoriesOfClass(), ICatInformation::GetCategoryDesc()

7.6.2    IMultiQI

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.

When to Implement

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 to Use

When more than one interface pointer is sought, client applications should QueryInterface() for IMultiQI() and use it if available.

Table 7-3:  IMultiQI Methods in VTable Order

IUnknown Methods Description
QueryInterface() Returns pointers to supported interfaces.
AddRef() Increments reference count.
Release() Decrements reference count.

Table 7-4:  IMultiQI Methods

IMultiQI Methods Description
QueryMultipleInterfaces Queries for multiple interfaces.

See Also

IUnknown::QueryInterface()  

IMultiQI::QueryMultipleInterfaces()

NAME

IMultiQI::QueryMultipleInterfaces() - Fills a caller-provided array of structures with pointers to multiple interfaces. Calling this method is equivalent to issuing a series of separate 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.

Synopsis

#include <objidl.h>

HRESULT QueryMultipleInterfaces(
        ULONG cMQIs,
        MULTI_QI * pMQIs );

Description

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().

Parameters

cMQIs

[in] Pointer to the number of elements in an array of MULTI_QI structures, each of which contains the IID of a single interface.

pMQIs

[in, out] Pointer to the first MULTI_QI strucutre in the array.

Return Values

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.

See Also

IUnknown()

7.7    Client Related API Descriptions

 

CoCreateInstance()

NAME

CoCreateInstance() - Creates a single uninitialized object of the class associated with a specified CLSID. Call 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.

Synopsis

#include <objbase.h>

STDAPI CoCreateInstance((
        REFCLSID rclsid,
        LPUNKNOWN pUnkOuter,
        DWORD dwClsContext,
        REFIID riid,
        LPVOID * ppv );

Description

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.

Parameters

rclsid

[in] CLSID associated with the data and code that will be used to create the object.

pUnkOuter

[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()).

dwClsContext

[in] Context in which the code that manages the newly created object will run. The values are taken from the enumeration CLSCTX.

riid

[in] Reference to the identifier of the interface to be used to communicate with the object.

ppv

[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer.

Return Values

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.

See Also

CoGetClassObject(), IClassFactory::CreateInstance(), CoCreateInstanceEx(), CLSCTX, Instance Creation Helper Functions  

CoCreateInstanceEx()

NAME

CoCreateInstanceEx() - Creates an instance of a specific class on a specific machine.

Synopsis

#include <objbase.h>

HRESULT CoCreateInstanceEx((
        REFCLSID rclsid,
        IUnknown * punkOuter,
        DWORD dwClsCtx,
        COSERVERINFO * pServerInfo,
        ULONG cmq,
        MULTI_QI pResults );

Description

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.

Parameters

rclsid

[in] CLSID of the object to be created.

punkOuter

[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.

dwClsCtx

[in] Values taken from the CLSCTX enumeration.

pServerInfo

[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).

cmq

[in] Number of MULTI_QI structures in pResults. Must be greater than zero.

pResults

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).

Return Values

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.

See Also

CoGetInstanceFromFile(), CoGetInstanceFromIStorage(), CLSCTX, COSERVERINFO, Instance Creation Helper Functions  

CoFreeAllLibraries()

NAME

CoFreeAllLibraries() - Frees all the DLLs that have been loaded with the 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.

Synopsis

#include <objbase.h>

void CoFreeAllLibraries((
         );

Description

To unload libraries, CoFreeAllLibraries() uses a list of loaded DLLs for each process that the COM library maintains. The CoUninitialize() function calls CoFreeAllLibrariesinternally, so COM applications usually have no need to call this function directly.

See Also

CoLoadLibrary(), CoFreeLibrary(), CoFreeUnusedLibraries(), CoGetClassObject(), CoUninitialize()  

CoFreeLibrary()

NAME

CoFreeLibrary() - Frees a library that, when loaded, was specified to be freed explicitly.

Synopsis

#include <objbase.h>

void CoFreeLibrary((
        HINSTANCE hInst );

Description

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 CoLoadLibrarycall specifies that it be freed automatically (the bAutoFree parameter is set to TRUE).

Parameters

hInst

[in] Handle to the library module to be freed, as returned by CoLoadLibrary().

See Also

CoFreeAllLibraries(), CoFreeUnusedLibraries(), CoLoadLibrary()  

CoFreeUnusedLibraries()

NAME

CoFreeUnusedLibraries() - Unloads any DLLs that are no longer in use and that, when loaded, were specified to be freed automatically.

Synopsis

#include <objbase.h>

void CoFreeUnusedLibraries((
         );

Description

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.

See Also

CoFreeLibrary(), CoFreeUnusedLibraries(), CoLoadLibrary(), DLLCanUnloadNow  

CoGetClassObject()

NAME

CoGetClassObject() - Provides a pointer to an interface on a class object associated with a specified CLSID. CoGetClassObject() locates, and if necessary, dynamically loads the executable code required to do this.

Synopsis

#include <objbase.h>

STDAPI CoGetClassObject((
        REFCLSID rclsid,
        DWORD dwClsContext,
        COSERVERINFO * pServerInfo,
        REFIID riid,
        LPVOID * ppv );

Description

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:

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 OleCreateXXX 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.

Parameters

rclsid

[in] CLSID associated with the data and code that you will use to create the objects.

dwClsContext

[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.

pServerInfo

[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).

riid

[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.

ppv

[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer.

Return Values

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).

See Also

CoCreateInstanceEx(), CoRegisterClassObject(), CoRevokeClassObject(), CLSCTX, Creating an Object through a Class Object  

CoGetInstanceFromFile()

NAME

CoGetInstanceFromFile() - Creates a new object and initializes it from a file using IPersistFile::Load().

Synopsis

#include <objbase.h>

HRESULT CoGetInstanceFromFile((
        COSERVERINFO * pServerInfo,
        CLSID * pclsid,
        Iunknown * punkOuter,
        DWORD dwClsCtx,
        OLECHAR * szName,
        ULONG cmq,
        MULTI_QI * rgmqResults );

Description

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:

Parameters

pServerInfo

[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.

pclsid

[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.

punkOuter

[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.

dwClsCtx

[in] Values taken from the CLSCTX enumeration.

grfMode

[in] Flags specifying how the file is to be opened. Values are taken from the STGM enumeration.

szName

[in] File to initialize the object with using IPersistFile::Load(). May not be NULL.

cmq

[in] Number of MULTI_QI structures in rgmqResults. Must be greater than zero.

rgmqResults

[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).

Return Values

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.

See Also

CoCreateInstanceEx(), CoGetInstanceFromIStorage(), CLSCTX, Instance Creation Helper Functions  

CoGetInstanceFromIStorage()

NAME

CoGetInstanceFromIStorage() - Creates a new object and initializes it from a storage object through an internal call to IPersistStorage::Load().

Synopsis

#include <objbase.h>

HRESULT CoGetInstanceFromIStorage((
        COSERVERINFO * pServerInfo,
        CLSID * pclsid,
        Iunknown * punkOuter,
        DWORD dwClsCtx,
        Istorage * pstg,
        ULONG cmq,
        MULTI_QI * rgmqResults );

Description

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:

Parameters

pServerInfo

[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.

pclsid

[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.

punkOuter

[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.

dwClsCtx

Values taken from the CLSCTX enumeration.

pstg

Pointer to storage to initialize the object with using IPersistStorage::Load(). May not be NULL.

cmq

Number of MULTI_QI structures in rgmqResults. Must be greater than zero.

rgmqResults

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).

Return Values

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.

See Also

CoCreateInstanceEx(), CoGetInstanceFromFile(), CLSCTX, Instance Creation Helper Functions  

CoGetTreatAsClass()

NAME

CoGetTreatAsClass() - Returns the CLSID of an object that can emulate the specified object.

Synopsis

#include <objbase.h>

HRESULT CoGetTreatAsClass((
        REFCLSID clsidOld,
        LPCLSID pclsidNew );

Description

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).

Parameters

clsidOld

[in] CLSID of the object that can be emulated (treated as) an object with a different CLSID.

pclsidNew

[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.

Return Values

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.

See Also

CoTreatAsClass()  

CoIsHandlerConnected()

NAME

CoIsHandlerConnected() - Determines whether a remote object is connected to the corresponding in-process object.

Synopsis

#include <objbase.h>

BOOL CoIsHandlerConnected((
        LPUNKNOWN pUnk );

Description

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.

Parameters

pUnk

[in] Pointer to the controlling IUnknown() interface on the remote object.

Return Values

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).

 

CoLoadLibrary()

NAME

CoLoadLibrary() - Loads a specific DLL into the caller's process. The CoGetClassObject() function calls CoLoadLibrary() internally; applications should not call it directly.

Synopsis

#include <objbase.h>

HINSTANCE CoLoadLibrary((
        LPOLESTR lpszLibName,
        BOOL bAutoFree );

Description

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 CoFreeLibraryfunction to decrement it.

Parameters

lpszLibName

[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.

bAutoFree

[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.

Return Values

Module Handle

Handle of the loaded library.

NULL

Library could not be loaded.

See Also

CoFreeAllLibraries(), CoFreeLibrary(), CoFreeUnusedLibraries(), CoGetClassObject()  

CoTreatAsClass()

NAME

CoTreatAsClass() - Establishes or removes an emulation, in which objects of one class are treated as objects of a different class.

Synopsis

#include <objbase.h>

STDAPI CoTreatAsClass((
        REFCLSID clsidOld,
        REFCLSID clsidNew );

Description

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:

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.

Parameters

clsidOld

[in] CLSID of the object to be emulated.

clsidNew

[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.

Return Values

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.

See Also

CoGetTreatAsClass()

7.8    COM Client Related Structure Definitions

7.8.1    MULTI_QI

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;

Members

pIID

[in] Pointer to an interface identifier.

pItf

[out] Pointer to the interface requested in pIID. Must be set to NULL on entry.

hr

[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.

See Also

CoGetInstanceFromFile(), CoGetInstanceFromIStorage(), CoCreateInstanceEx()

7.9    COM Client Related Enumeration Definitions

7.9.1    CLSCTX

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)

Elements

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.

Defined Terms

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.

Description

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:

Table 7-5:  COM Use of 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:

  1. If the call specifies either

    1. an explicit COSERVERINFO structure indicating a machine different from the current machine, or

    2. 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:

  2. 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.

  1. 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.)

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

See Also

CoCreateInstance(), CoGetClassObject(), CoRegisterClassObject(), CoGetInstanceFromFile(), CoGetInstanceFromIStorage(), CoCreateInstanceEx(), COSERVERINFO structure, Creating an Object through a Class Object, Registering a Running EXE Server