Managing Object Lifetimes in OLE

Douglas Hodges

Created: August 25, 1994
Revised: November 18, 1994

The Problem

Managing the lifetimes of objects is easy when every object is written by the same programmer and everything runs within a single application. In this situation the programmer can do anything he wants. There does not need to be any formalized conventions of how the lifetimes of objects are managed. The programmer knows exactly when a particular object is no longer needed and can directly free the object whenever he wants (for example, by calling delete pObj).

The problem becomes an order of magnitude more complex when the application runs as its own process (let's say single-threaded, for simplicity) and is built up of a set of components that are written by separate organizations. In this case an individual programmer cannot make assumptions about how/when a particular component/object (in this discussion I will use these terms rather freely interchangeably) is able to be destroyed. To solve this problem, OLE uses a standard mechanism called reference counting. With reference counting, basically, rules are established as to when the refcount of an object must be incremented and when it must be decremented; and further, it is defined that whenever an object has a positive refcount, the object's memory must remain valid. When the object's refcount transitions to zero the object may then be destroyed and its memory/resources freed. The main principle behind these rules is to allow a client of an interface to be guaranteed that he will not GP Fault when he dereferences the pointer. If the application runs as a standard application started by the user and later shut down by the user, then reference counting objects is all that is required to manage the lifetime of objects within the process (assuming there are no reference counting bugs!).

The problem becomes an additional order of magnitude more complex when there are connections between objects in different processes (similar issues would apply to connections between objects in different threads, however, for this discussion I will only concentrate on single-threaded processes). OLE introduces many scenarios that involve communication between objects in different processes:

In these scenarios, the lifetime of the object that has connections from outside of its process must be managed properly. These scenarios cannot be solved by using reference counts alone. These are the scenarios that are the subject of this discussion.

Organization of This Document

I begin first with a explanation of why simple reference counting (AddRef/Release) is not sufficient to manage these scenarios. I will use one of the key scenarios involving Compound Documents to illustrate the basic problem. I will try to build up the scenario slowly, showing only enough detail to make my point. Often in the diagrams I will leave out details that I think will only distract the reader from the main point. Next I will discuss, in general terms, the basic strategy for handling these scenarios involving remote connections. I will concentrate on how the closing of a top-level object or document should be managed. I will discuss the remote mechanism used by OLE and how OLE tries to automatically handle these scenarios on behalf of the object. I will explain when OLE's automatic built-in mechanism works and when it is necessary to actively manage the problem. Initially I will ignore the detail of the difference between organizing the shutdown of the application versus the shutdown of the document/object. Later I will add the complexity of managing the application. I will discuss what the ideal shutdown behaviors are for the various styles of applications that we have (for example, SDI vs. MDI). I will then show a strategy of implementing these behaviors. Appendix B consists of sample code that implements the techniques discussed in this paper.

Definition of Terms

There is a lot of OLE terminology that I will use in this paper. It is best if the reader has a good general understanding of OLE and the Component Object Model (COM) before trying to tackle the subject discussed in this paper. But in order to assist the reader, following are definitions of some of the key terms and concepts that will be used in this discussion:

Term Definition
Compound Document Refers to application documents that can embed, link, or be embedded. These are the classic scenarios for traditional OLE. These scenarios are governed by standard user model guidelines. A Microsoft Excel object embedded in a Word document is a typical example of a Compound Document scenario. This term is used to distinguish from the more general object uses of OLE and COM.
LocalServer object Object implemented with a server EXE.
InprocServer object Object implemented with a server DLL.
CLSID GUID (globally unique identifier) used to identify a class of an OLE object.
IID GUID used to identify an interface.
QI [verb] Abbreviation for QueryInterface. This is used as a verb as in the sentence, "You must first QI for interface IOleContainer and then call the method IOleContainer::LockContainer."
COM identity A single COM object that is defined by the set of interfaces that can be retrieved via IUnknown::QueryInterface and that accept remote connections from clients in other processes. All of the interfaces in this set must return the same IUnknown* pointer if QI'ed for IID_IUnknown. This IUnknown* pointer represents the identity of the object to OLE. The notion of a COM identity is fundamental to the principles of OLE "remoting." In the shipping versions of OLE 2, a single COM identity can only live in one process. In the future this restriction may be lifted. An object that is able to accept remote connections is defined as a COM identity that lives in a particular process. All clients that establish a remote connection to an object get a special object that acts as a stand-in for the real object in the client's process. This stand-in object is called a proxy or handler. Each handler has a communication link back to the original object. A handler object cannot have remote connections connected to it. When a pointer to a handler object is passed to a third process, the destination process gets a new handler that is also connected with the real object in the original process. Thus a handler object is not considered to be a COM identity.
Marshaling an interface pointer The act of passing an interface pointer from one process to another. In the act of marshaling an interface pointer, a proxy/stub pair is created if they do not already exist.
Proxy/stub A special object pair that is used to manage the low-level details of "remoting" a particular interface. These objects handle the marshaling of parameters and return values for each method of the interface from one process to another. The proxy lives in the client's (caller's) process, and the stub lives in the object's (callee's) process.
Handler or object handler A special object that acts as a stand-in for a real object in a client process.
DefHandler The special implementation of a handler provided by OLE that is used by practically every LocalServer object that supports the Compound Document interfaces: IOleObject, IDataObject, IViewObject, and so on.
ProxyManager The special implementation of a handler provided by OLE that is used when remoting general objects that do not support the Compound Document object interfaces: IOleObject, IDataObject, IViewObject, and so on. The ProxyManager manages all client-side aspects of a remote connection to an object. A ProxyManager is used as part of the implementation of the DefHandler.
StubManager A special object implemented by OLE that manages all remote connections to a particular COM identity. The StubManager lives in the process of the object. The StubManager holds references to the object.
Pseudo-object An object that is a sub-piece of a larger whole object. Pseudo-objects often do not have their own identifiable storage. They cannot live independently of the whole object.

Why Is Reference Counting Not Enough?

In order to illustrate why reference counting is not sufficient, I would like to use one of the key scenarios involving Compound Documents in OLE: a silent-update of a link to an embedded object. This was a driving scenario for OLE 2 that was not possible with OLE 1. Let me build up this scenario in stages.

Visio Object Embedded in Word

Consider that I have a Visio object embedded in a Word document called FOO.DOC. Both Visio and Word are implemented using .EXEs. In this scenario, Visio is the embedded object and, in OLE lingo, is implemented as a LocalServer (that is, an .EXE rather than a .DLL). The document FOO.DOC is the embedding container and is managed by the Word application. The following picture shows the in-memory data structures at the time when the Word document is launched by the user and the Visio object is running.

Visio Object Embedded in Word as Link Source

Continuing with the same example, now consider that the Visio object embedded in FOO.DOC is the source of a link in a third document, say a Microsoft Excel worksheet called BAR.XLS. The user might create such a link by taking the following steps:

  1. Open the FOO.DOC Word document.

  2. Activate the Visio object.

  3. Select the Edit SelectAll command.

  4. Select the Edit Copy command.

  5. Open the BAR.XLS Microsoft Excel spreadsheet.

  6. Select the Edit PasteLink command.

Assume that the link in Microsoft Excel is set as a manual link. This means that updates to the link will only occur when the user explicitly requests them using the Update Now button of Microsoft Excel's Links... dialog. If the user requests to update the link at a time when neither Word or Visio is running, then in the process of completing the update operation, both Word and Visio applications will be launched. The link will get an update of data from the Visio object and both Word and Visio will shut down (or at least this is how it is supposed to work!). The complete operation should take place invisibly to the user. This operation is referred to as the silent-update operation. At the mid-point of the update operation, Microsoft Excel has an external reference directly to the Visio object but does not have any reference to the Word document. The presence of the connection from Microsoft Excel to Visio needs to hold Word and the FOO.DOC document running. Later, the act of Microsoft Excel releasing its connection to the Visio object should cause both Visio and Word to shut down.

Note   OLE 2 uses an object called a Moniker to identify a link source. OLE 2’s Link object stores a Moniker as part of its persistent state. When it is time for the Link object to connect to the link source object, a process called Moniker binding takes place. For this purpose, Monikers implement an IMoniker::BindToObject method. There are different types of Monikers and each performs its BindToObject operation in a different manner. OLE 2 provides three useful Moniker implementations that are used in this scenario: FileMoniker, ItemMoniker, and CompositeMoniker. The link source in this example is identified with a CompositeMoniker composed of a FileMoniker (“FOO.DOC”) constructed with an ItemMoniker (the name given by Word to identify the Visio object—perhaps Object1). The Moniker binding process in this case would first proceed by launching Word and asking it to open the FOO.DOC file. Next, Word would be asked to load and run the object named Object1. Finally, a pointer to the Visio object would be returned to the Link object in the Microsoft Excel process.

The following picture shows the in-memory data structures at the time when Microsoft Excel is connected to the Visio object:

In this scenario the Visio object has references or connections from both its embedding container (FOO.DOC) and from its linking client (BAR.XLS). Visio must realize that when the reference from the linking client is released, it should shut down. A simple call to IUnknown::Release would decrement the Visio object's reference count from 2 to 1. The reference from the embedding container would remain and prevent the Visio object from shutting down. We do not want the reference from the embedding container to hold the object alive; thus, we need the Visio object to realize that the strength of the reference from the linking client is somehow different from the reference from its embedding container.

The Solution

In order to solve this problem we introduce the notion of weak and strong references.

Weak vs. Strong References

A strong reference (sometimes also called a lock) holds an object alive and running. As long as a strong reference to an object exists, the object is not supposed to shut down. Note that this is different from merely ensuring that the memory pointed to by the interface pointer remains valid; in the case of a LocalServer object, a strong reference not only keeps the memory of the Handler valid, but also keeps the LocalServer .EXE running.

Other references are considered weak. The presence of weak references is not enough to stop an object from shutting down. The container/embedded object relationship is analogous to a parent/child relationship. The nature of a parent/child relationship is that if a child is being used by a client, then the parent is required to stay running. The child cannot live independently of the parent. Thus whenever there is a strong reference on a child, the child holds a strong reference on the parent. But if the child is not being used by a client, then the simple existence of the child should not hold the parent alive. This implies that in a normal state it is important for the parent not to hold a strong reference on the child; the parent must hold a weak reference on the child.

Let us now return to our linking scenario. The OLE Link in the Microsoft Excel application holds a strong reference on the Visio object. The reference from the Word embedding container is considered weak (in this scenario the ClientSite is the parent and the VisioObject is the child). When the link releases its strong reference on the Visio object, the Visio object will shut down because there are no other strong references.

In the diagram, cRef indicates the total count of references (the sum of both weak and strong references) and cLock indicates the count of strong references. In order to properly manage a shutdown in the scenario above, an object needs to maintain two counts. When a strong reference is added, both cRef and cLock are incremented. When a weak reference is added, a simple IUnknown::Addref is made that increments only cRef. The primary purpose for maintaining these two counts is so that the object can sense the transition of these counts to zero. When the count of strong references, cLock, transitions to zero, the object invokes its Close method. In its Close method the object performs all actions necessary to "encourage" its total reference count to go to zero. This will include such actions as revoking from the Running Object Table, sending OnClose notification, closing any internal pseudo-objects, and disconnecting any remaining remote (external) connections. When the total reference count, cRef, transitions to zero, the memory for the object itself is destroyed.

Running Object Holds Strong Reference on Container

In our silent update scenario, it is necessary for the Word document to stay running for the duration of the update operation. We have already accounted for why the Visio object stays running due to the presence of the strong reference from the Microsoft Excel linking client. But what holds the Word document itself alive? During the update operation, there is not a direct reference from the Microsoft Excel application to the Word document. In order to solve this problem, OLE requires that an embedded object hold its embedding container alive while it is running. OLE automatically takes care of this on behalf of LocalServer (EXE) object implementations. OLE's DefHandler arranges to hold the embedding container alive while the object's LocalServer application is running. When the DefHandler receives an OnClose notification informing it that the LocalServer application has shut down, it releases its lock on the embedding container. An InprocServer (DLL) object that allows remote connections (that is, it does not specify OLEMISC_CANTLINKINSIDE) must take care of this on its own. A running embedded object can add a strong reference to its embedding container by making the following calls:

IOleClientSite::GetContainer(ppOleContainer)
IOleContainer::LockContainer(fLock)

In the following picture we add the detail of the DefHandler and the lock on the container on behalf of the running embedded object.

Registering in the Running Object Table

In order to allow a potential linking client to connect to an existing running instance of an object rather than attempting to launch a second instance, OLE provides a system maintained table called the running object table (ROT for short). Objects that want to allow potential clients to connect to the current instance will register themselves in the ROT. For the duration that an object is registered in the ROT, the ROT holds a reference to the object. The object can control whether this is a weak or a strong registration with flags passed to the IRunningObjectTable::Register method. By default the registration contributes a weak reference. If the ROTFLAGS_REGISTRATIONKEEPSALIVE flag is specified, the registration creates a strong reference. In the standard Compound Document scenarios, an object should register itself in the ROT with a weak registration. For example, in our link to the Visio embedding scenario, both the Word document and the Visio object would register themselves with a weak registration in the ROT. Registration in the ROT is necessary to allow clients to connect to the running instance of the object, but we do not want the act of being registered in the ROT to keep the object from shutting down.

The following picture adds the detail of the Word document and the Visio object registering in the ROT to our linking to an embedded Visio object scenario.

In the case pictured, the Visio object and the Word document are running invisibly (that is, not under user control); they were launched programmatically to service two linking clients. The Visio object should not shut down until both linking clients have released their strong references. When these two references are released, the strong count will transition to zero and the Visio object will invoke its Close method. In this method the object will revoke from the ROT, which will cause the reference from the ROT to be released, and the Visio object will send OnClose notification, which will cause the DefHandler to release its reference to the object. The total reference count will then go to zero and the Visio object will destroy itself. When the DefHandler receives the OnClose notification it will also call IOleContainer::LockContainer(FALSE) to release its strong reference on the Word document container. The Word document's strong count will transition to zero, and the document will invoke its Close method. As part of its Close method the Word document will revoke itself from the ROT, thus causing its total reference count to transition to zero. The Word document will then perform its final destruction.

Remote (or External) Connections

The act of passing a pointer to an interface across process, typically as an out parameter of another method call, causes the interface pointer to be marshaled into the other process. This work is handled as part of the Remote Procedure Call (RPC) mechanism used by OLE. It is not necessary to have a complete, detailed understanding of this process. We only need to have a general understanding of the structures that result after a successful marshaling of an interface pointer has finished. (If you want more information on RPC in OLE and the process of marshaling interface pointers, please refer to the OLE 2 Programmer’s Reference, Vol. 1 or the OLE 2.0 specification, section 4.7.) It is not possible for a client process to have a direct memory pointer to the interface of an object living in another process. Instead, the client ends up with a direct memory pointer to a Proxy object or Handler object that acts as a stand-in for the real object in the client's process. This proxy has an RPC communication link to a structure called a Stub in the process of the object. One object may support more than one interface. For each remoted interface there is a proxy/stub pair. In order to coordinate the multiple proxies and stubs, OLE uses a ProxyManager in the process of the client and a StubManager in the process of the object.

For general COM objects, the ProxyManager object is used directly as the handler for the object in the process of the client. For most OLE Compound Document objects, the ProxyManager is an internal sub-piece of OLE's DefHandler. The DefHandler is built as an aggregate of three pieces:

The StubManager acts as the common point of connection for all external references to the object. The StubManager in turn holds "Addref"ed pointers to one or more of the object's interfaces. In 16-bit OLE the communication mechanism used between the proxies and stubs was called LRPC and was based on a protocol involving posted and sent Window messages. In 32 bit OLE, the LRPC mechanism has been replaced by the DCE RPC communication protocol. In the future, this DCE RPC mechanism will be extended to support Distributed OLE (that is, RPC across machines).

For one particular COM object identity, there can be at most one StubManager. OLE enforces object identity by always "QueryInterface"ing for IUnknown and then mapping a particular IUnknown* pointer to a particular StubManager. In OLE 2.0, a particular COM identity can live in only one process. The StubManager for that COM identity lives in the same process as the object. If a pointer to that object needs to be remoted to another process, then a Handler or ProxyManager will be created to act as the stand-in for the object in that destination process. For a particular COM identity, there will be at most one associated ProxyManager/Handler in a particular client process. This uniqueness of one ProxyManager for one object in one process and one StubManager for one object living only in the object's original process is part of the fundamental principles of OLE and COM.

Note   In 16-bit OLE 2.0 and in the shipping version of 32-bit OLE for Windows NT™ 3.5, all COM objects must live in the single thread that calls OleInitialize. Only one thread may call OleInitialize. A particular COM object, however, can only live in one particular thread. This is known as the apartment model. In the next version of Windows NT it may be possible to have one COM identity participate in more than one thread of a process. This manner of dealing with objects is called free-threading.

Relying on the StubManager to Manage Strong/Weak References

The StubManager manages the notion of strong references and weak references. It maintains both the count of strong references and the count of total references, cLock and cRef. An object that wants to rely on the StubManager for managing strong/weak references only maintains a single reference count (cRef). On the transition of this count to zero, the object destroys itself. The object must make sure to perform all necessary clean-up in its destructor method, including revoking from the ROT if necessary. The default implementation for the StubManager is to release all pointers it holds to the real object when the count of strong references transitions to zero. At this point the StubManager is a zombie because it no longer has a connection to the object. This will normally cause the real object to get its final release, which then causes it to destroy itself. The destructor code for the object should take care of any necessary clean-up, including revoking from the ROT and sending any OnClose notifications. This will eventually cause the final reference count on the StubManager to transition to zero, upon which the StubManager will destroy itself.

Notice that in the sequence just described, because the object is relying on the StubManager to manage the weak and strong counts, the object does not get a call to its Close method. It simply gets its final release, upon which it calls its destructor. If the object is implemented in this style, because of this scenario, it is required for the object to be prepared to execute its shutdown clean-up code either from a direct call to its Close method or when it enters its destructor directly without a prior call to Close. We will see later how this scenario causes problems and in fact can result in a data loss for the user.

The following picture adds the detail of the StubManager for the Visio embedded object to our Visio linking scenario (the StubManager is depicted by the small circle). The StubManager for the Word document object and the ClientSite object are omitted from the drawing so as not to overcomplicate the drawing. In this drawing, the Visio object is relying on its StubManager to manage the weak and strong counts. Thus, the Visio object only maintains a single count (cRef). (In following sections we will see why it is not necessarily the best idea for the embedded object to rely on its StubManager to manage the weak and strong counts. An alternate strategy involving IExternalConnection is a better strategy.)

The original intent of OLE's StubManager was to shield the object developer from having to deal with two counts. The goal was to make it so that an object implementation only had to deal with one count of total references. The object would only have to notice the transition of the total reference count to zero and then destroy itself. In many cases, this works fine. In particular, this often works fine for objects meeting one or more of the following descriptions:

In the next sections, we will see cases where strong references are generated internally within an application; these are strong references that are not caused by the remote connections to the StubManager. We have already discussed one case of an internally generated strong reference: the lock that a running embedded object holds on its container. The call that the object makes to IOleContainer::LockContainer (actually, the call is made by the DefHandler on behalf of a LocalServer—an in-proc server must make this call on its own) informs the container directly; the StubManager does not know about the strong reference (unless the container calls CoLockObjectExternal). These internally generated strong references lead to cases where relying on OLE's StubManager to manage the weak and strong references is insufficient. These problematic cases often result in shutdown failures and, in some cases, result in data loss for the user.

Managing the User as a Strong Reference

In the discussion of our Visio linking scenario thus far, I have always discussed the case where Word was launched invisibly and programmatically by the silent-update operation. If, on the other hand, the Visio object and the Word document had already been running and visible to the user when the manual link in Microsoft Excel was updated, then we would not want the Visio object and the Word document to shut down. The link in Microsoft Excel should silently connect to the Visio object, get its update, and then release its strong reference, while the Visio object should remain running because it is under the control of the user. In order to accomplish this, we manage the fact that the object is visible to the user, and thus under the control of the user, by adding a strong reference on behalf of the user. This is an example of an internally generated strong reference that the StubManager does not know about. The act of a link client connecting and disconnecting behind the scenes should not cause a document or object that the user is editing to suddenly shut down. The following picture illustrates this strong reference on behalf of the user. (To simplify the drawing the StubManager has been omitted.)

If an object is locked on behalf of the user because it is visible, then the object will only shutdown if the user issues File Close command. In the previous example, the Visio object would only shut down if the user either issued a File Close command on the Visio object directly or issued File Close on the Word document. As part of the shutdown process of an embedding container, the container must call IOleObject::Close on each embedding. If, at the time of the update operation, only the FOO.DOC Word document was visible to the user and the Visio object was not running at all, the following would occur:

By standard conventions for OLE Compound Documents, when the user issues the File Close command, the document/object is forced to shut down even if link clients are currently connected. The same is true when the IOleObject::Close method is called. An explicit call to Close, either by the user or programmatically, will typically break any outstanding locks and force a shutdown of the object. The implementation of Close by a typical Compound Document application calls CoDisconnectObject at the end to force all remote connections to disconnect. This will break even a programmatic silent update operation in progress. (It would be possible to only send the OnClose notification and not forcibly tear down remote connections in a object’s Close implementation. In theory all external connections should clean up themselves. When the OnClose notification is received by Compound Document Link clients, the OLE Link object will always disconnect. Presumably the other external connections are present for some programmatic purpose. When these other programmatic operations are finished, they too will disconnect. Thus the external connections should automatically clean up properly, but standard Compound Document object implementations tend to be somewhat conservative and pessimistic—not trusting other client implementations to be done properly. Given the history of some of our application implementations, this pessimism is somewhat warranted.)

Managing Pseudo-Objects

Another scenario that contributes internally generated strong references is the management of pseudo-objects. Some objects allow remote connections to sub-pieces of their structure. For example, a word processor may allow clients to link to bookmarks; a spreadsheet may allow clients to link to named ranges. These type of sub-objects, which represent sub-pieces of a whole object, are referred to as pseudo-objects. A pseudo-object often does not have its own identifiable storage. Pseudo-objects cannot live independently. They depend on their parent (whole) object to stay running. As such, pseudo-objects need to implement a locking strategy similar to embedded objects. While the pseudo-object is running, it holds a strong reference on its parent/whole object. The reference from the parent object to the pseudo-object is considered weak (that is, it does not hold the pseudo-object alive). Pseudo-objects, however, are implemented privately as part of the whole object. It is not necessary for OLE to specify interfaces to control the coordination of pseudo-objects. Pseudo-objects are able to use special knowledge of the whole object in order to call private methods and/or access private state. In addition, because the whole object and the pseudo-object are implemented privately together, it is possible for the whole object to hold a "non-Addref"ed pointer to the pseudo-object.

A common strategy for managing pseudo-objects is to implement a COM object for the pseudo-object which considers any reference as a strong reference. As such, the weak reference from the parent object is managed as a "non-Addref"ed pointer. Using this strategy, pseudo-objects only need to maintain a single reference count because there is not a combination of both weak and strong pointers—all pointers being considered strong. Such pseudo-objects hold a strong reference on their parent object while they have a non-zero reference count. When the reference count of the pseudo-object transitions to zero, the pseudo-object releases its strong reference on its parent.

Managing Weak and Strong References Yourself

There are some very important scenarios where it is not sufficient to allow OLE's StubManager to manage the strong vs. weak reference counts. These scenarios can result in data loss for the user and, most often, serious shutdown problems where the object server becomes orphaned invisibly to the user and never shuts down. Some of these scenarios include the following:

  1. Top-level embeddable OLE object that supports linking to itself or its sub-pieces (that is, any embeddable OLE object that does not specify OLEMISC_CANTLINKINSIDE). This is particularly important for objects that also support in-place editing.

  2. Top-level embeddable OLE object that supports OLE Automation.

  3. Top-level file-based OLE object/document that supports linking to its sub-pieces (pseudo-objects).

  4. Top-level file-based OLE object/document that supports OLE Automation over itself and its sub-pieces (pseudo-objects).

The first two scenarios can result in data loss if the embedded object happens to have unsaved changes at a time when the strong count on its StubManager transitions to zero. On this transition the StubManager releases its references to the object, thus becoming a zombie. The object then is unable to have its modified data saved back to its container. When the object calls IOleClientSite::SaveObject, the subsequent call of IPersistStorage::Save from its container cannot get through because the StubManager is a zombie. This scenario can easily occur if a silent update occurs at a time when an in-place object is UIDeactivated. (To help avoid this data loss, an in-place container should hold its embedding locked (that is, call OleLockRunning) when it in-place activates the object. It can release this lock when it wants to close the object.)

Scenarios 3 and 4 involve the coordination of a combination of both externally generated locks and internally generated locks. The externally generated locks are the result of external connections that connect through the StubManager. The internally generated locks are the result of locks on behalf of pseudo-objects, locks on behalf of the user, and locks on behalf of running embeddings (IOleContainer::LockContainer calls). In order to properly manage all of the scenarios, it is required that one piece of code know about all of the locks. There are basically two strategies:

I prefer the second strategy of explicitly tracking the strong and weak references rather than relying on the StubManager. (This is particularly true for 32-bit OLE, where if an object implements IExternalConnection, the StubManager will never become a zombie. This is particularly important because it means that it is possible to directly increment/decrement the strong lock count maintained in the object for internally generated locks (for example, locks on behalf of the user and locks on behalf of pseudo-objects). It is no longer necessary to pass all locks through the StubManager by calling CoLockObjectExternal as it was with 16-bit OLE 2. Thus, with 32-bit OLE, it becomes unnecessary to ever have to call CoLockObjectExternal.)

The IExternalConnection interface allows an object to track the number of external connections:

By implementing the IExternalConnection interface, the object will be informed by the StubManager of all strong external connections. The object can combine this with the count of strong references originating from internal sources in order to have an accurate count of all strong references.

The following events can all contribute strong references on an object:

The implementation of each of the above would be to call a common, private Document::Lock(TRUE, 0) method. For the Lock(TRUE) case, this method would increment the total count of locks (cLock) and call IUnknown::Addref to increment the total reference count (cRef) of the document.

The following events can all release a strong reference on an object:

The implementation of each of the above would be to call a common, private Document::Lock(FALSE, fLastUnlockCloses) method. For the Lock(FALSE) case, this method would decrement the total count of locks (cLock). If this count transitions to zero and fLastUnlockCloses is TRUE, then the document's close method is called. Finally, in order to balance the previous Addref call, IUnknown::Release would be called. If this release call causes the total reference count to transition to zero, then the document will be destroyed.

By explicitly tracking the external connections with IExternalConnection, the data loss situation of the first two scenarios above can be avoided by the object detecting when it is about to shut down, at a point when the StubManager is still connected. This means that the call to IOleClientSite::SaveObject is invoked at a point when the subsequent IPersistStorage::Save method call will succeed.

Managing Embedded Objects and Containers

The scenario of  the embedded object and the embedding container is the principal scenario for OLE Compound documents. The relationship between the embedding and the container is an example of a parent-child relationship. In this scenario the parent/container has ultimate control over its children/embeddings.

The following diagram shows the methods involved in managing the OLE Compound Document Container-Embedding relationship:

Objects implement the following methods:

Containers implement the following methods:

The container at any time should be able to command an embedded object to shut down. The container of course holds an "Addref"ed pointer to its embedding. The presence of this Addref guarantees that the container will not GP Fault when it dereferences this pointer. Simply releasing this "Addref"ed pointer is not sufficient to command an object to shut down; there may be other references to the object, so releasing this pointer will not necessarily cause the object's reference count to go to zero. The object may, for example, be registered in the running object table (ROT) and/or may have connections from one or more linking clients. These will also contribute reference counts on the object. Thus the container needs a special way to command the object to shut down. For this purpose OLE introduces the IOleObject::Close method.

In addition, the container needs a way to force the object to go running. In the case of an object implemented with a LocalServer, this forces the object's EXE server to be launched. The IRunnableObject::Run method is used for this purpose. The OleRun API is a simple helper that "QueryInterface"s for the IRunnableObject interface and then calls the Run method. The container can determine whether an object is running by calling IRunnableObject::IsRunning or by using the helper API OleIsRunning. Normally the reference from the container to the embedded object needs to be considered weak. In the case of a LocalServer, the container holds a pointer to an object handler (normally OLE's DefHandler) loaded in its process. When the object goes running, the LocalServer EXE is launched and a remote connection is established between the handler and the LocalServer. Initially all remote connections are established as strong references by OLE. The embedding container needs a way to inform the object's handler that it is being used in the context of an embedding relationship and not being used, for example, in the context of a remote link connection. This forces the connection with the LocalServer to be made weak. The embedding container informs the object handler that it is a contained object by calling IRunnableObject::SetContainedObject or by using the helper API OleSetContainedObject. This method is not important for InprocServers. If the container wants to temporarily force an embedded object to stay running for special reasons, the container can add a strong reference on the object by calling IRunnableObject::LockRunning or by using the helper API OleLockRunning. For example, an in-place container should hold its embedded objects locked running when it "UIDeactivate"s them. The in-place container wants to force the objects to stay running so they can be reactivated quickly.

Managing the Application

Thus far we have discussed how to manage weak and strong references on the top-level document object to control when the document should close. Similar techniques are used to manage the shutdown of the LocalServer application itself. There are three basic types of applications:

The CoCreateInstance API is used to create objects in OLE/COM. An application implements a special COM object called a ClassObject, which implements the IClassFactory interface in order to allow OLE to create instances of the type of object that the application supports. The CoCreateInstance API first launches the application with a "-Embedding" argument on the command line. The application must create and initialize its ClassObject and call the RegisterClassObject API before yielding. Depending on the type of application, it will specify one of the following flags on its call to RegisterClassObject:

The following factors contribute to the shutdown logic for both MDI and old-style SDI applications:

It is best to manage the application shutdown logic in terms of an application object that maintains a count of strong references or locks. The above-mentioned factors contribute strong locks on the application object. When the count of strong locks transitions to zero, the application should shut down. This is exactly analogous to how locks (or strong references) are used to control the closing logic for a document/object.

If the application object does not allow external connections, we do not have to deal with a combination of weak and strong references. This simplifies things somewhat. All references to the object are then considered strong, so we only need to maintain a single count. If, for example, the application exposes the application object remotely through OLE Automation by calling the RegisterActiveObject API, this object will need to manage both weak and strong references; this object will need to maintain two counts (cLock and cRef). The application object should be registered with a weak registration by passing the proper flag to the RegisterActiveObject API.

Note   By default the RegisterActiveObject API registers the object with a strong reference. In 32-bit OLE the REGOBJ_TABLEWEAK flag has been defined to allow the registration to be weak. (See the 32-bit OLE 2 Programmer’s Reference.). In 16-bit OLE it would be necessary to organize a separate COM identity for the object passed to RegisterActiveObject in order to deal with the fact that the registration is strong.

Ideal Shutdown Behavior for an MDI Application

The following are requirements that characterize the ideal shutdown behavior for an MDI application ("ideal" behavior as defined by the standard conventions for Compound Documents as originally intended by OLE):

Ideal Shutdown Behavior for a New-Style SDI Application

A new-style SDI application is characterized by the following behavior: When the user chooses File New or File Open, a new document window appears and becomes the active window; the current document window remains running with the old document. This is the new style of application intended to be document-centric rather than application-centric. This behavior is new in comparison with the Windows 3.1 Paintbrush and Write-style SDI applications. The main difference between a new-style SDI application and an MDI application from an object lifetime point of view is that an SDI application does not expose distinct abstractions for an application and an object; they both are presented as a single abstraction to the user. The user sees the document/object and the "application" window as one thing. Thus we do not need to manage a concept of latching control of the application for an SDI application. We only need to manage when the document/object should close. When the document is visible, it is locked on behalf of the user. When the document is hidden or closed, it is unlocked on behalf of the user.

The following requirements describe the ideal shutdown behavior for a new-styleSDI application (that is, ideal behavior as defined by the standard conventions for Compound documents as originally intended by OLE):

Managing the ClassObject

It is best to implement the ClassObject as a separate object from the application object. In this way the reference counts held on the ClassObject as a result of calling the RegisterClassObject API do not interfere with the logic of when to shut down. The existence of these references should not be allowed to hold the application alive. The references should ideally be considered as weak references with regard to application shutdown, but unfortunately the ClassObject Table holds strong references. Also, the ClassObject Table contributes an indeterminate number of references on the ClassObject. By implementing the ClassObject as a separate object we do not have to deal with the references on the ClassObject with regard to application shutdown. At application InitInstance time, the ClassObject should be created and registered; during application termination (that is, when the application object destructor is called), the ClassObject should be revoked.

Note   Holding onto a pointer to an application's ClassObject does not hold the application alive. If a client wants to hold the application associated with a ClassObject alive, it must call IClassFactory::LockServer.

The following picture shows the Visio application after the Open verb (IOleObject::DoVerb) has been executed on a Visio object embedded in a Word document. The Visio application has been launched by OLE to service the object. The object has been made visible and thus is locked on behalf of the user. Because the application was launched by OLE, the user does not have a lock on the application. This picture shows the detail of the Visio embedding's ClassObject and application object:

Appendix A: Implementing Object Lifetime Management

The following diagram shows the key methods and attributes maintained on the application and document objects in order to properly implement object lifetime management:

Appendix B gives sample code for an old-style SDI application that implements proper object lifetime management.

Appendix B: Sample Source Code

class Capp : IUnknowm
{
public:
    CApp();
    ~CApp() {};
    ULONG AddRef(void);
    ULONG Release (void);
    HRESULT QueryInterface (REFIID riid,LPVOID FAR* lplpUnk);
    void Show(BOOL fGiveUserCtrl);
    void Hide(void);
    void HideIfNoReasonToStayVisible(void);
    DWORD Lock(BOOL fLock, BOOL fLastUnlockCloses);
    BOOL CloseAllDocsAndExit(BOOL fForceEndSession);
    void DocLockApp(void);
    void DocUnlockApp(LPDOCUMENT lpOutlineDoc);
    BOOL AppInitInstance(HINSTANCE hInst);
    BOOL OleInitInstance(HINSTANCE hInst);
    void TerminateApplication(void);
    BOOL ParseCmdLine(LPSTR lpszCmdLine, int nCmdShow);
    BOOL RegisterClassFactory(void);
    void RevokeClassFactory(void);
        
implementations:
    struct CClassFactory : IClassFactory 
    {
    public:
        CClassFactory (LPAPP pApp)
        {
            m_lpApp = pApp;
            m_cRef = 0;
        }
        
        STDMETHOD(QueryInterface) (REFIID riid, void **ppvObject);
        STDMETHOD_(ULONG,AddRef)(void);
        STDMETHOD_(ULONG,Release)(void);
    
           STDMETHOD(CreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
        STDMETHOD(LockServer)(BOOL fLock);    
    
    private:
        LPAPP      m_lpApp;
        ULONG    m_cRef;                 // total ref count for ClassFactory
                                         // NOTE: CClassFactory treated as separate COM
                                         //       identity from the CApp COM object
#if defined( _DEBUG )
        ULONG    m_cSvrLock;             // total count of LockServer locks  
                                        // (for debugging purposes only)
#endif
        friend CApp;
    };
    DECLARE_NC(CApp, CClassFactory)
    CClassFactory        m_ClassFactory;
shared_state:
    ULONG       m_cRef;                 // total ref count for app
    ULONG       m_cStrongRef;          // total strong reference count
                                        //  NOTE: if all refs are considered strong
                                        // on App then we only need one count: m_cRef
    BOOL        m_fUserCtrl;          // Is user in cotrol of App?
    ULONG       m_cDoc;                 // total count of open documents
    BOOL          m_fAppClosing;       // recursion guard
    DWORD       m_dwRegClassFac;        // value returned by CoRegisterClassObject
    LPDOCUMENT  m_pDocument;         // main SDI document visible to user
};

class FAR CDocument : IUnknown
{
public:
    CDocument();
    ~CDocument();
    ULONG AddRef(void);
    ULONG Release (void);
    HRESULT QueryInterface(REFIID riid,LPVOID FAR* lplpUnk);
    DWORD Lock(BOOL fLock, BOOL fLastUnlockCloses);
    BOOL Close(DWORD dwSaveOption);
    void Show(void);
    void Hide(BOOL fShutDown);
implementations:    // Nested Classes.
    struct CDocPersistFileImpl : IPersistFile 
    {
    public:
        CDocPersistFileImpl(LPDOCUMENT pDocument)
        {
            m_pDocument    = pDocument; 
        }
       // *** IUnknown methods ***
        STDMETHOD(QueryInterface) ( REFIID riid, LPVOID FAR* ppvObj);
        STDMETHOD_(ULONG,AddRef) (void);
        STDMETHOD_(ULONG,Release) (void);
        // *** IPersist methods ***
        STDMETHOD(GetClassID) ( LPCLSID lpClassID);

    

        // *** IPersistFile methods ***
        STDMETHOD(IsDirty) (void) ;
        STDMETHOD(Load) ( LPCOLESTR lpszFileName, DWORD grfMode) ;
        STDMETHOD(Save) ( LPCOLESTR lpszFileName, BOOL fRemember) ;
        STDMETHOD(SaveCompleted) ( LPCOLESTR lpszFileName) ;
        STDMETHOD(GetCurFile) ( LPOLESTR *lplpszFileName) ;
    private:
        LPDOCUMENT m_pDocument;
        int  m_cRef;                   // interface specific ref count.
    };
    struct CDocExternalConnectionImpl : IExternalConnection 
    {
    public:
        CDocExternalConnectionImpl(LPDOCUMENT pDocument)
        {
            m_pDocument = pDocument; 
        }
       // *** IUnknown methods ***
        STDMETHOD(QueryInterface) ( REFIID riid, LPVOID FAR* ppvObj);
        STDMETHOD_(ULONG,AddRef) (void)  ;
        STDMETHOD_(ULONG,Release) (void) ;
        // *** IExternalConnection methods ***
        STDMETHOD_(DWORD, AddConnection) ( DWORD extconn, DWORD reserved) ;
        STDMETHOD_(DWORD, ReleaseConnection) ( DWORD extconn, DWORD reserved, 
                                                 BOOL fLastReleaseCloses) ;
    private:
        LPDOCUMENT m_pDocument;
        int   m_cRef;               // interface specific ref count.
    };
    DECLARE_NC(CDocument, CDocPersistFileImpl)
    DECLARE_NC(CDocument, CDocExternalConnectionImpl)
shared_state:
    CDocPersistFileImpl        m_PersistFile;
    CDocExternalConnectionImpl    m_ExternalConnection;
    LPAPP        m_pApp;
   HWND           m_hwnd;             // client area window for the Doc
   int            m_docInitType;      // is doc new or loaded from a file?
    ULONG      m_cRef;             // total ref count for document
   ULONG      m_cStrongRef;           // total strong reference count
                                     //  this is the total combination of:
                                     //      IExternalConnection locks,
                                     //      IOleContainer::LockContainer locks,
                                     //      and internal generated locks
                                     //  when this count transitions to 0
                                     //  and fLastUnlockCloses==TRUE, then
                                     //  CDocument::Close is called to
                                     //  close the document.
#if defined( _DEBUG )
    ULONG  m_cCntrLock;             // total count of LockContainer locks  
                                     //(for debugging purposes only)
    ULONG  m_cExtConn;              // total count of IExternalConnection locks
                                     //(for debugging purposes only)
#endif
    BOOL         m_fDocClosing;      // recursion guard
    OLECHAR        m_szFileName[256];  // associated file; "(Untitled)" if none
   LPOLESTR       m_lpszDocTitle;     // name of doc to appear in window title
    DWORD      m_dwRegROT;         // key if doc registered as running
    LPMONIKER  m_lpFileMoniker;    // moniker if file-based/untitled doc
};

/* CDocument::Lock
** ---------------
**    Lock/Unlock the Doc object. if the last lock is unlocked and
**    fLastUnlockReleases == TRUE, then the Doc object will shut down
**    (ie. it will recieve its final release and its refcnt will go to 0).
*/
DWORD CDocument::Lock(BOOL fLock, BOOL fLastUnlockCloses)
{
    DWORD cLock;
    if (fLock) {
        cLock = ++m_cStrongRef;
        AddRef();    // released later when Lock(FALSE) called
    } else  {
        if ( m_cStrongRef > 0 ){
            --m_cStrongRef;
        }else{
            AssertSz (m_fDocClosing, "Doc::Lock(FALSE) called with m_cStrongRef == 0");
             return 0;    // all locks already released
        }
        cLock = m_cStrongRef;
        if( m_cStrongRef == 0 && fLastUnlockCloses )
             Close(OLECLOSE_NOSAVE);        // Discard changes; user must explicitly do save
        Release();
    }
   return cLock;
}
// CDocument::IOleContainer::LockContainer
STDMETHODIMP NC(CDocument,CDocOleContainerImpl)::LockContainer(BOOL fLock)
{
#if defined( _DEBUG )
   if (fLock) {
      ++m_pDocument->m_cCntrLock;
   } else {
      Assert(--m_pDocument->m_cCntrLock >= 0);
   }
#endif  // _DEBUG
   /* OLE2NOTE: in order to hold the document alive we call
   **    CDocument::Lock to add a strong reference to our Doc
   **    object. this will keep the Doc alive when all other external
   **    references release us. whenever an embedded object goes
   **    running a LockContainer(TRUE) is called. when the embedded
   **    object shuts down (ie. transitions from running to loaded)
   **    LockContainer(FALSE) is called. if the user issues File.Close
   **    the document will shut down in any case ignoring any
   **    outstanding LockContainer locks because forceably breaks any
   **    existing strong reference counts. This guarantees that
   **    the Doc object gets its final release (ie. cRefs goes to 0).
   */
   m_pDocument->Lock(fLock, TRUE /* fLastUnlockReleases */);
   return NOERROR;
}

// CDocument::IExternalConnection::AddConnection
STDMETHODIMP_(DWORD) NC(CDocument,CDocExternalConnectionImpl)::AddConnection(
      DWORD                   extconn,
      DWORD                   reserved
)
{
    if( extconn & EXTCONN_STRONG ) {
#ifdef _DEBUG
        m_pDocument->m_cExtConn++;
#endif
          return m_pDocument->Lock(TRUE, 0);
    }
    return 0;
}

// CDocument::IExternalConnection::ReleaseConnection
STDMETHODIMP_(DWORD) NC(CDocument,CDocExternalConnectionImpl)::ReleaseConnection(
      DWORD                   extconn,
      DWORD                   reserved,
      BOOL                    fLastUnlockCloses
)
{
   if( extconn & EXTCONN_STRONG ) {
#ifdef _DEBUG
        Assert(--m_pDocument->m_cExtConn >= 0);
#endif
          return m_pDocument->Lock(FALSE, fLastUnlockCloses);
    }
    return 0;
}
/* CDocument::Show
 * ---------------
 *
 *      Show the window of the document to the user.
 */
void CDocument::Show()
{
    if (FInShow())
        return;
    
    AssertSz(m_docInitType != DOCTYPE_UNKNOWN, "can't show unitialized document\r\n");
    if (m_docInitType == DOCTYPE_UNKNOWN)
        return;
    SetFInShow();
    // OLE2NOTE: while the document is visible, we do NOT want it to be
    // prematurely destroyed when a linking client disconnects. thus
    // we must inform OLE to hold an external lock on our document.
    // this arranges that OLE holds at least 1 reference to our
    // document that will NOT be released until we release this
    // external lock. later, when the document window is hidden, we
    // will release this external lock.
    if (!IsWindowVisible(m_hwnd)) {
        // make document window visible and make sure it is not minimized
        ShowWindow(m_hwnd, SW_SHOWNORMAL);
        Lock(TRUE , 0);    // Lock the document on behalf of the user
    }
#if defined( OLE_SERVER )
    if (m_docInitType == DOCTYPE_EMBEDDED && m_lpOleClientSite != NULL) {
        /* OLE2NOTE: we must also ask our container to show itself if
        **    it is not already visible and to scroll us into view. we
        **    must make sure to call this BEFORE showing our server's
        **    window and taking focus. we do not want our container's
        **    window to end up on top.
        */
        m_lpOleClientSite->ShowObject();
        /* OLE2NOTE: if we are an embedded object and we are not
        **    in-place active in our containers window, we must inform our
        **    embedding container that our window is opening.
        **    the container must now hatch our object.
        */
        if (! m_fInPlaceActive)
            m_lpOleClientSite->OnShowWindow(TRUE);
        /* OLE2NOTE: the lifetime of our document is controlled by our
        **    client and NOT by the user. we are not an independent
        **    file-level object. we simply want to show our window here.
        **
        **    if we are not in-place active (ie. we are opening
        **    our own window), we must make sure our main app window is
        **    visible. we do not, however, want to give the user
        **    control of the App window; we do not want App::Show
        **    to lock the App on behalf of the user.
        */
        if (! IsWindowVisible(m_pApp->m_hWndApp) || IsIconic(m_pApp->m_hWndApp)) {
            if (! m_fInPlaceActive)
                m_pApp->Show(FALSE /* fGiveUserCtrl */);
            SetFocus(m_hwnd);
        }
    } else
#endif  // OLE_SERVER
    {   // DOCTYPE_NEW || DOCTYPE_FROMFILE
        /* we must make sure our app window is visible. because the
        **    document is a top-level file-based document, we do want
        **    the user to latch control of the app window. 
        m_pApp->Show(TRUE /* fGiveUserCtrl */);
    }
    // make document window visible and make sure it is not minimized
    ShowWindow(m_hwnd, SW_SHOWNORMAL);
    SetFocus(m_hwnd);
    ClearFInShow();
}
/* CDocument::Hide
 * ---------------
 *
 *      Hide the window of the document from the user.
 *      take away the control of the document by the user.
 */
void CDocument::Hide(BOOL fShutdown)
{
    if (! IsWindowVisible(m_hwnd))
        return;
    // OLE2NOTE: the document is now being hidden, so we must release
    // the external lock made when the document was made visible.
    // if this is a shutdown situation (fShutdown==TRUE), then OLE
    // is instructed to release our document. if this is that last
    // external lock on our document, thus enabling our document to
    // complete its shutdown operation. If This is not a shutdown
    // situation (eg. in-place server hiding its window when
    // UIDeactivating or IOleObject::DoVerb(OLEVERB_HIDE) is called),
    // then OLE is told to NOT immediately release the document.
    // this leaves the document in an unstable state where the next
    // Lock/Unlock sequence will shut the document down (eg. a
    // linking client connecting and disconnecting).
    if (IsWindowVisible(m_hwnd)) 
        Lock(FALSE , fShutdown);     // Unlock the document on behalf of the user
    ShowWindow(m_hwnd, SW_HIDE);
    // OLE2NOTE: if there are no more documents visible to the user.
    // and the app itself is not under user control, then
       // it has no reason to stay visible. we thus should hide the
    // app. we cannot directly destroy the app, because it may be
    // validly being used programatically by another client
       // application and should remain running. it should simply be
    // hidded from the user.
    m_pApp->HideIfNoReasonToStayVisible();
}

/* CDocument::Close
 * ----------------
 *
 * Close active document. If modified, prompt the user if
 * he wants to save.
 *
 *  Returns:
 *      FALSE -- user canceled the closing of the doc.
 *      TRUE -- the doc was successfully closed
 */
BOOL CDocument::Close(DWORD dwSaveOption)
{
    BOOL fAbortIfSaveCanceled = (dwSaveOption == OLECLOSE_PROMPTSAVE);
    LPUNKNOWN pUnk;
    if (m_fDocClosing)
        return TRUE;    // Closing is already in progress
    if (! CheckSaveChanges(&dwSaveOption) && fAbortIfSaveCanceled)
        return FALSE;           // cancel closing the doc
    m_fDocClosing = TRUE;            // guard against recursive call
    /* OLE2NOTE: in order to have a stable app and doc during the
    **    process of closing, we intially AddRef the App and Doc ref
    **    cnts and later Release them. These initial AddRefs are
    **    artificial; they simply guarantee that these objects do not
    **    get destroyed until the end of this routine.
    */
    m_pApp->AddRef();
    AddRef();
    {
        /* OLE2NOTE: force all OLE objects to close. this forces all
        **    OLE object to transition from running to loaded. we can
        **    NOT exit if any embeddings are still running.
        **    if an object can't be closed and this close operation was
        **    started by the user, then we will abort closing our document.
        */
        if (! CloseAllOleObjects(OLECLOSE_NOSAVE) && fAbortIfSaveCanceled) {
            Release();       // release artificial AddRef above
            m_pApp->Release();        // release artificial AddRef above
            ClearFObjClosing();        // clear recursion guard
            return FALSE;   // Closing is aborted
        }
    }
#if defined( INPLACE_SVR )
        /* OLE2NOTE: if the server is currently in-place active we must
        **    deactivate it now before closing
        */
        ServerDoc_DoInPlaceDeactivate((LPSERVERDOC)lpOleDoc);
#endif
    /* OLE2NOTE: Revoke the object from the Running Object Table.
    */
    OleStdRevokeAsRunning(&m_dwRegROT);
    /* OLE2NOTE: if the user is in control of the document, the user
    **    accounts for one refcnt on the document. Closing the
    **    document is achieved by releasing the object on behalf of
    **    the user. if the document is not referenced by any other
    **    clients, then the document will also be destroyed. if it
    **    is referenced by other clients, then it will remain until
    **    they release it. it is important to hide the window and call
    **    IOleClientSite::OnShowWindow(FALSE) BEFORE sending OnClose
    **    notification.
    */
    Hide(TRUE);
#if defined( OLE_SERVER )
    {
      LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
      LPSERVERNAMETABLE lpServerNameTable =
         (LPSERVERNAMETABLE)((LPDOCUMENT)lpOleDoc)->m_lpNameTable;
      /* OLE2NOTE: force all pseudo-objects to close. this informs all
      **    linking clients of pseudo-objects to release their PseudoObj.
      */
      ServerNameTable_CloseAllPseudoObjs(lpServerNameTable);
      /* OLE2NOTE: send last OnDataChange notification to clients
      **    that have registered for data notifications when object
      **    stops running (ADVF_DATAONSTOP), if the data in our
      **    object has ever changed. it is best to only send this
      **    notification if necessary.
      */
      if (lpServerDoc->m_lpDataAdviseHldr) {
         if (lpServerDoc->m_fSendDataOnStop) {
            ServerDoc_SendAdvise((LPSERVERDOC)lpOleDoc,OLE_ONDATACHANGE,
                  NULL,   /* lpmkDoc -- not relevant here */
                  ADVF_DATAONSTOP);
         }
         /* OLE2NOTE: we just sent the last data notification that we
         **    need to send; release our DataAdviseHolder. we SHOULD be
         **    the only one using it.
         */
         OleStdVerifyRelease((LPUNKNOWN)lpServerDoc->m_lpDataAdviseHldr,
               OLESTR("DataAdviseHldr not released properly"));
         lpServerDoc->m_lpDataAdviseHldr = NULL;
      }
      // OLE2NOTE: inform all of our linking clients that we are closing.
      if (lpServerDoc->m_lpOleAdviseHldr) {
         ServerDoc_SendAdvise(
               (LPSERVERDOC)lpOleDoc,
               OLE_ONCLOSE,
               NULL,   /* lpmkDoc -- not relevant here */
               0       /* advf -- not relevant here */
         );
         /* OLE2NOTE: OnClose is the last notification that we need to
         **    send; release our OleAdviseHolder. we SHOULD be the only
         **    one using it. this will make our destructor realize that
         **    OnClose notification has already been sent.
         */
         OleStdVerifyRelease(
               (LPUNKNOWN)lpServerDoc->m_lpOleAdviseHldr,
               OLESTR("OleAdviseHldr not released properly") );
         lpServerDoc->m_lpOleAdviseHldr = NULL;
      }
      /* release our Container's ClientSite. */
      if(lpServerDoc->m_lpOleClientSite) {
         OleStdRelease((LPUNKNOWN)lpServerDoc->m_lpOleClientSite);
         lpServerDoc->m_lpOleClientSite = NULL;
      }
   }
#endif
    if (m_lpSLStm) {
        pUnk = (LPUNKNOWN)m_lpSLStm;
        m_lpSLStm = NULL;
        OleStdRelease(pUnk);
    }
    if (m_lpStg) {
        pUnk = (LPUNKNOWN)m_lpStg;
        m_lpStg = NULL;
        OleStdRelease(pUnk);
    }
    if (m_lpFileMoniker) {
        pUnk = (LPUNKNOWN)m_lpFileMoniker;
        m_lpFileMoniker = NULL;
        OleStdRelease(pUnk);
    }
    /* OLE2NOTE: this call forces all external connections to our
    **    object to close down and therefore guarantees that we receive
    **    all releases associated with those external connections.
    */
    CoDisconnectObject((LPUNKNOWN)&m_Unknown, 0);
    Assert(m_cExtConn == 0);
    Assert(m_cCntrLock == 0);
    // force any other LockContainer and internal locks to break
    while (m_cStrongRef > 0)
        Lock(FALSE, TRUE);
    Release();       // release artificial AddRef above
    m_pApp->Release();       // release artificial AddRef above
    return TRUE;
}
/* CDocument::Init
 * ---------------
 *
 *  Initialize the fields of a new Document object. The object is initially
 *  not associated with a file or an (Untitled) document. This function sets
 *  the docInitType to DOCTYPE_UNKNOWN. After calling this function the
 *  caller should call:
 *      1. CDocument::InitNewFile to set the Document to (Untitled)
 *      2. CDocument::LoadFromFile to associate the Document with a file.
 *  This function creates a new window for the document.
 *
 *  NOTE: the window is initially created with a NIL size. it must be
 *        sized and positioned by the caller. also the document is initially
 *        created invisible. the caller must call CDocument::ShowWindow
 *        after sizing it to make the document window visible.
 */
BOOL CDocument::Init(
    LPAPP        pApp, 
    HINSTANCE     hInst, 
    LPFRAME     pCFrame, 
    HWND         hwndFrame)
{
    ...
        
    m_pApp = pApp;
    m_pCFrame = pCFrame;
    // OLE2NOTE: an in-place contanier MUST use
    // WS_CLIPCHILDREN window style for the window
    // that it uses as the parent for the server's
    // in-place active window so that its
    // painting does NOT interfere with the painting
    // of the server's in-place active child window.
        
    m_hwnd = CreateWindowEx( WS_EX_ACCEPTFILES,
                            DOCWNDCLASS,
                            NULL,
                            WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_CHILDWINDOW,
                            0, 0,
                            0, 0,
                            hwndFrame,
                            (HMENU)1,
                            hInst,
                            NULL);
    Assert(m_hwnd);
    if(! m_hwnd) {
        m_pApp->ErrorMessage(ErrMsgDocWnd);
        return FALSE;
    }
   SetWindowLong(m_hwnd, 0, (LONG) this);
    m_docInitType        = DOCTYPE_UNKNOWN;
#if defined( _DEBUG )
    m_cCntrLock                    = 0;
    m_cExtConn                    = 0;
#endif
    m_cRef                        = 0;
    m_cStrongRef                = 0;
    m_pApp->DocLockApp();
    ...
        
    return TRUE;
}
/* CDocument::~CDocument
 * ---------------------
 *
 *  Destructor for a document.
 *  Unlock the lock that a document holds on the application.
 */
void CDocument::~CDocument()
{
    if (FObjDestroying())
        return;     // doc destruction is in progress
    // OLE2NOTE: in order to guarantee that the application does not
    // prematurely exit before the destruction of the document is
    // complete, we intially AddRef the App refcnt later Release it.
    // This initial AddRef is artificial; it simply guarantees that
    // the app object does not get destroyed until the end of this routine.
    m_pApp->AddRef();
    DestroyWindow(m_hwnd);
    ...
        
   /*****************************************************************
   ** OLE2NOTE: each document addref's the app object in order to  **
   **    guarentee that the app does not shut down while the doc   **
   **    is still open. since this doc is now destroyed, we will   **
   **    release this refcnt now. if there are now more open       **
   **    documents AND the app is not under the control of the     **
   **    user (ie. launched by OLE) then the app will revoke its   **
   **    ClassFactory. if there are no more references to the      **
   **    ClassFactory after it is revoked, then the app will shut  **
   **    down. this whole procedure is triggered by calling        **
   **    OutlineApp_DocUnlockApp.                                **
   *****************************************************************/
    m_pApp->DocUnlockApp(this);
    m_pApp->Release();       // release artificial AddRef above
}

/* CApp::Show
 * ----------
 *
 *      Show the window of the app to the user.
 *      make sure app window is visible and bring the app to the top.
 *      IF fGiveUserCtrl == TRUE
 *          THEN give the user the control over the lifetime of the app.
 */
void CApp::Show(BOOL fGiveUserCtrl)
{
    /* OLE2NOTE: while the application is visible and under user
   **    control, we do NOT want it to be prematurely destroyed when
   **    the user closes a document. thus we must inform OLE to hold
   **    an external lock on our application on behalf of the user.
   **    this arranges that OLE holds at least 1 reference to our
   **    application that will NOT be released until we release this
   **    external lock. later, when the application window is hidden, we
   **    will release this external lock.
   */
   if (fGiveUserCtrl && ! m_fUserCtrl) {
      m_fUserCtl = TRUE;
      Lock(TRUE /* fLock */, 0 /* not applicable */);
   }
   m_pCFrame->Show(TRUE /*fShow*/, TRUE /*fTakefocus*/);
   /* OLE2NOTE: because our app is now visible, we can enable the busy
   **    dialog. we should NOT put up any dialogs if our app is
   **    invisible.
   */
   EnableBusyDialogs(TRUE, TRUE);
}
/* CApp::Hide
 * ----------
 *
 *      Hide the window of the app from the user.
 *      Take away the control of the app by the user.
 */
void CApp::Hide()
{
#if defined( MDI_VERSION )
   /* OLE2NOTE: the application is now being hidden, so we must release
   **    the external lock that was made on behalf of the user.
   **    if this is that last external lock on our application, thus
   **    enabling our application to complete its shutdown operation.
   */
   if (m_fUserCtrl) {
      m_fUserCtrl = FALSE;
      Lock(FALSE /*fLock*/, TRUE /*fLastUnlockReleases*/);
   }
#endif 
   m_pCFrame->Show(FALSE /*fShow*/);
   /* OLE2NOTE: because our app is now INVISIBLE, we must DISABLE the busy
   **    dialog. we should NOT put up any dialogs if our app is
   **    invisible.
   */
   EnableBusyDialogs(FALSE, FALSE);
}

/* CApp::HideIfNoReasonToStayVisible
** ---------------------------------
**
** if there are no more documents visible to the user and the app
**    itself is not under user control, then it has no reason to stay
**    visible. we thus should hide the app. we cannot directly destroy
**    the app, because it may be validly being used programatically by
**    another client application and should remain running. the app
**    should simply be hidden from the user.
*/
void CApp::HideIfNoReasonToStayVisible()
{
#ifdef MDI_VERSION
   if (m_fUserCtrl) {
      return;     // remain visible; user in control of app
   }
#endif
   /* Because this is an SDI app, there is only one user document.
   ** check if it is visible to the user. an MDI app would loop over
   ** all open MDI child documents to see if any are visible.
   */
   if (m_pCDocument && IsWindowVisible(m_pCDocument->GetHWnd()))
      return;     // remain visible; the doc is visible to the user
   // if we reached here, the app should be hidden
   Hide();
}
/* CApp::DocLockApp
** ----------------
**    Add a lock on the App on behalf of the Doc. the App may not close
**    while the Doc exists.
**
**    when a document is first created, it calls this method to
**    guarantee that the application stays alive (Init).
**    when a document is destroyed, it calls
**    DocUnlockApp to release this hold on the app.
*/
void CApp::DocLockApp()
{
   ++m_cDoc;
   Lock(TRUE /* fLock */, 0 /* not applicable */);
}
/* CApp::DocUnlockApp
** ------------------
**    Forget all references to a closed document.
*/
void CApp::DocUnlockApp(LPDOCUMENT lpOutlineDoc)
{
   /* forget pointers to destroyed document */
   if (m_pCDocument == lpOutlineDoc)
      m_pCDocument = NULL;
   /* OLE2NOTE: when there are no open documents and the app is not
   **    under the control of the user then revoke our ClassFactory to
   **    enable the app to shut down.
   **
   **    NOTE: data transfer documents (non-user documents) do NOT
   **    hold the app alive. therefore they do not Lock the app.
   */
    if (! lpOutlineDoc->FDataTransferDoc()){
        ULONG cDoc;
        /* OLE2NOTE: when there are no open documents and the app is not
        **    under the control of the user then revoke our ClassFactory to
        **    enable the app to shut down.
        */
        cDoc = --m_cDoc;
        AssertSz (m_cDoc >= 0, "DocUnlockApp called with cDoc == 0");
        Lock(FALSE /* fLock */, TRUE /* fLastUnlockReleases */);
    }
}
/* CApp::Lock
** ----------
**    Lock/Unlock the App object. if the last lock is unlocked and
**    fLastUnlockCloses == TRUE, then the app object will shut down
**    (ie. it will recieve its final release and its refcnt will go to 0).
*/
DWORD CApp::Lock(BOOL fLock, BOOL fLastUnlockCloses)
{
    DWORD cLock;
    if (fLock) {
        cLock = ++m_cStrongRef;
        AddRef();    // released later when Lock(FALSE) called
    } else  {
        if ( m_cStrongRef > 0 ){
            --m_cStrongRef;
        }else{
            AssertSz (m_fAppClosing, "App::Lock(FALSE) called with m_cStrongRef == 0");
             return 0;    // all locks already released
        }
        cLock = m_cStrongRef;
        if( m_cStrongRef == 0 && fLastUnlockCloses )
             CloseAllDocsAndExit();    
        Release();
    }
   return cLock;
}

/* CApp::CloseAllDocsAndExit
 * -------------------------
 *
 *  Close all active documents and exit the app.
 *  Because this is an SDI, there is only one document
 *  If the doc was modified, prompt the user if he wants to save it.
 *
 *  Returns:
 *      TRUE if the app is successfully closed
 *      FALSE if failed or aborted
 *
 * OLE2NOTE: we cannot directly
 *     destroy the App object. we can only take all
 *     necessary actions to ensure that our object receives
 *     all of its Releases from clients holding onto
 *     pointers (eg. closing all docs and flushing the
 *     clipboard) and then we must hide our window and wait
 *     actually for our refcnt to reach 0. when it reaches 0,
 *     our destructor (Destroy) will be called.
 *     each document addref's the app object in order to
 *     guarentee that the app does not shut down while the doc
 *     is still open. closing all docs, will release these
 *     refcnt's. if there are now more open documents AND the
 *     app is not under the control of the user (ie. launched by
 *     OLE) then the app will now shut down. the Release
 *     function executes this shut down procedure. after closing
 *     all docs, then releasing the user refcnt will force the
 *     app to shut down.
 */
BOOL CApp::CloseAllDocsAndExit(BOOL fForceEndSession)
{
   DWORD dwSaveOption = (fForceEndSession ? OLECLOSE_NOSAVE : OLECLOSE_PROMPTSAVE);
    if (m_fAppClosing)
        return TRUE;    // Closing is already in progress
    m_fAppClosing = TRUE;            // guard against recursive call
   /* OLE2NOTE: in order to have a stable App object during the
   **    process of closing, we intially AddRef the App ref cnt and
   **    later Release it. This initial AddRef is artificial; it is
   **    simply done to guarantee that our App object does not
   **    destroy itself until the end of this routine.
   */
   AddRef();
   /* Because this is an SDI app, there is only one document.
   ** Close the doc. if it is successfully closed and the app will
   ** not automatically exit, then also exit the app.
   ** if this were an MDI app, we would loop through and close all
   ** open MDI child documents.
   */
    if (! m_pCDocument->Close(dwSaveOption)) {
      Release();
      return FALSE;     // User Aborted shutdown
   }
   Hide();
   /* OLE2NOTE: this call forces all external connections to our
   **    object to close down and therefore guarantees that we receive
   **    all releases associated with those external connections.
   */
   CoDisconnectObject((LPUNKNOWN)&m_Unknown, 0);
   Release();       // release artificial AddRef above
   return TRUE;
}
/*************************************************************************
** CApp::IClassFactory interface implementation
*************************************************************************/
STDMETHODIMP NC(CApp,CClassFactory)::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
        *ppvObj = (LPVOID) this;
    else {
        *ppvObj = NULL;
        return ResultFromScode(E_NOINTERFACE);
    }
    ((IUnknown FAR*) *ppvObj)->AddRef();
    return NOERROR;
}
STDMETHODIMP_(ULONG) NC(CApp,CClassFactory)::AddRef()
{
   return m_cRef++;
}
STDMETHODIMP_(ULONG) NC(CApp,CClassFactory)::Release()
{
    LONG cRef = --m_cRef;
    
    // NOTE: the memory for this CClassFactory object is allocated as part of 
    // the CApp object. It will be destroyed when the CApp object is destroyed.
    
    Assert(cRef >= 0);        
       return cRef;
}

STDMETHODIMP NC(CApp,CClassFactory)::CreateInstance (
        LPUNKNOWN           lpUnkOuter,
        REFIID              riid,
        LPVOID FAR*         lplpvObj)
{
    HRESULT     hrErr;
    /* OLE2NOTE: we must make sure to set all out parameters to NULL. */
    *lplpvObj = NULL;
    /*********************************************************************
    ** OLE2NOTE: this is an SDI app; it can only create and support one
    **    instance. After the instance is created, the OLE libraries
    **    should not call CreateInstance again. it is a good practise
    **    to specifically guard against this.
    *********************************************************************/
    if (m_lpApp->m_pCDocument != NULL)
        return ResultFromScode(E_UNEXPECTED);
    /* OLE2NOTE: create a new document instance. by the time we return
    **    from this method the document's refcnt must be 1.
    */
    m_lpApp->m_pCDocument = m_lpApp->CreateDoc();
    if (!m_lpApp->m_pDocument) {
        return ResultFromScode(E_OUTOFMEMORY);
    }
    /* OLE2NOTE: retrieve pointer to requested interface. the ref cnt
    **    of the object after OutlineApp_CreateDoc is 0. this call to
    **    QueryInterface will increment the refcnt to 1. the object
    **    returned from IClassFactory::CreateInstance should have a
    **    refcnt of 1 and be controlled by the caller. If the caller
    **    releases the document, the document should be destroyed.
    */
    hrErr = m_lpApp->m_pDocument->QueryInterface(riid, lplpvObj);
    return hrErr;
}
STDMETHODIMP NC(CApp,CClassFactory)::LockServer (BOOL fLock)
{
    HRESULT hrErr;
#if defined( _DEBUG )
    if (fLock) {
        ++m_cSvrLock;
    } else {
        /* OLE2NOTE: when there are no open documents and the app is not
        **    under the control of the user and there are no outstanding
        **    locks on the app, then revoke our ClassFactory to enable the
        **    app to shut down.
        */
        --m_cSvrLock;
        AssertSz (m_cSvrLock >= 0,"LockServer(FALSE) called with cLock == 0");
    }
#endif  // _DEBUG
    /* OLE2NOTE: in order to hold the application alive we call
    **    CApp::Lock to add a strong reference to our app
    **    object. this will keep the app alive when all other external
    **    references release us. if the user issues File.Exit the
    **    application will shut down in any case ignoring any
    **    outstanding LockServer locks because lock are forcibly
    **    broken in CloseAllDocsAndExitCommand. this will guarantee 
    **    that the App object gets its final release (ie. cRefs goes to 0).
    */
    hrErr = m_lpApp->Lock( fLock, TRUE /* fLastUnlockReleases */);
    return hrErr;
}
/* CApp:: ParseCmdLine
** -------------------
**    Parse commandline arguments. Check if app is launched by OLE ("-Embedding").
**    If necessary register our class object with CoRegisterClassObject API.
*/
BOOL CApp::ParseCmdLine(LPSTR lpszCmdLine, int nCmdShow)
{
    char szFileName[256];
    BOOL fStatus = TRUE;
    BOOL fEmbedding = FALSE;
    BOOL fFileNew = FALSE;
    OLECHAR szUniStr[256];
    szFileName[0] = '\0';
    ParseCmdLine(lpszCmdLine, &fEmbedding, &fFileNew, (LPSTR)szFileName);
    if(fEmbedding) {
        if (szFileName[0] == '\0') {
         /*****************************************************************
         ** App was launched with /Embedding.
         **    We must register our ClassFactory with OLE, remain hidden
         **    (the app window is initially created not visible), and
         **    wait for OLE to call IClassFactory::CreateInstance
         **    method. We do not automatically create a document as we
         **    do when the app is launched by the user from the
         **    FileManager. We must NOT make our app window visible
         **    until told to do so by our container.
         **
         ** OLE2NOTE: Because we are an SDI app, we only register our
         **    ClassFactory if we are launched with the /Embedding
         **    flag WITHOUT a filename. an MDI app would ALWAYS
         **    register its ClassFactory. it can handle multiple
         **    objects at the same time, while an SDI application
         **    can only handle a single embedded or file-based
         **    object at a time.
         *****************************************************************/
            fStatus = RegisterClassFactory();
            return fStatus;
        }
       /*****************************************************************
       ** App was launched with /Embedding <Filename>.
       **    We must create a document and load the file and
       **    register it in the RunningObjectTable BEFORE we
       **    enter our GetMessage loop (ie. before we yield).
       **    One way to perform these tasks is to call the same
       **    interface methods that OLE 2.0 calls for linking to a
       **    file:
       **          IClassFactory::CreateInstance
       **          IPersistFile::Load
       **
       **    We must NOT make our app window visible until told to
       **    do so by our container. An application will be
       **    launched in this manner by an OLE 1.0 application
       **    link situation (eg. double clicking a linked object
       **    or OleCreateLinkFromFile called).
       **
       ** OLE2NOTE: Because we are an SDI app, we should NOT
       **    register our ClassFactory when we are launched with the
       **    /Embedding <Filename> flag. our SDI instance can only
       **    handle a single embedded or file-based object.
       **    an MDI app WOULD register its ClassFactory at all
       **    times because it can handle multiple objects.
       *****************************************************************/
        // allocate a new document object
        m_pDocument = CreateDoc();
        if (!m_pDocument) {
            return FALSE;
        }
       /* OLE2NOTE: initially the Doc object is created with a 0 ref
       **    count. in order to have a stable Doc object during the
       **    process of initializing the new Doc instance,
       **    we intially AddRef the Doc ref cnt and later
       **    Release it. This initial AddRef is artificial; it is simply
       **    done to guarantee that a harmless QueryInterface followed by
       **    a Release does not inadvertantly force our object to destroy
       **    itself prematurely.
       */
        m_pDocument->AddRef();
       /* OLE2NOTE: LoadFromFile will register our document
       **    in the RunningObjectTable. this registration will
       **    AddRef our document. therefore our document will not
       **    be destroyed when we release the artificial AddRef
       */
        A2W (szFileName, szUniStr, 256);
        fStatus = m_pDocument->LoadFromFile( (LPOLESTR)szUniStr);
        m_pDocument->Release(); // rel AddRef
        return fStatus;
    }
    /*****************************************************************
    ** App was launched by the user (without /Embedding) and
    **    therefore is marked to be under user control.
    **    In this case, because we are an SDI app, we do NOT
    **    register our ClassFactory with OLE. This app instance can
    **    only manage one document at a time (either a user
    **    document or an embedded object document). An MDI app
    **    would register its ClassFactory here.
    **
    **    We must create a document for the user (either
    **    initialized from a file given on the command line or
    **    initialized as an untitled document. We must also make
    **    our app window visible to the user.
    *****************************************************************/
    // allocate a new document object
    m_pDocument = CreateDoc();
    if (!m_pDocument){
          AssertSz(0, "CreateDoc Failure" );
        goto error;
    }
    /* OLE2NOTE: initially the Doc object is created with a 0 ref
    **    count. in order to have a stable Doc object during the
    **    process of initializing the new Doc instance,
    **    we intially AddRef the Doc ref cnt and later
    **    Release it. This initial AddRef is artificial; it is simply
    **    done to guarantee that a harmless QueryInterface followed by
    **    a Release does not inadvertantly force our object to destroy
    **    itself prematurely.
    */
    m_pDocument->AddRef();
    // show main app window
    ShowWindow(m_hwndApp, nCmdShow);
    UpdateWindow(m_hwndApp);
    if(*szFileName) {
        if (fFileNew)
            {
            // initialize the document from the specified file
            A2W (szFileName, szUniStr, 256);
            if (!m_pDocument->LoadFromFile(szUniStr))
                {
                AssertSz(0, "LoadFromFile Failure" );
                goto error;
                }
            m_pDocument->m_docInitType = DOCTYPE_NEW;
            OLESTRCPY(m_pDocument->m_szFileName, UNTITLED);
            m_pDocument->m_lpszDocTitle = m_pDocument->m_szFileName;
            m_pDocument->SetTitle();
            }
        else 
            {
            // initialize the document from the specified file
            A2W (szFileName, szUniStr, 256);
            if (!m_pDocument->LoadFromFile(szUniStr))
                {
                AssertSz(0, "LoadFromFile Failure" );
                goto error;
                }
            } // LoadfromFile
        } // szFileName != NULL
    else{
        // set the doc to an (Untitled) doc.
        if (! m_pDocument->InitNewFile()){
            AssertSz(0, "InitNewFile Failure" );
            goto error;
        }
    }
    // position and size the new doc window
    m_pDocument->Release();// rel AddRef above
    m_pDocument->Show();
    return fStatus;
error:
    ErrorMessage(ErrMsgNewOOM);
    if (m_pDocument)      // rel artificial AddRef above
        m_pDocument->Release();
    return FALSE;
}
/* CApp::RegisterClassFactory
 * --------------------------
 *
 * Register our app's ClassFactory with OLE.
 *
 */
BOOL CApp::RegisterClassFactory()
{
   HRESULT hrErr;
   if (m_dwRegClassFac)
      return TRUE;    // already registered
   /******************************************************************
   ** An SDI app must register its ClassFactory if it is launched
   **    for embedding (/Embedding command line option specified).
   ** An MDI app must register its ClassFactory in all cases,
   ******************************************************************/
   hrErr = CoRegisterClassObject(
            CLSID_APP,
            (LPUNKNOWN) &m_ClassFactory,
            CLSCTX_LOCAL_SERVER,
            REGCLS_SINGLEUSE,
            &m_dwRegClassFac
   );
   if(hrErr != NOERROR) {
      OleDbgOutHResult("CoRegisterClassObject returned", hrErr);
      ErrorMessage(ErrMsgRegCF);
         return FALSE;
   }
   return TRUE;
}

/* CApp::RevokeClassFactory
 * ------------------------
 *
 * Revoke our app's ClassFactory.
 *
 */
void CApp::RevokeClassFactory()
{
    if (m_dwRegClassFac) {
        CoRevokeClassObject(m_dwRegClassFac);
        m_dwRegClassFac = 0;
    }
}