Object States and Activation

OLE Documents introduces a few object states above and beyond the simple passive, loaded, and running states that we've seen in earlier chapters. The following table explains how OLE defines and differentiates these states:

State

Description

Passive

The object exists only as a persistent representation in some storage medium (disk, memory, and so on).

Loaded

Some in-process code is loaded for the object in the client's process and can access the persistent data as necessary. This state indicates that at least an in-process handler is loaded for the object. Loading an object does not mean launching a local or remote server.

Running

The object's server is fully loaded and running and has complete access to its persistent data. The object is registered in the running object table. The object has created its user interface, but that UI is not visible to the end user.

Active

The object's user interface is visible to the end user.


We've seen many objects in earlier chapters that blur the distinction between these states: some objects make no distinction between loaded and running; others make no distinction between running and active. In OLE Documents, however, all of these states are distinct for both embedded and linked objects. (Objects that are also in-place capable have additional states, as we'll see in Chapters 22 and 23.) Also, specific actions on behalf of the client control how the object moves from state to state. OLE Documents is primarily concerned with the control of active objects, as activation is one of its primary features.

The sequence of CoCreateInstance followed by IPersistStream::Load or IPersistStorage::Load moves a passive object into at least the loaded state, depending on the object. These sequences are encapsulated in API functions such as OleLoadFromStream and OleLoad, both of which return a new interface pointer to the newly loaded object. Calling this object's Release member will take the object back to the passive state. (A client usually does this after saving the object with OleSaveToStream or OleSave.)

In OLE Documents, the loaded state means that only some in-process code is loaded for the object. If the object has an in-process server, that server DLL is in memory. If an in-process server is not available, OLE will try to load an in-process handler, be it a custom handler or the OLE default handler. In OLE Documents, the default handler is always loaded in the absence of a custom handler. Outside OLE Documents, of course, the most that is loaded is a simple object proxy. In any case, a client always has an interface pointer to a loaded object and can call members in that interface as desired.

The client, or container, can now precisely move the object into the running state through an interface named IRunnableObject, which includes member functions named Run, IsRunning, LockRunning, and SetContainedObject. Calls to these members are wrapped for convenience in the OLE API functions OleRun, OleIsRunning, OleLockRunning, and OleSetContainedObject. When Run is called on an in-process server that supports OLE Documents (as we'll see in Chapter 19), the object initializes any user interface it needs when active and registers itself in the running object table. It does not, however, show itself. When called on a handler or the generic proxy, Run causes that handler to launch the local server (through CoCreateInstance). Servers that support OLE Documents will use the presence of -Embedding on the command line as a signal that the object in question has now entered the running state but should not be visible. This is why servers, such as those we saw with OLE Automation, are not generally supposed to make themselves visible on startup when -Embedding is present.

Some other means are necessary to make the object visible. In OLE Automation, this is usually controlled through an object's Visible property—setting Visible to TRUE makes the object visible, and setting it to FALSE hides the object. In cases outside OLE Documents, having the user close the object's UI directly or having the client release the object programmatically is the only way to get an object out of the running state and all the way back to passive.

OLE Documents, however, gives an added layer of control, represented through two member functions of the interface IOleObject. As we'll see later in this chapter, this interface represents objects that understand OLE Documents. Two of its member functions interest us here. The first is IOleObject::DoVerb, which takes an integer verb identifier. A verb is a specific action the object can execute that has meaning to the end user. Typical verbs are Edit, Open, Show, Hide, and Play. A container uses an object's registry information to determine which verbs to display to the end user on an object-specific pop-up menu. When the user selects one of these items, the container calls IOleObject::DoVerb, which will run an object first if the object is not already in that state.

The Show verb moves the object from the invisible running state into the visible active state. This verb has a value named OLEIVERB_SHOW and can be implicit in some of the object's other custom verbs at the object's choosing. In any case, Show makes the object visible so that the end user can now manipulate the object directly. Once the object is visible, the Hide verb, OLEIVERB_HIDE, tells the object to move back to the invisible running state and stay there. By using these verbs, passed to the object through IOleObject::DoVerb, a container can precisely control an object's transitions between hidden and visible.

The other function of interest is IOleObject::Close, which takes an active or a running object back to the loaded state—but not as far back as passive. After IOleObject::Close, the container will still have valid interface pointers to the object because that object is still loaded. The container can then call OleRun or IOleObject::DoVerb again to get the object running or active.

With this basic understanding of the various states of content objects, we're now ready to see the compound document architecture as a whole.