8    COM Servers

As described in earlier chapters, a COM Server is some module of code, a DLL or an EXE, that implements one or more object classes (each with their own CLSID). A COM server structures the object implementations such that COM clients can create and use objects from the server using the CLSID to identify the object through the processes described in Chapter 7.

In addition, COM servers themselves may be clients of other objects, usually when the server is using those other objects to help implement part of its own objects. This chapter will cover the various methods of using an object as part of another through the mechanisms of containment and aggregation.

Another feature that servers might support is the ability to emulate a different server of a different CLSID. The COM Library provides a few API functions to support this capability that are covered at the end of this chapter.

8.1    COM Server Responsibilities

One of the most important ways for a client to get a pointer to an object is for the client to ask that a server be launched, and that an instance of the object provided by the server be created and activated. It is the responsibility of the server to ensure that this happens properly. There are several important parts to this.

The server must implement code for a class object through an implementation of either the IClassFactory() or IClassFactory2() interface.

The server must register its CLSID in the system registry on the machine on which it resides, and further, has the option of publishing its machine location to other systems on a network to allow clients to call it without requiring the client to know the server's location.

The server is primarily responsible for security--that is, for the most part, the server determines whether it will provide a pointer to one of its objects to a client.

In-process servers should implement and export certain functions that allow the client process to instantiate them.

8.2    Implementing IClassFactory

When a client uses a CLSID to request the creation of an object instance, the first step is creation of a class object, an intermediate object that contains an implementation of the methods of the IClassFactory() interface. While COM provides several instance creation functions, the first step in the implementation of these functions is the creation of a class object.

As a result, all servers must implement the methods of the IClassFactory() interface. This interface contains two methods: CreateInstance and LockServer. CreateInstance must create an uninitialized instance of the object, and return a pointer to a requested interface on the object.

The LockServer method just increments the reference count on the class object to ensure that the server stays in memory, and does not shut down before the client is ready for it to do so.

To enable a server to be responsible for its own licensing, COM defines IClassFactory2(), which inherits its definition from IClassFactory(). Thus, a server implementing IClassFactory2() must, by definition, implement the methods of IClassFactory(). For more information on IClassFactory2(), see Section Section 8.3.

COM also provides helper functions for implementing out-of-process servers. For more information, see Section Section 8.6.

8.3    Licensing and IClassFactory2

The IClassFactory() interface on a class object provides the basic object creation mechanism of COM. Using IClassFactory(), a server can control object creation on a machine basis. The implementation of the IClassFactory::CreateInstance() method can allow or disallow object creation based the existence of a machine license. A machine license is a piece of information separate from the application that exists on a machine to indicate that the software was installed from a valid source, such as the vendor's installation disks. If the machine license does not exist, the server can disallow object creation. Machine licensing prevents piracy in cases where a user attempts to copy the software from one machine to another; because the license information is not copied with the software, and the machine that receives the copy is not licensed.

However, in a component software industry, vendors need a finer level of control over licensing. In addition to machine license control, the a vendor needs to allow some clients to create a component object while preventing other clients from the same capability. This kind of licensing requires that the client application obtain a license key from component while the client application is still under development. The client application uses the license key later at run-time to create objects on an unlicensed machine.

For example, if a vendor provides a library of controls to developers, the developer who purchases the library will have a full machine license, allowing the objects to be created on the development machine. The developer can then build a client application on the licensed machine incorporating one or more of the controls. When the resulting client application is run on another machine, the controls used in the client application must be created on the other machine even if that machine does not possess a machine license to the controls from the original vendor.

The IClassFactory2() interface provides this level of control. To allow key-based licensing for any given component, you implement IClassFactory2() on the class factory object for that component. IClassFactory2() is derived from IClassFactory(), so by implementing IClassFactory2() the class factory object fulfills the basic COM requirements.

The GetLicInfo method fills a LICINFO structure with information describing the licensing behavior of the class factory. For example, the class factory can provide license keys for run-time licensing if the fRunTimeKeyAvail member is TRUE.

The RequestLicKey method provides a license key for the component. A machine license must be available when the client calls this method.

The CreateInstanceLic method creates an instance of the licensed component if the license key parameter (BSTR bstrKey) is valid.

In its type information, a component uses the attribute licensed to mark the coclass that supports licensing through IClassFactory2().

To incorporate a licensed component into your client application, you use the methods in IClassFactory2().

First, you need a separate development tool that is also a client of the licensed component. The purpose of this tool is to obtain the run-time license key and save it in your client application. This tool runs only on a machine that possesses a machine license for the component. The tool calls the GetLicInfo and RequestLicKey methods to obtain the run-time license key and then saves the license key in your client application. For example, the development tool could create a .H file containing the BSTR license key. Then, you would include that .H file in your client application.

To instantiate the component within your client application, you first try to instantiate the object directly with IClassFactory::CreateInstance(). If CreateInstance succeeds, then the second machine is itself licensed for the component and objects can be created at will. If CreateInstancefails with the return code CLASS_E_NOTLICENSED, the only way to create the object is to pass the run-time key to the CreateInstanceLic method. CreateInstanceLic verifies the key and creates the object if the key is valid.

In this way an application built with components (such as controls), can run on a machine that has no other license--only the client application containing the run-time license is allowed to create the component objects in question.

The IClassFactory2() interface supports flexibility in licensing schemes. For example, the server implementor can encrypt license keys in the component for added security. Server implementers can also enable or disable levels of functionality in their objects by providing different license keys for different functions. For example, one key might allow a base level of functionality, while another would allow basic and advanced functionality, and so on. See OLE Controls Inside Out published by MS Press for detailed consideration of these issues.

8.4    Registering COM Servers

After you have defined a class in code (ensuring that it implements IClassFactory() or IClassFactory2()) and assigned it a CLSID, you need to put information in the registry that will allow COM, on request of a client with the CLSID, to create instances of its objects. This information tells the system, for a given CLSID, where the DLL or EXE code for that class is located, and how it is to be launched. There is more than one way of registering a class in the registry. In addition, there are other ways of ``registering'' a class with the system when it is running, so the system is aware that a running object is currently in the system. These topics are described in the following sections.

8.4.1    Registering a Class at Installation

If a class is intended to be available to clients at any time, as most applications are, you usually register it through an installation and setup program. This means putting information about the application into the registry, including how and where its objects are to be instantiated. This information must be registered for all CLSIDs. Other information is optional. Win32 tools, such as Regsvr32, make it simple to write a setup program that registers servers at installation.

If you are not relying on system defaults, there are two important keys in the registry: CLSID and AppID. Among the important pieces of information under these keys is how the object is to be instantiated. Objects can be designated as in-process, out-of-process local, or out-of-process remote.

Under the new AppID key, are several named-values that define information specific to that application. Among these are RemoteServerName, and ActivateAtStorage, both of which can be used to permit a client with no built-in knowledge of the location of the server, to create an object. For more information on remote instantiation, see Section Section 7.1.3 and Section Section 7.1.4.

A server or ROT object that is not a Win32 service or run under a specific user account can be referred to as an ``activate as activator'' server. For these servers, the security context and the window station/desktop of the client must match the server's.

When a class is registered as in-process, a call to CoGetClassObject() to create its class object is automatically passed by COM to the DllGetClassObject() function, which the class must implement to give the calling object a pointer to its class object.

Classes implemented in executables can specify that COM should execute their process and wait for the process to register their class object's IClassFactory() through a call to the CoRegisterClassObject() function.

For detailed COM registry information, see Chapter 8.

8.4.2    Registering a Running EXE Server

When an executable (EXE) server is launched, it should call CoRegisterClassObject(), which registers the CLSID for the server in what is called the class table (this is a different table than the running object table). When a server is registered in the class table, it allows the SCM to determine that it is not necessary to launch the class again; because the server is already running. Only if the server is not listed in the class table will the SCM check the registry for appropriate values and launch the server associated with the given CLSID.

You pass CoRegisterClassObject() the CLSID for the class and a pointer to its IUnknown() interface. Clients who subsequently call CoGetClassObject() with this CLSID will retrieve a pointer to their requested interface, as long as security does not forbid it. There are several instance creation and activation functions described in Section Section 7.1.4,

The server for a class object should call CoRevokeClassObject() to revoke the class object (remove its registration) when all of the following are true:

8.4.3    Registering Objects in the ROT

Typically, when a client asks a server to create an object instance, the server typically creates moniker for the object, and registers it in the running object table (ROT) through a call to IRunningObjectTable::Register().

A few additional issues arise when registering ROT objects for use by remote clients. When the server calls CreateFileMoniker to create a file moniker to be registered in the ROT, servers should pass local file names that are drive-based, not in UNC format. This ensures that the moniker comparison data that is generated by the ROT register call will match what is used while doing a ROT lookup on the part of a remote client. This is because when the distribed COM service receives an activation request for a file local to the server from a remote client, the file is converted to a local-drive-based path.

8.5    Self-Registration

As component software continues to grow as a market, there will be more and more instances where a user obtains a new software component as a single DLL or EXE module, such as downloading a new component from an on-line service or receiving one from a friend on a floppy disk. In these cases, it is not practical to require the user to go through a lengthy installation procedure or setup program. Besides the licensing issues, which are handled through IClassFactory2(), an installation procedure typically creates the necessary registry entries for a component to run properly in the COM context.

Self-Registration is the standard means through which a server module can package its own registry operations, both registration and unregistration, into the module itself. When used with licensing handled through IClassFactory2(), a server can become an entirely self-contained module with no need for external installation programs or .REG files.

[Footnote 41] Any self-registering module, DLL or EXE, should first include a string called OleSelfRegister in the StringFileInfo section of its version information resource: [Footnote 41]

The existence of this data allows any interested party, such as an application that wishes to integrate this new component, to determine if the server supports self-registration without having to load the DLL or EXE first.

If the server is packaged in a DLL module, the DLL must export the functions DllRegisterServer() and DllUnregisterServer(). Any application that wishes to instruct the server to register itself (that is, all its CLSIDs and type library IDs) can obtain a pointer to DllRegisterServer() through the Win32 API function GetProcAddress. Within DllRegisterServer(), the DLL creates all its necessary registry entries, storing the correct path to the DLL for all InprocServer32 or InprocHandler32 entries.

When an application wishes to remove the component from the system, it should unregister that component by calling DllUnregisterServer(). Within this call, the server removes exactly those entries it previously created in DllRegisterServer(). The server should not blindly remove all entries for its classes because other software may have stored additional entries, such as a TreatAs key.

If the server is packaged in an EXE module, then the application wishing to register the server launches the EXE server with the command-line argument /RegServer or -RegServer (case-insensitive). If the application wishes to unregister the server, it launches the EXE with the command-line argument /UnregServer or -UnregServer. The self-registering EXE detects these command-line arguments and invokes the same operations as a DLL would within DllRegisterServer() and DllUnregisterServer(), respectively, registering its module path under LocalServer32 instead of InprocServer32 or InprocHandler32.

The server must register the full path to the installation location of the DLL or EXE module for their respective InprocServer32, InprocHandler32, and LocalServer32 keys in the registry. The module path is easily obtained through the Win32 API function GetModuleFileName.

8.6    Out-of-process Server Implementation Helpers

Four helper functions that can be called by out-of-process servers are now available to simplify the job of writing server code. COM clients and COM in-process servers typically would not call them. These functions are designed to help prevent race conditions in server activation when the servers have multiple apartments or multiple class objects. They can also, however, as easily be used for single-threaded and single class object servers. The functions are as follows:

To shut down properly, a COM server must keep track of how many object instances it has instantiated and how many times its IClassFactory::LockServer() method has been called. Only when both of these counts reach zero, can a server shut down. In single-threaded COM servers, the decision to shut down was coordinated with incoming activation requests by the fact that the requests were serialized by the message queue. The server, upon receiving a Release() on its final object instance and deciding to shut down, would revoke its class objects before any more activation requests were dispatched. If an activation request did come in after this point, COM would recognize that the class objects were revoked, and would return an error to the SCM, which would then cause a new instance of the local server process to be run.

However, in an apartment model server, in which different class objects are registered on different apartments, and in all free-threaded servers, this decision to shut down must be co-ordinated with activation requests across multiple threads, so one thread of the server does not decide to shut down while another thread of the server is busy handing out class objects or object instances. One classical but cumbersome approach to solving this is to have the server, after it has revoked its class objects, recheck its instance count and stay alive until all instances have been released.

To make it easier for server writers to handle these types of race conditions, COM provides two new reference counting functions. CoAddRefServerProcess() increments a global per-process reference count. CoReleaseServerProcess() decrements the global per-process reference count. When the global per-process reference count reaches zero, COM automatically does a CoSuspendClassObjects(), which prevents any new activation requests from coming in. The server can then deregister its various class objects from its various threads at leisure without worry that another activation request may come in. All new activation requests are henceforth handled by the SCM launching a new instance of the local server process.

The simplest way for a local server application to make use of these APIs is to call CoAddRefServerProcess() in the constructor for each of its instance objects, and in each of its IClassFactory::LockServer() methods when the fLock parameter is TRUE. The server application should also call CoReleaseServerProcess() in the destructor of each of its instance objects, and in each of its IClassFactory::LockServer() methods when the fLock parameter is FALSE.

Finally, the server application should pay attention to the return code from CoReleaseServerProcess() and if it returns 0, the server application should initiate its cleanup, which, for a server with multiple threads, typically means that it should signal its various threads to exit their message loops and call CoRevokeClassObject() and CoUninitialize(). Note that if these functions are used at all, they must be used in both the object instances and the LockServer method, otherwise, the server application may be shut down prematurely.

In the latest versions of Windows NT, when a CoGetClassObject() request is made, COM contacts the server, marshals the IClassFactory() interface of the class object, returns to the client process, unmarshals the IClassFactory() interface, and returns this to the client. At this point, clients typically call IClassFactory::LockServer(TRUE) to prevent the server process from shutting down. However, there is a window of time between when the class object is marshaled and when the client calls LockServer, in which another client could connect to the same server, get an instance and Release() that instance causing the server to shutdown and leaving the first client high and dry with a disconnected IClassFactory() pointer. To prevent this race condition, COM adds an implicit IClassFactory::LockServer(TRUE) to the class object when it marshals the IClassFactory() interface, and an implicit IClassFactory::LockServer(FALSE) when the client releases the IClassFactory() interface. Because of this change, it is no longer necessary to remote LockServer calls back to the server, so the proxy for IClassFactory::LockServer() simply returns S_OK without actually remoting the call.

There is another activation-related race condition during initialization of an out-of-process server process. A COM server that registers multiple classes typically calls CoRegisterClassObject(....REGCLS_LOCAL_SERVER) for each CLSID it supports. After it has done this for all classes, the server enters its message loop. For a single-threaded COM server, all activation requests are blocked until the server enters the message loop. However, for an apartment model server that registers different class objects in different apartments, and for all free-threaded servers, activation requests can arrive earlier than this. In the case of apartment model servers, activation requests could arrive as soon as any one thread has entered its message loop. In the case of free-threaded servers, an activation request could arrive as soon as the first class object is registered. Since an activation can happen this early, it is also possible for the final Release() to occur (and hence cause the server to begin shutting down) before the rest of the server has had a chance to finish initializing.

To eliminate these race conditions and simplify the job of the server writer, any server that wants to register multiple class objects with COM should call CoRegisterClassObject(...., REGCLS_LOCAL_SERVER | REGCLS_SUSPENDED) for each different CLSID the server supports. After all classes have been registered and the server process is ready to accept incoming activation requests, the server should make one call to CoResumeClassObjects(). This API tells COM to inform the SCM about all the registered classes, and it begins letting activation requests into the server process. Using these APIs has serveral advantages. First, only one call is made to the SCM regardless of how many CLSIDs are registered, thus reducing the overall registration time (and hence startup time of the server application). The second advantage is that if the server has multiple apartments and different CLSIDs are registered in different apartments, or if the server is a free-threaded server, no activation requests will come in until the server calls CoResumeClassObjects(), giving the server a chance to register all of its CLSIDs and get properly set up before having to deal with activation requests, and possible shut down requests.

8.7    Object Handlers

As mentioned earlier this specification, object handlers from one perspective are special cases of in-process servers that talk to their local or remote servers as well as a client. From a second perspective, an object handler is really just a fancy proxy for a local or remote server that does a little more than just forward calls through RPC. The latter view is more precise architecturally: a ``handler'' is simply the piece of code that runs in the client's space on behalf of a remote object; it can be used synonymously with the term ``proxy object.'' The handler may be a trivial one, one that simply forwards all of its calls on to the remote object, or it may implement some amount of non-trivial client side processing. (In practice, the term ``proxy object'' is most often reserved for use with trivial handlers, leaving ``handler'' for the more general situation.)

The structure of an object handler is exactly the same as a full-in process server: an object handler implements an object, a class factory, and the two functions DllGetClassObject() and DllCanUnloadNow() exactly as described above.

The key difference between handlers and full DLL servers (and simple proxy objects, for that matter) is the extent to which they implement their respective objects. Whereas the full DLL server implements the complete object (using other objects internally, if desired), the handler only implements a partial object depending on a local or remote server to complete the implementation. Again, the reasons for this is that sometimes a certain interface can only be useful when implemented on an in-process object, such as when member functions of that interface contain parameters that cannot be shared between processes. Thus the object in the handler would implement the restricted in-process interface but leave all others for implementation in the local or remote server.

8.8    Object Reusability

With object-oriented programming it is often true that there already exists some object that implements some of what you want to implement, and instead of rewriting all that code yourself you would like to reuse that other object for your own implementation. Hence we have the desire for object reusability and a number means to achieve it such as implementation inheritance, which is exploited in C++ and other languages. However, as discussed in Section 3.5, implementation inheritance has some significant drawbacks and problems that do not make it a good object reusability mechanism for a system object model.

For that reason COM supports two notions of object reuse, containment and aggregation, that were also described in Chapter 3. In that chapter we saw that containment, the most common and simplest for of object reuse, is where the ``outer object'' simply uses other ``inner objects'' for their services. The outer object is nothing more than a client of the inner objects. We also saw in Chapter 3 the notion of aggregation, where the outer object exposes interfaces from inner objects as if the outer object implemented those interfaces itself. We brought up the catch that there has to be some mechanism through which the IUnknown() behavior of inner object interfaces exposed in this manner is appropriate to the outer object. We are now in a position to see exactly how the solution manifests itself.

The following sections treat Containment and Aggregation in more detail using the TextRender object as an example. To refresh our memory of this object's purpose, the following list reiterates the specific features of the TextRender object that implements the IPersistFile() and IDataObject() interfaces:

8.8.1    Reusability Through Containment

Let's say that when we decide to implement the TextRender object we find that another object exists with CLSID_TextImage that is capable of accepting text through IDataObject::SetData but can do nothing more than render a metafile or bitmap for that text through IDataObject::GetData. This ``TextImage'' object cannot render memory copies of the text and has no concept of reading or writing text to a file. But it does such a good job implementing the graphical rendering that we wish to use it to help implement our TextRender object.

In this case the TextRender object, when asked for a metafile or bitmap of its current text in IDataObject::GetData, would delegate the rendering to the TextImage object. TextRender would first call TextImage's IDataObject::SetData to give it the most recent text (if it has changed since the last call) and then call TextImage's IDataObject::GetData asking for the metafile or bitmap format. This delegation is illustrated in Figure 8-1.

Figure 8-1:  Using Inner Objects through Containment

To create this configuration, the TextRender object would, during its own creation, instantiate the TextImage object with the following code, storing the TextImage's IDataObject() pointer in a TextImage field m_pIDataObjImage:

//TextRender initialization
HRESULT     hr;
hr=CoCreateInstance(CLSID_TextImage, CLSCTX_SERVER, NULL, IID_IDataObject, (void *)&m_pIDataObjImage);
if (FAILED(hr))
 //TextImage not available, either fail or disable graphic rendering
//Success:  can now make use of TextImage object.

8.8.2    Reusability Through Aggregation

Let's now say that we are planning to revise our TextRender object at a later time than out initial containment implementation in the previous section. At that time we find that the implementor of the TextImage object at the time the implementor of the TextRender object sat down to work (or perhaps is making a revision of his object) that the vendor of the TextImage object has improved TextImage such that it implements everything that TextRender would like to do through its IDataObject() interface. That is, TextImage still accepts text through SetData but has recently added the ability to make copies of its text and provide those copies through GetData in addition to metafiles and bitmaps.

In this case, the implementor of TextRender now sees that TextImage's implementation of IDataObject is exactly the implementation that TextRender requires. What we, as the implementors of TextRender, would like to do now is simply expose TextImage's IDataObject() as our own as shown in Figure 8-2.

Figure 8-2:  Exposing an Inner Object's Interface

The only catch is that we must implement the proper behavior of the IUnknown() members in the inner object's (TextImage) IDataObject() interface: AddRef and Release() have to affect the reference count on the outer object (TextRender) and not the reference count of the inner object. Furthermore, QueryInterface() has to be able to return the TextRender object's IPersistFile() interface. The solution is to inform the inner object that it is being used in an aggregation such that when it sees IUnknown() calls to its interfaces it can delegate those calls to the outer object.

[Footnote 42] One other catch remains: the outer object must have a means to control the lifetime of the inner object through AddRef and Release() as well as have a means to query for the interfaces that only exist on the inner object. For that reason, the inner object must implement an isolated version of IUnknown() that controls the inner object exclusively and never delegates to the outer object. [Footnote 42] This requires that the inner object separate the IUnknown() members of its functional interfaces from an implementation of IUnknown() that strictly controls the inner object itself. In other words, the inner object, to support aggregation, must implement two sets of IUnknown functions: delegating and non-delegating.

This, then, is the mechanism for making aggregation work:

  1. When creating the inner object, the outer object must pass its own IUnknown() to the inner object through the pUnkOuter parameter of IClassFactory::CreateInstance. pUnkOuter in this case is called the ``controlling unknown.''

  2. The inner object must check pUnkOuter in its implementation of CreateInstance. If this parameter is non-NULL, then the inner object knows it is being created as part of an aggregate. If the inner object does not support aggregation, then it must fail with CLASS_E_NOAGGREGATION. If aggregation is supported, the inner object saves pUnkOuter for later use, but does not call AddRef() on it. The reason is that the inner object's lifetime is entirely contained within the outer object's lifetime, so there is no need for the call and to do so would create a circular reference.

  3. If the inner object detects a non-NULL pUnkOuter in CreateInstance, and the call requests the interface IUnknown() itself (as is almost always the case), the inner object must be sure to return its non-delegating IUnknown().

  4. If the inner object itself aggregates other objects (which is unknown to the outer object) it must pass the same pUnkOuter pointer it receives down to the next inner object.

  5. When the outer object is queried for an interface it exposes from the inner object, the outer object calls QueryInterface() in the non-delegating IUnknown() to obtain the pointer to return to the client.

  6. The inner object must delegate to the controlling unknown, that is, pUnkOuter, all IUnknown calls occurring in any interface it implements other than the non-delegating IUnknown().

Through these steps, the inner object is made aware of the outer object, obtains an IUnknown() to which it can delegate calls to insure proper behavior of reference counting and QueryInterface(), and provides a way for the outer object to control the inner object's lifetime separately. The mechanism is illustrated in Figure 8-3.

Figure 8-3:  Aggregation Mechanism

Now let's look at how this mechanism manifests itself in code. First off, the TextRender object no longer needs its own IDataObject() implementation and can thus remove it from its class, but will need to add a member m_pUnkImage to maintain the TextImage's non-delegating IUnknown:

class CTextRender() : public IPersistFile() {
 private:
  ULONG  m_cRef;   //Reference Count
  char *  m_pszText;  //Pointer to allocated text
  ULONG  m_cchText;  //Number of characters in m_pszText
  IUnknown() * m_pUnkImage;  //TextImage IUnknown()
  //Other internal member functions here
 public:
  [Constructor, Destructor]
  //Outer object IUnknown()
  HRESULT QueryInterface(REFIID iid, void ** ppv);
  ULONG AddRef(void);
  ULONG Release(void);
  //IPersistFile Member overrides
  ...
 };

In the previous section we saw how the TextRender object would create a TextImage object for containment using CoCreateInstance() with the pUnkOuter parameter set to NULL. In aggregation, this parameter will be TextRender's own IUnknown (obtained using a typecast). Furthermore, TextRender must request IUnknown() initially from TextImage (storing the pointer in m_pUnkImage):

//TextRender initialization
HRESULT     hr;
hr=CoCreateInstance(CLSID_TextImage, CLSCTX_ SERVER, (IUnknown() *)this, IID_IUnknown, (void *)&m_pUnkImage);
if (FAILED(hr))
 //TextImage not available, either fail or disable graphic rendering
//Success:  can now make use of TextImage object.

Now, since TextRender does not have its own IDataObject() any longer, its implementation of QueryInterface() will use m_pUnkImage to obtain interface pointers:

HRESULT CTextRender::QueryInterface(REFIID iid, void ** ppv) {
 *ppv=NULL;
 //This code assumes an overloaded == operator for GUIDs exists
 if (IID_IUnknown==iid)
  *ppv=(void *)(IUnknown() *)this;
 if (IID_IPersitFile==iid)
  *ppv=(void *)(IPersistFile() *)this;
 if (IID_IDataObject==iid)
  return m_pUnkImage->QueryInterface(iid, ppv);
 if (NULL==*ppv)
  return E_NOINTERFACE;  //iid not supported.
 //Any call to anyone's AddRef() is our own.
 AddRef();
 return NOERROR;
 }

Note that delegating QueryInterface() to the inner object is done only for those interfaces that the outer object knows it wants to expose. The outer object should not delegate the query as a default case, for such blind forwarding without an understanding of the semantic being forwarded will almost assuredly break the outer object should the inner one be revised with new functionality.

8.8.2.1    Caching interfaces on the inner object

In order to avoid reference counting cycles, special action is needed if the outer object wishes to cache pointers to the inner object's interfaces.

Specifically, if the outer object wishes to cache a to an inner object's interface, once it has obtained the interface from the inner object, the outer object should invoke Release() on the punkOuter that was given to the inner object at its instantiation time.

// Obtaining inner object interface pointer
pUnkInner->QueryInterface(IID_IFoo, &pIFoo);
pUnkOuter->Release();
// Releasing inner object interface pointer
pUnkOuter->AddRef();
pIFoo->Release();

It is suggested that to allow inner objects to do better resource management that controlling objects delay the acquisition of cached pointers and release them when there is no possible use for them.

8.8.2.2    Efficiency at any Depth of Aggregation

Aggregation has one interesting aspect when aggregates are used on more than one level of an object implementation. Imagine that the TextImage object in the previous example is itself an aggregate object that uses other inner objects. In such a case TextImage will be passing some controlling unknown to those other inner objects. If TextImage is not being aggregated by anyone else, then the controlling unknown is its own; otherwise it passes the pUnkOuter from IClassFactory::CreateInstance on down the line, and any other inner objects that are aggregates themselves do the same.

The net result is that any object in an aggregation, no matter how deeply it is buried in the overall structure, will almost always delegate directly to the controlling unknown if its interface is exposed from that final outer object. Therefore performance and efficiency of multiple levels of aggregation is not an issue. At worst each delegation is a single extra function call.

8.9    Emulating Other Servers

The final topic related to COM Servers for this chapter is what is known as emulation: the ability for one server associated with one CLSID to emulate a server of another CLSID. A server that can emulate another is responsible for providing compatible behavior for a different class through a different implementation. This forms the basis for allowing end-users the choice in which servers are used for which objects, as long as the behavior is compatible between those servers.

As far as COM is concerned, it only has to provide some way for a server to indicate that it wishes to emulate some CLSID. To that end, the COM Library supplies the function CoTreatAsClass() to establish an emulation that remains in effect (persistently) until canceled or changed. In addition it supplies CoGetTreatAsClass() to allow a caller to determine if a given CLSID is marked for emulation.

8.10    COM Server Related Interface Descriptions

8.10.1    ICatRegister

The ICatRegister() interface provides methods for registering and unregistering component category information in the Registry. This includes both the human-readable names of categories and the categories implemented/required by a given component or class.

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

When to Use

The owner of a category uses this interface to register or unregister the human-readable names. The owner of a component uses this interface to add or remove categories implemented or required by this component.

Table 8-1:  ICatRegister Methods in VTable Order

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

Table 8-2:  ICatRegister Methods

ICatRegister() Methods Description
RegisterCategories Registers one or more component categories.
UnRegisterCategories Removes the registration of one or more component categories.
RegisterClassImplCategories Registers the class as implementing one or more component categories.
UnRegisterClassImplCategories Removes one or more implemented category identifiers from a class.
RegisterClassReqCategories Registers the class as requiring one or more component categories.
UnRegisterClassReqCategories Removes one or more required category identifiers from a class.

 

ICatRegister::RegisterCategories()

NAME

ICatRegister::RegisterCategories() - Registers one or more component categories. Each component category consists of a CATID and a list of locale-dependent description strings.

Synopsis

#include <comcat.h>

HRESULT RegisterCategories(
        ULONG cCategories,
        CATEGORYINFO * rgCategoryInfo );

Description

This function can only be called by the owner of a category, usually as part of the installation or de-installation of the operating system or application.

Parameters

cCategories

[in] The number of component categories to register.

rgCategoryInfo

[in] The array of cCategories CATEGORYINFO structures. By providing the same CATID for multiple CATEGORYINFO structures, multiple locales can be registered for the same component category.

Return Values

S_OK

The function was successful.

E_INVALIDARG

One or more arguments are incorrect.

See Also

ICatRegister::RegisterClassImplCategories(), ICatRegister::RegisterClassReqCategories(), ICatRegister::UnRegisterCategories(), ICatRegister::UnRegisterClassImplCategories(), ICatRegister::UnRegisterClassReqCategories()  

ICatRegister::RegisterClassImplCategories()

NAME

ICatRegister::RegisterClassImplCategories() - Registers the class as implementing one or more component categories.

Synopsis

#include <comcat.h>

HRESULT RegisterClassImplCategories(
        REFCLSID rclsid,
        ULONG cCategories,
        CATID * rgcatid );

Description

In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. This function can only be called by the owner of a class, usually as part of the installation of the component.

Parameters

rclsid

[in] The class ID of the relevent class for which category information will be set.

cCategories

[in] The number of category CATIDs to associate as category identifiers for the class.

rgcatid

[in] The array of cCategories CATID to associate as category identifiers for the class.

Return Values

S_OK

The function was successful.

E_INVALIDARG

One or more arguments are incorrect.

See Also

ICatRegister::RegisterCategories(), ICatRegister::RegisterClassReqCategories(), ICatRegister::UnRegisterCategories(), ICatRegister::UnRegisterClassImplCategories(), ICatRegister::UnRegisterClassReqCategories()  

ICatRegister::RegisterClassReqCategories()

NAME

ICatRegister::RegisterClassReqCategories() - Registers the class as requiring one or more component categories.

Synopsis

#include <comcat.h>

HRESULT RegisterClassReqCategories(
        REFCLSID rclsid,
        ULONG cCategories,
        CATID * rgcatid );

Description

In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. This function can only be called by the owner of a class, usually as part of the installation of the component.

Parameters

rclsid

[in] The class ID of the relevent class for which category information will be set.

cCategories

[in] The number of category CATIDs to associate as category identifiers for the class.

rgcatid

[in] The array of cCategories CATID to associate as category identifiers for the class.

Return Values

S_OK

The function was successful.

E_INVALIDARG

One or more arguments are incorrect.

See Also

ICatRegister::RegisterCategories(), ICatRegister::RegisterClassImplCategories(), ICatRegister::UnRegisterCategories(), ICatRegister::UnRegisterClassImplCategories(), ICatRegister::UnRegisterClassReqCategories()  

ICatRegister::UnRegisterCategories()

NAME

ICatRegister::UnRegisterCategories() - Removes the registration of one or more component categories. Each component category consists of a CATID and a list of locale-dependent description strings.

Synopsis

#include <comcat.h>

HRESULT UnRegisterCategories(
        ULONG cCategories,
        REFCATID rgcatid );

Description

This function will be successful even if one or more of the category IDs specified are not registered. This function can only be called by the owner of a category, usually as part of the installation or de-installation of the operating system or application.

This method does not remove the component category tags from individual classes. To do this, use the ICatRegister::UnRegisterClassCategories() method.

Parameters

cCategories

[in] The number of cCategories CATIDs to be removed.

rgcatid

[in] Identifies the categories to be removed.

Return Values

S_OK

The function was successful.

E_INVALIDARG

One or more arguments are incorrect.

See Also

ICatRegister::RegisterCategories(), ICatRegister::RegisterClassImplCategories(), ICatRegister::RegisterClassReqCategories(), ICatRegister::UnRegisterClassImplCategories(), ICatRegister::UnRegisterClassReqCategories()  

ICatRegister::UnRegisterClassImplCategories()

NAME

ICatRegister::UnRegisterClassImplCategories() - Removes one or more implemented category identifiers from a class.

Synopsis

#include <comcat.h>

HRESULT UnRegisterClassImplCategories(
        REFCLSID rclsid,
        ULONG cCategories,
        CATID * rgcatid );

Description

In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. The call will be successful even if one or more of the category IDs specified are not registered for the class. This function can only be called by the owner of a class, usually as part of the de-installation of the component.

Parameters

rclsid

[in] The class ID of the relevant class to be manipulated.

cCategories

[in] The number of category CATIDs to remove.

rgcatid

[in] The array of cCategories CATID that are to be removed. Only the category IDs specified in this array are removed.

Return Values

S_OK

The function was successful.

E_INVALIDARG

One or more arguments are incorrect.

See Also

ICatRegister::RegisterCategories(), ICatRegister::RegisterClassImplCategories(), ICatRegister::RegisterClassReqCategories(), ICatRegister::UnRegisterCategories(), ICatRegister::UnRegisterClassReqCategories()  

ICatRegister::UnRegisterClassReqCategories()

NAME

ICatRegister::UnRegisterClassReqCategories() - Removes one or more required category identifiers from a class.

Synopsis

#include <comcat.h>

HRESULT UnRegisterClassReqCategories(
        REFCLSID rclsid,
        ULONG cCategories,
        CATID * rgcatid* );

Description

In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. The call will be successful even if one or more of the category IDs specified are not registered for the class.

Parameters

rclsid

[in] The class ID of the relevent class to be manipulated.

cCategories

[in] The number of category CATIDs to remove.

rgcatid

[in] The array of cCategories CATID that are to be removed. Only the category IDs specified in this array are removed.

Return Values

S_OK

The function was successful.

E_INVALIDARG

One or more arguments are incorrect.

See Also

ICatRegister::RegisterCategories(), ICatRegister::RegisterClassImplCategories(), ICatRegister::UnRegisterCategories(), ICatRegister::UnRegisterClassImplCategories(), ICatRegister::RegisterClassReqCategories()

8.10.2    IClassFactory

The IClassFactory() interface contains two methods intended to deal with an entire class of objects, and so is implemented on the class object for a specific class of objects (identified by a CLSID). The first method, CreateInstance(), creates an uninitialized object of a specified CLSID, and the second, LockServer(), locks the object's server in memory, allowing new objects to be created more quickly.

When to Implement

You must implement this interface for every class that you register in the system registry and to which you assign a CLSID, so objects of that class can be created.

When to Use

After calling the CoGetClassObject() function to get an IClassFactory() interface pointer to the class object, call the CreateInstance method of this interface to create a new uninitialized object.

It is not, however, always necessary to go through this process to create an object. To create a single uninitialized object, you can, instead, just call CoCreateInstance(). COM also provides numerous helper functions (with names of the form OleCreateXxx) to create compound document objects.

Call the LockServer method to keep the object server in memory and enhance performance only if you intend to create more than one object of the specified class.

Table 8-3:  IClassFactory Methods in VTable Order

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

Table 8-4:  IClassFactory Methods

IClassFactory() Methods Description
CreateInstance Creates an uninitialized object.
LockServer Locks object application open in memory.

See Also

CoGetClassObject(), CoCreateInstance()  

IClassFactory::CreateInstance()

NAME

IClassFactory::CreateInstance() - Creates an uninitialized object.

Synopsis

#include <unknwn.h>

HRESULT CreateInstance(
        IUnknown * pUnkOuter,
        REFIID riid,
        void ** ppvObject );

Description

The IClassFactory() interface is always on a class object. The CreateInstance method creates an uninitialized object of the class identified with the specified CLSID. When an object is created in this way, the CLSID must be registered in the system registry with CoRegisterClassObject().

The pUnkOuter parameter indicates whether the object is being created as part of an aggregate. Object definitions are not required to support aggregation--they must be specifically designed and implemented to support it.

The riid parameter specifies the IID (interface identifier) of the interface through which you will communicate with the new object. If pUnkOuter is non-NULL (indicating aggregation), the value of the riid parameter must be IID_IUnknown. If the object is not part of an aggregate, riid often specifies the interface though which the object will be initialized.

For COM embeddings, the initialization interface is IPersistStorage(), but in other situations, other interfaces are used. To initialize the object, there must be a subsequent call to an appropriate method in the initializing interface. Common initialization functions include IPersistStorage::InitNew() (for new, blank embeddable components), IPersistStorage::Load() (for reloaded embeddable components), IPersistStream::Load(), (for objects stored in a stream object) or IPersistFile::Load() (for objects stored in a file).

In general, if an application supports only one class of objects, and the class object is registered for single use, only one object can be created. The application must not create other objects, and a request to do so should return an error from IClassFactory::CreateInstance(). The same is true for applications that support multiple classes, each with a class object registered for single use; a CreateInstance for one class followed by a CreateInstance for any of the classes should return an error.

To avoid returning an error, applications that support multiple classes with single-use class objects can revoke the registered class object of the first class by calling CoRevokeClassObject() when a request for instantiating a second is received. For example, suppose there are two classes, A and B. When IClassFactory::CreateInstance() is called for class A, revoke the class object for B. When B is created, revoke the class object for A. This solution complicates shutdown because one of the class objects might have already been revoked (and cannot be revoked twice).

Parameters

pUnkOuter

[in] If the object is being created as part of an aggregate, pointer to the controlling IUnknown() interface of the aggregate. Otherwise, pUnkOuter must be NULL.

riid

[in] Reference to the identifier of the interface to be used to communicate with the newly created object. If pUnkOuter is NULL, this parameter is frequently the IID of the initializing interface; if pUnkOuter is non-NULL, riid must be IID_IUnknown (defined in the header as the IID for IUnknown()).

ppvObject

[out] Indirect pointer to the requested interface. If the object does not support the interface specified in riid, ppvObject must be set to NULL.

Return Values

This method supports the standard return values E_UNEXPECTED, E_OUTOFMEMORY, and E_INVALIDARG, as well as the following:

S_OK

The specified object was created.

CLASS_E_NOAGGREGATION

The pUnkOuter parameter was non-NULL and the object does not support aggregation.

E_NOINTERFACE

The object that ppvObject points to does not support the interface identified by riid.

See Also

CoRegisterClassObject(), CoRevokeClassObject(), CoCreateInstance(), CoGetClassObject()  

IClassFactory::LockServer()

NAME

IClassFactory::LockServer() - Called by the client of a class object to keep a server open in memory, allowing instances to be created more quickly.

Synopsis

#include <unknwn.h>

HRESULT LockServer(
        BOOL fLock );

Description

IClassFactory::LockServer() controls whether an object's server is kept in memory. Keeping the application alive in memory allows instances to be created more quickly.

Notes to Callers

Most clients do not need to call this function. It is provided only for those clients that require special performance in creating multiple instances of their objects.

Notes to Implementors

If the lock count is zero, there are no more objects in use, and the application is not under user control, the server can be closed. One way to implement IClassFactory::LockServer() is to call CoLockObjectExternal().

The process that locks the object application is responsible for unlocking it. Once the class object is released, there is no mechanism that guarantees the caller connection to the same class later (as in the case where a class object is registered as single-use). It is important to count all calls, not just the last one, to IClassFactory::LockServer(), because calls must be balanced before attempting to release the pointer to the IClassFactory() interface on the class object or an error results. For every call to LockServer with fLock set to TRUE, there must be a call to LockServer with fLock set to FALSE. When the lock count and the class object reference count are both zero, the class object can be freed.

Parameters

fLock

[in] If TRUE, increments the lock count; if FALSE, decrements the lock count.

Return Values

This method supports the standard return values E_FAIL, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK

The specified object was either locked ( fLock = TRUE) or unlocked from memory ( fLock = FALSE).

See Also

CoLockObjectExternal()

8.10.3    IClassFactory2

The IClassFactory2() interface enables a class factory object, in any sort of object server, to control object creation through licensing. This interface is an extension to IClassFactory(). This extension enables a class factory executing on a licensed machine to provide a license key that can be used later to create an object instance on an unlicensed machine. Such considerations are important for objects like controls that are used to build applications on a licensed machine. Subsequently, the application built must be able to run on an unlicensed machine. The license key gives only that one client application the right to instantiate objects through IClassFactory2() when a full machine license does not exist.

When to Implement

Implement this interface on a class factory object if you need to control object creation through a license. A class that supports licensing should be marked in an object's type information with the [licensed] attribute on the object's coclass entry.

The CreateInstance method inherited from IClassFactory() is allowed to return CLASS_E_NOTLICENSED to indicate that object creation is controlled through licensing. The caller can create an instance of this object only through IClassFactory2::CreateInstanceLic() if the caller has a license key obtained from IClassFactory2::RequestLicKey(). Otherwise, no object creation is allowed.

When to Use

Use this interface to create licensed objects or to obtain a license key that can be used in later creations.

Table 8-5:  IClassFactory2 Methods in VTable Order

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

Table 8-6:  IClassFactory Methods

IClassFactory() Methods Description
CreateInstance Creates an uninitialized object.
LockServer Locks object application open in memory.

Table 8-7:  IClassFactory2 Methods

IClassFactory2() Methods Description
GetLicInfo Fills a LICINFO structure with information on the licensing capabilities of this class factory.
RequestLicKey Creates and returns a license key that the caller can save and use later in calls to IClassFactory2::CreateInstanceLic().
CreateInstanceLic Creates an instance of the licensed object given a license key from IClassFactory2::RequestLicKey().

See Also

IClassFactory()  

IClassFactory2::CreateInstanceLic()

NAME

IClassFactory2::CreateInstanceLic() - Creates an instance of the object class supported by this class factory, given a license key previously obtained from IClassFactory2::RequestLicKey(). This method is the only possible means to create an object on an otherwise unlicensed machine.

Synopsis

#include <ocidl.h>

HRESULT CreateInstanceLic(
        IUnknown * pUnkOuter,
        IUnknown * pUnkReserved,
        REFIID riid,
        BSTR bstrKey,
        void ** ppvObject );

Description

Notes to Implementors

If the class factory does not provide a license key (that is, IClassFactory2::RequestLicKey() returns E_NOTIMPL and the fRuntimeKeyAvail field in LICINFO is set to FALSE in IClassFactory2::GetLicInfo()), then this method can also return E_NOTIMPL. In such cases, the class factory is implementing IClassFactory2() simply to specify whether or not the machine is licensed at all through the fLicVerified field of LICINFO.

Parameters

pUnkOuter

[in] Pointer to the controlling IUnknown() interface on the outer unknown if this object is being created as part of an aggregate. If the object is not part of an aggregate, this parameter must be NULL.

pUnkReserved

[in] Unused. Must be NULL.

riid

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

bstrKey

[in] Run-time license key previously obtained from IClassFactory2::RequestLicKey() that is required to create an object.

ppvObject

[out] Indirect pointer to the interface of the type specified in riid. This parameter is set to NULL on failure.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK

The license was successfully created.

E_NOTIMPL

This method is not implemented because objects can only be created on fully licensed machines through IClassFactory::CreateInstance().

E_POINTER

The pointers passed in bstrKey or ppvObject are not valid. For example, it may be NULL.

E_NOINTERFACE

The object can be created (and the license key is valid) except the object does not support the interface specified by riid.

CLASS_E_NOAGGREGATION

The pUnkOuter parameter is non-NULL, but this object class does not support aggregation.

CLASS_E_NOTLICENSED

The key provided in bstrKey is not a valid license key.

See Also

IClassFactory2::GetLicInfo(), IClassFactory2::RequestLicKey(), LICINFO  

IClassFactory2::GetLicInfo()

NAME

IClassFactory2::GetLicInfo() - Fills a caller-allocated LICINFO structure with information describing the licensing capabilities of this class factory.

Synopsis

#include <ocidl.h>

HRESULT GetLicInfo(
        LICINFO * pLicInfo );

Description

Notes to Implementors

E_NOTIMPL is not allowed as a return value since this method provides critical information for the client of a licensed class factory.

Parameters

pLicInfo

[out] Pointer to the caller-allocated LICINFO structure to be filled on output.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK

The LICINFO structure was successfully filled in.

E_POINTER

The address in pLicInfo is not valid. For example, it may be NULL.

See Also

IClassFactory2::CreateInstanceLic(), IClassFactory2::RequestLicKey(), LICINFO  

IClassFactory2::RequestLicKey()

NAME

IClassFactory2::RequestLicKey() - If the fRuntimeKeyAvail field in LICINFO has been returned as TRUE from IClassFactory2::GetLicInfo(), then this method creates and returns a license key. The caller can save the license key persistently and use it later in calls to IClassFactory2::RequestLicKey().

Synopsis

#include <ocidl.h>

HRESULT RequestLicKey(
        DWORD dwReserved ,
        BSTR * pbstrKey );

Description

The caller can save the license key for subsequent calls to IClassFactory2::CreateInstanceLic() to create objects on an otherwise unlicensed machine.

Notes to Callers

The caller must free the BSTR with SysFreeString() when the key is no longer needed. The value of fRuntimeKeyAvail is returned through a previous call to IClassFactory2::GetLicInfo().

Notes to Implementors

This method allocates the BSTR key with SysAllocString() or SysAllocString[Len], and the caller becomes responsible for this BSTR once this method returns successfully.

This method need not be implemented when a class factory does not support run-time license keys.

Parameters

dwReserved

[in] Unused. Must be zero.

pbstrKey

[out] Pointer to the caller-allocated BSTR variable that receives the callee-allocated license key on successful return from this method. This parameter is set to NULL on any failure.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK

The license key was successfully created.

E_NOTIMPL

This class factory does not support run-time license keys.

E_POINTER

The address in pbstrKey is not valid. For example, it may be NULL.

CLASS_E_NOTLICENSED

This class factory supports run-time licensing, but the current machine itself is not licensed. Thus, a run-time key is not available on this machine.

See Also

IClassFactory2::CreateInstanceLic(), IClassFactory2::GetLicInfo(), LICINFO

8.10.4    IExternalConnection

The IExternalConnection() interface enables an embedded object to keep track of external locks on it, thereby enabling the safe and orderly shutdown of the object following silent updates. An object that supports links either to itself or to some portion of itself (a range of cells in a spreadsheet, for example) should implement this interface to prevent possible loss of data during shutdown.

Such data loss can occur when an object happens to have unsaved changes at a time when its stub manager's count of strong external references has reached zero. This situation would arise, for example, at the end of a silent update, when the final link client breaks its connection to the object. With the severing of this connection, the stub manager's count of strong external references would reach zero, causing it to release its pointers to an interface on the object and initiate shutdown of the object.

If the object manages its own count of external locks, rather than relying on the stub manager to do so, it can save its data before the stub manager has a chance to release its pointers. An object can obtain a count of external connections by implementing the IExternalConnection() interface. The stub manager calls this interface whenever a new strong external reference is added or deleted. The object combines this count with its own tally of strong internal references to maintain an accurate total of all locks.

When to Implement

All embeddable compound-document objects that support links to themselves or portions of themselves should implement IExternalConnectionto prevent possible data loss during shutdown. In addition, an in-place container should call OleLockRunning() to hold a strong lock on its embedded objects.

When to Use

An object's stub manager should call IExternalConnection() whenever an external connection is added or released.

Table 8-8:  IExternalConnection Methods in VTable Order

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

Table 8-9:  IExternalConnection Methods

IExternalConnection() Methods Description
AddConnection Increments count of external locks.
ReleaseConnection Decrements count of external locks.

 

IExternalConnection::AddConnection()

NAME

IExternalConnection::AddConnection() - Increments an object's count of its strong external connections (links).

Synopsis

#include <objidl.h>

HRESULT AddConnection(
        DWORD exconn,
        DWORD dwreserved );

Description

An object created by a EXE object server relies on its stub manager to call IExternalConnection::AddConnection() whenever a link client activates and therefore creates an external lock on the object. When the link client breaks the connection, the stub manager calls IExternalConnection::ReleaseConnection() to release the lock.

Since DLL object applications exist in the same process space as their objects, they do not use RPC (remote procedure calls) and therefore do not have stub managers to keep track of external connections. Therefore, DLL servers that support external links to their objects must implement IExternalConnection() so link clients can directly call the interface to inform them when connections are added or released.

The following is a typical implementation for the AddConnection method:

DWORD XX::AddConnection(DWORD extconn, DWORD dwReserved)
{
    return extconn&EXTCONN_STRONG ? ++m_cStrong : 0;
}

Parameters

exconn

[in] Type of external connection to the object. The only type of external connection currently supported by this interface is strong, which means that the object must remain alive as long as this external connection exists. Strong external connections are represented by the value EXTCONN_STRONG = 0x0001, which is defined in the enumeration EXTCONN.

dwreserved

[in] Passes information about the connection. This parameter is reserved for use by COM. Its value can be zero, but not necessarily. Therefore, implementations of AddConnection should not contain blocks of code whose execution depends on whether a zero value is returned.

Return Values

DWORD value

The number of reference counts on the object; used for debugging purposes only.

See Also

IExternalConnection::ReleaseConnection(), IRunnableObject::LockRunning()  

IExternalConnection::ReleaseConnection()

NAME

IExternalConnection::ReleaseConnection() - Decrements an object's count of its strong external connections (references).

Synopsis

#include <objidl.h>

HRESULT ReleaseConnection(
        DWORD extconn,
        DWORD dwreserved,
        BOOL fLastReleaseCloses );

Description

If fLastReleaseCloses equals TRUE, calling ReleaseConnection causes the object to shut itself down. Calling this method is the only way in which a DLL object, running in the same process space as the container application, will know when to close following a silent update.

Parameters

extconn

[in] Type of external connection to the object. The only type of external connection currently supported by this interface is strong, which means that the object must remain alive as long as this external connection exists. Strong external connections are represented by the value EXTCONN_STRONG = 0x0001, which is defined in the enumeration EXTCONN.

dwreserved

[in] Passes information about the connection. This parameter is reserved for use by COM. Its value can be zero, but not necessarily. Therefore, implementations of ReleaseConnection should not contain blocks of code whose execution depends on whether a zero value is returned.

fLastReleaseCloses

[in] TRUE specifies that if the connection being released is the last external lock on the object, the object should close. FALSE specifies that the object should remain open until closed by the user or another process.

Return Values

DWORD value

The number of connections to the object; used for debugging purposes only.

See Also

IExternalConnection::AddConnection()

8.11    COM Server Related API Descriptions

 

CoDisconnectObject()

NAME

CoDisconnectObject() - Disconnects all remote process connections being maintained on behalf of all the interface pointers that point to a specified object. Only the process that actually manages the object should call CoDisconnectObject().

Synopsis

#include <objbase.h>

STDAPI CoDisconnectObject((
        IUnknown * pUnk,
        DWORD dwReserved );

Description

The CoDisconnectObject() function enables a server to correctly disconnect all external clients to the object specified by pUnk.

The CoDisconnectObject() function performs the following tasks:

  1. Checks to see if the object to be disconnected implements the IMarshal() interface. If so, it gets the pointer to that interface; if not, it gets a pointer to the standard marshaler's (i.e., COM's) IMarshal() implementation.

  2. Using whichever IMarshal() interface pointer it has acquired, the function then calls IMarshal::DisconnectObject() to disconnect all out-of-process clients.

An object's client does not call CoDisconnectObject() to disconnect itself from the server (clients should use IUnknown::Release() for this purpose). Rather, an COM server calls CoDisconnectObject() to forcibly disconnect an object's clients, usually in response to a user closing the server application.

Similarly, a COM container that supports external links to its embedded objects can call CoDisconnectObject() to destroy those links. Again, this call is normally made in response to a user closing the application. CoDisconnectObject() does not necessarily disconnect out-of-process clients immediately. If any marshaled calls are pending on the server object, CoDisconnectObject() disconnects the object only when those calls have returned. In the meantime, CoDisconnectObject() sets a flag that causes any new marshaled calls to return CO_E_OBJECTNOTCONNECTED.

Parameters

pUnk

[in] Pointer to any IUnknown-derived interface on the object to be disconnected.

dwReserved

[in] Reserved for future use; must be zero.

Return Values

S_OK

All connections to remote processes were successfully deleted.

See Also

IMarshal::DisconnectObject()  

CoLockObjectExternal()

NAME

CoLockObjectExternal() - Called either to lock an object to ensure that it stays in memory, or to release such a lock. Call CoLockObjectExternal() to place a strong lock on an object to ensure that it stays in memory.

Synopsis

#include <objbase.h>

STDAPI CoLockObjectExternal((
        IUnknown * pUnk,
        BOOL fLock,
        BOOL fLastUnlockReleases );

Description

The CoLockObjectExternal function prevents the reference count of an object from going to zero, thereby ``locking'' it into existence until the lock is released. The same function (with different parameters) releases the lock. The lock is implemented by having the system call IUnknown::AddRef() on the object. The system then waits to call IUnknown::Release() on the object until a later call to CoLockObjectExternal() with fLock set to FALSE. This function can be used to maintain a reference count on the object on behalf of the end user, because it acts outside of the object, as does the user.

The CoLockObjectExternal function must be called in the process in which the object actually resides (the EXE process, not the process in which handlers may be loaded).

Calling CoLockObjectExternal() sets a strong lock on an object. A strong lock keeps an object in memory, while a weak lock does not. Strong locks are required, for example, during a silent update to an OLE embedding. The embedded object's container must remain in memory until the update process is complete. There must also be a strong lock on an application object to ensure that the application stays alive until it has finished providing services to its clients. All external references place a strong reference lock on an object.

The CoLockObjectExternal function is typically called in the following situations:

There are several things to be aware of when you use CoLockObjectExternal() in the implementation of IOleContainer::LockContainer. An embedded object would call IOleContainer::LockContainer on its container to keep it running (to lock it) in the absence of other reasons to keep it running. When the embedded object becomes visible, the container must weaken its connection to the embedded object with a call to the OleSetContainedObject function, so other connections can affect the object.

Unless an application manages all aspects of its application and document shutdown completely with calls to CoLockObjectExternal(), the container must keep a private lock count in IOleContainer::LockContainer so that it exits when the lock count reaches zero and the container is invisible. Maintaining all aspects of shutdown, and thereby avoiding keeping a private lock count, means that CoLockObjectExternal() should be called whenever one of the following conditions occur:

For debugging purposes, it may be useful to keep a count of the number of external locks (and unlocks) set on the application.

The end user has explicit control over the lifetime of an application, even if there are external locks on it. That is, if a user decides to close the application (File, Exit), it must shut down. In the presence of external locks (such as the lock set by CoLockObjectExternal()), the application can call the CoDisconnectObject() function to force these connections to close prior to shutdown.

Parameters

pUnk

[in] Pointer to the IUnknown() interface on the object to be locked or unlocked.

fLock

[in] Whether the object is to be locked or released. Specifying TRUE holds a reference to the object (keeping it in memory), locking it independently of external or internal AddRef/Release operations, registrations, or revocations. If fLock is TRUE, fLastLockReleases is ignored. FALSE releases a lock previously set with a call to this function.

fLastUnlockReleases

[in] Whether a given lock is the last reference that is supposed to keep an object alive. If it is, TRUE releases all pointers to the object (there may be other references that are not supposed to keep it alive).

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK

The object was locked successfully.

 

CoRevokeClassObject()

NAME

CoRevokeClassObject() - Informs COM that a class object, previously registered with the CoRegisterClassObject() function, is no longer available for use.

Synopsis

#include <objbase.h>

HRESULT CoRevokeClassObject((
        DWORD dwRegister );

Description

A successful call to CoRevokeClassObject() means that the class object has been removed from the global class object table (although it does not release the class object). If other clients still have pointers to the class object and have caused the reference count to be incremented by calls to IUnknown::AddRef(), the reference count will not be zero. When this occurs, applications may benefit if subsequent calls (with the obvious exceptions of IUnknown::AddRef() and IUnknown::Release()) to the class object fail.

An object application must call CoRevokeClassObject() to revoke registered class objects before exiting the program. Class object implementers should call CoRevokeClassObject() as part of the release sequence. You must specifically revoke the class object even when you have specified the flags value REGCLS_SINGLEUSE in a call to CoRegisterClassObject(), indicating that only one application can connect to the class object.

Parameters

dwRegister

[in] Token previously returned from the CoRegisterClassObject() function.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK

The class object was successfully revoked.

See Also

CoGetClassObject(), CoRegisterClassObject()  

CoRegisterClassObject()

NAME

CoRegisterClassObject() - Registers an EXE class object with COM so other applications can connect to it. EXE object applications should call CoRegisterClassObject() on startup. It can also be used to register internal objects for use by the same EXE or other code (such as DLLs) that the EXE uses.

Synopsis

#include <objbase.h>

STDAPI CoRegisterClassObject((
        REFCLSID rclsid,
        IUnknown * pUnk,
        DWORD dwClsContext,
        DWORD flags,
        LPDWORD * lpdwRegister );

Description

Only EXE object applications call CoRegisterClassObject(). Object handlers or DLL object applications do not call this function--instead, they must implement and export the DllGetClassObject() function.

At startup, a multiple-use EXE object application must create a class object (with the IClassFactory() interface on it), and call CoRegisterClassObject() to register the class object. Object applications that support several different classes (such as multiple types of embeddable objects) must allocate and register a different class object for each.

Multiple registrations of the same class object are independent and do not produce an error. Each subsequent registration yields a unique key in lpdwRegister.

Multiple document interface (MDI) applications must register their class objects. Single document interface (SDI) applications must register their class objects only if they can be started by means of the /Embedding switch.

The server for a class object should call CoRevokeClassObject() to revoke the class object (remove its registration) when all of the following are true:

After the class object is revoked, when its reference count reaches zero, the class object can be released, allowing the application to exit.

For information on the flags parameter, refer to the REGCLS enumeration.

Parameters

rclsid

[in] CLSID to be registered.

pUnk

[in] Pointer to the IUnknown() interface on the class object whose availability is being published.

dwClsContext

[in] Context in which the executable code is to be run. For information on these context values, see the CLSCTX enumeration.

flags

[in] How connections are made to the class object. For information on these flags, see the REGCLS enumeration.

lpdwRegister

[out] Pointer to a value that identifies the class object registered; later used by the CoRevokeClassObject() function to revoke the registration.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK

The class object was registered successfully.

CO_E_OBJISREG

Already registered in the class object table.

See Also

CoGetClassObject(), CoRevokeClassObject(), DllGetClassObject(), REGCLS, CLSCTX  

DllCanUnloadNow()

NAME

DllCanUnloadNow() - Determines whether the DLL that implements this function is in use. If not, the caller can safely unload the DLL from memory.

Synopsis

#include <objbase.h>

STDAPI DllCanUnloadNow(( );

Description

A call to DllCanUnloadNow() determines whether the DLL from which it is exported is still in use. A DLL is no longer in use when it is not managing any existing objects (the reference count on all of its objects is 0).

COM does not provide this function. DLLs that support the Component Object Model (COM) should implement and export DllCanUnloadNow().

Notes to Callers

You should not have to call DllCanUnloadNow() directly. COM calls it only through a call to the CoFreeUnusedLibraries() function. When it returns S_OK, CoFreeUnusedLibraries() safely frees the DLL.

Notes to Implementors

You need to implement DllCanUnloadNow() in, and export it from, DLLs that are to be dynamically loaded through a call to the CoGetClassObject() function. (You also need to implement and export the DllGetClassObject() function in the same DLL).

If a DLL loaded through a call to CoGetClassObject() fails to export DllCanUnloadNow(), the DLL will not be unloaded until the application calls the CoUninitialize() function to release the COM libraries.

If the DLL links to another DLL, returning S_OK from DllCanUnloadNow() will also cause the second, dependent DLL to be unloaded. To eliminate the possibility of a crash, the primary DLL should call the CoLoadLibrary() function, specifying the path to the second DLL as the first parameter, and setting the auto free parameter to TRUE. This forces the COM library to reload the second DLL and set it up for a call to CoFreeUnusedLibraries() to free it separately when appropriate.

DllCanUnloadNow() should return S_FALSE if there are any existing references to objects that the DLL manages.

Return Values

S_OK

The DLL can be unloaded.

S_FALSE

The DLL cannot be unloaded now.

See Also

DllGetClassObject()  

DllGetClassObject()

NAME

DllGetClassObject() - Retrieves the class object from a DLL object handler or object application. DllGetClassObject() is called from within the CoGetClassObject() function when the class context is a DLL.

Synopsis

#include <objbase.h>

STDAPI DllGetClassObject((
        REFCLSID rclsid,
        REFIID riid,
        LPVOID * ppv );

Description

If a call to the CoGetClassObject() function finds the class object that is to be loaded in a DLL, CoGetClassObject() uses the DLL's exported DllGetClassObject() function.

COM does not provide this function. DLLs that support the COM Component Object Model (COM) must implement DllGetClassObject() in COM object handlers or DLL applications.

Notes to Callers

You should not call DllGetClassObject() directly. When an object is defined in a DLL, CoGetClassObject() calls the CoLoadLibrary() function to load the DLL, which, in turn, calls DllGetClassObject().

Notes to Implementors

You need to implement DllGetClassObject() in (and export it from) DLLs that support the Component Object Model.

Parameters

rclsid

[in] CLSID that will associate the correct data and code.

riid

[in] Reference to the identifier of the interface that the caller is to use to communicate with the class object. Usually, this is IID_IClassFactory (defined in the COM headers as the interface identifier for IClassFactory()).

ppv

[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer. If an error occurs, the interface pointer is NULL.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK

The object was retrieved successfully.

CLASS_E_CLASSNOTAVAILABLE

The DLL does not support the class (object definition).

Examples

Following is an example (in C++) of an implementation of DllGetClassObject(). In this example, DllGetClassObject() creates a class object and calls its QueryInterface() method to retrieve a pointer to the interface requested in riid. The implementation safely releases the reference it holds to the IClassFactory() interface because it returns a reference-counted pointer to IClassFactory() to the caller.

HRESULT_export  PASCAL DllGetClassObject() 
        (REFCLSID rclsid, REFIID riid, LPVOID * ppvObj) 
{ 
    HRESULT hres = E_OUTOFMEMORY; 
    *ppvObj = NULL; 
    CClassFactory *pClassFactory = new CClassFactory(rclsid); 
    if (pClassFactory != NULL)   { 
        hRes = pClassFactory->QueryInterface(riid, ppvObj); 
        pClassFactory->Release(); 
    } 
    return hRes; 
}

See Also

CoGetClassObject(), DllCanUnloadNow  

DllRegisterServer()

NAME

DllRegisterServer() - Instructs an in-process server to create its registry entries for all classes supported in this server module. If this function fails, the state of the registry for all its classes is indeterminate.

Synopsis

#include <olectl.h>

STDAPI DllRegisterServer((
        void );

Description

E_NOTIMPL is not a valid return code.

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK

The registry entries were created successfully.

SELFREG_E_TYPELIB

The server was unable to complete the registration of all the type libraries used by its classes.

SELFREG_E_CLASS

The server was unable to complete the registration of all the object classes.

See Also

DllUnregisterServer()  

DllUnregisterServer()

NAME

DllUnregisterServer() - Instructs an in-process server to remove only those entries created through DllRegisterServer().

Synopsis

#include <olectl.h>

STDAPI DllUnregisterServer((
        void );

Description

The server must not disturb any entries that it did not create which currently exist for its object classes. For example, between registration and unregistration, the user may have specified a TreatAs relationship between this class and another. In that case, unregistration can remove all entries except the TreatAs key and any others that were not explicitly created in DllRegisterServer(). The Win32 registry functions specifically disallow the deletion of an entire populated tree in the registry. The server can attempt, as the last step, to remove the CLSID key, but if other entries still exist, the key will remain.

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK

The registry entries were created successfully.

S_FALSE

Unregistration of this server's known entries was successful, but other entries still exist for this server's classes.

SELFREG_E_TYPELIB

The server was unable to remove the entries of all the type libraries used by its classes.

SELFREG_E_CLASS

The server was unable to remove the entries of all the object classes.

See Also

DllRegisterServer()  

CoAddRefServerProcess()

NAME

CoAddRefServerProcess() - Increments a global per-process reference count.

Synopsis

#include <objbase.h>

ULONG CoAddRefServerProcess((
        void );

Description

Servers can call CoAddRefServerProcess() to increment a global per-process reference count. This function is particularly helpful to servers that are implemented with multiple threads, either multi-apartmented or free-threaded. Servers of these types must coordinate the decision to shut down with activation requests across multiple threads. Calling CoAddRefServerProcess() increments a global per-process reference count, and calling CoReleaseServerProcess() decrements that count.

When that count reaches zero, COM automatically calls CoSuspendClassObjects(), which prevents new activation requests from coming in. This permits the server to deregister its class objects from its various threads without worry that another activation request may come in. New activation requests result in launching a new instance of the local server process.

The simplest way for a local server application to make use of these API functions is to call CoAddRefServerProcess() in the constructor for each of its instance objects, and in each of its IClassFactory::LockServer() methods when the fLock parameter is TRUE. The server application should also call CoReleaseServerProcess() in the destruction of each of its instance objects, and in each of its IClassFactory::LockServer() methods when the fLock parameter is FALSE. Finally, the server application should pay attention to the return code from CoReleaseServerProcess() and if it returns 0, the server application should initiate its cleanup, which, for a server with multiple threads, typically means that it should signal its various threads to exit their message loops and call CoRevokeClassObject() and CoUninitialize().

If these APIs are used at all, they must be called in both the object instances and the LockServer method, otherwise the server application may be shut down prematurely. In-process servers typically should not call CoAddRefServerProcess() or CoReleaseServerProcess().

Return Values

S_OK

The CLSID was retrieved successfully.

See Also

CoReleaseServerProcess(), IClassFactory::LockServer(), Out-of-process Server Implementation Helpers  

CoReleaseServerProcess()

NAME

CoReleaseServerProcess() - Decrements the global per-process reference count.

Synopsis

#include <objbase.h>

ULONG CoReleaseServerProcess((
        void );

Description

Servers can call CoReleaseServerProcess() to decrement a global per-process reference count incremented through a call to CoAddRefServerProcess()

When that count reaches zero, COM automatically calls CoSuspendClassObjects(), which prevents new activation requests from coming in. This permits the server to deregister its class objects from its various threads without worry that another activation request may come in. New activation requests result in launching a new instance of the local server process.

The simplest way for a local server application to make use of these API functions is to call CoAddRefServerProcess() in the constructor for each of its instance objects, and in each of its IClassFactory::LockServer() methods when the fLock parameter is TRUE. The server application should also call CoReleaseServerProcess() in the destruction of each of its instance objects, and in each of its IClassFactory::LockServer() methods when the fLock parameter is FALSE. Finally, the server application must check the return code from CoReleaseServerProcess(); if it returns 0, the server application should initiate its cleanup. This typically means that a server with multiple threads should signal its various threads to exit their message loops and call CoRevokeClassObject() and CoUninitialize().

If these APIs are used at all, they must be called in both the object instances and the LockServer method, otherwise the server application may be shutdown prematurely. In-process Servers typically should not call CoAddRefServerProcess() or CoReleaseServerProcess().

Return Values

0

The server application shouild initiate its cleanup.

Other Values

Server application should not yet initiate its cleanup.

See Also

CoSuspendClassObjects(), CoReleaseServerProcess(), IClassFactory::LockServer(), Out-of-process Server Implementation Helpers  

CoResumeClassObjects()

NAME

CoResumeClassObjects() - Called by a server that can register multiple class objects to inform the COM SCM about all registered classes, and permits activation requests for those class objects.

Synopsis

#include <objbase.h>

WINOLEAPI CoResumeClassObjects((
        void );

Description

Servers that can register multiple class objects call CoResumeClassObjects() once, after having first called CoRegisterClassObject(), specifying REGCLS_LOCAL_SERVER | REGCLS_SUSPENDED for each CLSID the server supports. This function causes COM to inform the SCM about all the registered classes, and begins letting activation requests into the server process.

This reduces the overall registration time, and thus the server application startup time, by making a single call to the SCM, no matter how many CLSIDs are registered for the server. Another advantage is that if the server has multiple apartments with different CLSIDs registered in different apartments, or is a free-threaded server, no activation requests will come in until the server calls CoResumeClassObjects(). This gives the server a chance to register all of its CLSIDs and get properly set up before having to deal with activation requests, and possibly shutdown requests.

Return Values

S_OK

The CLSID was retrieved successfully.

See Also

CoRegisterClassObject(), CoSuspendClassObjects(), Out-of-process Server Implementation Helpers  

CoSuspendClassObjects()

NAME

CoSuspendClassObjects() - Prevents any new activation requests from the SCM on all class objects registered within the process.

Synopsis

#include <objbase.h>

WINOLEAPI CoSuspendClassObjects((
        void );

Description

CoSuspendClassObjects() prevents any new activation requests from the SCM on all class objects registered within the process. Even though a process may call this API, the process still must call CoRevokeClassObject() for each CLSID it has registered, in the apartment it registered in. Applications typically do not need to call this API, which is generally only called internally by COM when used in conjunction with CoReleaseServerProcess().

Return Values

S_OK

The CLSID was retrieved successfully.

See Also

CoRevokeClassObject(), CoReleaseServerProcess(), Out-of-process Server Implementation Helpers

8.12    COM Server Related Structure Definitions

 

LICINFO()

NAME

LICINFO() - The LICINFO structure contains parameters that describe the licensing behavior of a class factory that supports licensing. The structure is filled during the IClassFactory2::GetLicInfo() method.

Synopsis

#include <ocidl.h>

( );

typedef struct tagLICINFO
    {
    ULONG cbLicInfo;
    BOOL  fRuntimeKeyAvail;
    BOOL  fLicVerified;
    } LICINFO;

Members

cbLicInfo

Size of the LICINFO structure.

fRuntimeKeyAvail

Whether this class factory allows the creation of its objects on a unlicensed machine through the use of a license key. If TRUE, IClassFactory2::RequestLicKey() can be called to obtain the key. If FALSE, objects can be created only on a fully licensed machine.

fLicVerified

Whether a full machine license exists so that calls to IClassFactory::CreateInstance() and IClassFactory2::RequestLicKey() will succeed. If TRUE, the full machine license exists. Thus, objects can be created freely. and a license key is available if fRuntimeKeyAvail is also TRUE. If FALSE, this class factory cannot create any instances of objects on this machine unless the proper license key is passed to IClassFactory2::CreateInstanceLic().

See Also

IClassFactory::CreateInstance(), IClassFactory2::CreateInstanceLic(), IClassFactory2::GetLicInfo(), IClassFactory2::RequestLicKey()

8.13    COM Server Related Enumeration Definitions

8.13.1    REGCLS

The REGCLS enumeration defines values used in CoRegisterClassObject() to control the type of connections to a class object. It is defined as follows:

typedef enum tagREGCLS 
{ 
    REGCLS_SINGLEUSE         = 0, 
    REGCLS_MULTIPLEUSE       = 1, 
    REGCLS_MULTI_SEPARATE    = 2, 
    REGCLS_SUSPENDED         = 4, 
    REGCLS_SURROGATE         = 8, 
} REGCLS;

Elements

REGCLS_SINGLEUSE

Once an application is connected to a class object with CoGetClassObject(), the class object is removed from public view so that no other applications can connect to it. This value is commonly used for single document interface (SDI) applications. Specifying this value does not affect the responsibility of the object application to call CoRevokeClassObject(); it must always call CoRevokeClassObject() when it is finished with an object class.

REGCLS_MULTIPLEUSE

Multiple applications can connect to the class object through calls to CoGetClassObject(). If both the REGCLS_MULTIPLEUSE and CLSCTX_LOCAL_SERVER are set in a call to CoRegisterClassObject(), the class object is also automatically registered as an in-process server, whether or not CLSCTX_INPROC_SERVER is explicitly set.

REGCLS_MULTI_SEPARATE

Multiple applications can connect to the class object through calls to CoGetClassObject(). If REGCLS_MULTI_SEPARATE is set, each execution context must be set separately: CoRegisterClassObject() does not automatically register an out-of-process server (for which CLSCTX_LOCAL_SERVER is set) as an in-process server. When a class object is registered with only the REGCLS_MULTI_SEPARATE and CLSCTX_LOCAL_SERVER flags set and the server tries to bind to an object with its own CLSID, another instance of the server is started.

REGCLS_SUSPENDED

Suspends registration and activation requests for the specified CLSID until there is a call to CoResumeClassObjects(). This is used typically to register the CLSIDs for servers that can register multiple class objects to reduce the overall registration time, and thus the server application startup time, by making a single call to the SCM, no matter how many CLSIDs are registered for the server.

REGCLS_SURROGATE

The class object is a surrogate process used to run DLL servers. The class factory registered by the surrogate process is not the actual class factory implemented by the DLL server, but a generic class factory implemented by the surrogate. This generic class factory delegates instance creation and marshaling to the class factory of the DLL server running in the surrogate.

Description

In CoRegisterClassObject(), members of both the REGCLS and the CLSCTX enumerations, taken together, determine how the class object is registered.

An EXE surrogate (in which DLL servers are run) calls CoRegisterClassObject() to register a class factory using a new REGCLS value, REGCLS_SURROGATE.

The following table summarizes the allowable REGCLS value combinations and the object registrations affected by the combinations.

Table 8-10:  Allowable REGCLS Combinations

REGCLS_ SINGLEUSE REGCLS_ MULTIPLEUSE REGCLS_ MULTI_ SEPARATE Other
CLSCTX_INPROC_SERVER Error In-process In-process Error
CLSCTX_LOCAL_SERVER Local In-process/ local Local Error
Both of the above Error In-process/ local In-process/ local Error
Other Error Error Error Error

All class factories for dll surrogates should be registered with REGCLS_SURROGATE set. Do not set REGCLS_SINGLUSE or REGCLS_MULTIPLEUSE when you register a surrogate for DLL servers.

See Also

CoGetClassObject(), CoRegisterClassObject(), CoRevokeClassObject(), DllGetClassObject()