OLE Document Objects

by Dan Wygant

Chapter 11 of ActiveX and VBScript, Volume 3 of the Web Publishing and Programming Resource Kit.


Using the Internet Explorer browser to surf my corporate intranet, I found a hyperlink to a CAD drawing and got curious. To satisfy my curiosity, I clicked the hyperlink. Almost instantly the drawing displayed inside the Internet Explorer (IE) browser window (as shown in Figure 11.1) with merged menus and Imagineer's toolbar.

Figure 11.1. Intergraph Imagineer CAD drawing in Internet Explorer.

The file was an Intergraph Imagineer CAD drawing with the .IGR filename extension. Because Imagineer can act as an ActiveX Document, the IE activated Imagineer in a mode Microsoft refers to as Visual Editing inside of IE.

I was truly pleased to see Imagineer work as an ActiveX Document (or DocObject) just as Microsoft Office's Word for Windows will act as an ActiveX Document. To have a CAD drawing product go ActiveX made me really start thinking ahead. Seeing Imagineer as a DocObject suggests that other products will soon follow suit by providing ActiveX Document capabilities for their file formats.

This fits right in with Microsoft's vision of an Active Desktop, in which every application, every file format, can be displayed as an ActiveX Document. From this vision two questions arise:

An answer to the work flow question can be illustrated by showing you what Intergraph's Imagineer product can do as an ActiveX Document within Microsoft's Internet Explorer. The second is more a question of basic object-oriented programming (OOP) methodology, now that Microsoft Foundation Classes (MFC) provide ActiveX document server support. You will discover this basic OOP methodology in the context of building on top of an MFC AppWizard-generated project. But before we start into MFC's implementation for ActiveX document server support, we must cover the specifications of the ActiveX Document interfaces.

So to reiterate, first you'll see an example of how to use an ActiveX Document in a work flow. Then I give you some basics on why you should use MFC, followed by ActiveX Document interface specifications. Finally, I will lay out the OOP methodology behind MFC's implementation for ActiveX document server support.

DocObject Work-Flow Possibilities

The key to successfully providing ActiveX Document support in your application is to define a work flow that adds value to your product. The added value should come in the form of ease of use and higher productivity for the user. Let's identify what ActiveX Documents can be used for before we try to define a work flow.

As mentioned in the introduction, an ActiveX Document is displayed in Visual Editing mode inside another application. There are two main ActiveX Document containers in which DocObjects do Visual Editing: The first is the Microsoft Office Binder, and the other - more important and pertinent - is the IE. The IE, an ActiveX container, can display ActiveX Documents in Visual Editing mode when the user clicks a hyperlinked file whose associated application can act as a DocObject. The IE, and the Office Binder DocObject container, basically provide a host for the ActiveX Document to run and display in. Figures 11.2, 11.3, and 11.4 show how two applications (container and server) merge into a single application. Figure 11.2 shows Imagineer running stand-alone, Figure 11.3 shows IE running stand-alone, and Figure 11.4 shows Imagineer running in the context of the IE. The callouts in Figure 11.4 specify which application is responsible for each portion of the merged graphical user interface.

Figure 11.2. Imagineer as a stand-alone application.




















Figure 11.3. IE as a stand-alone application.





















Figure 11.4. IE and Imagineer DocObject container and server applications merged.

The DocObject server basically provides a way to view or edit a particular file format in the context of another application, an ActiveX Document container. This is similar to the "Open" verb in OLE terminology. As stated in the introduction, Microsoft has coined the term "Visual Editing" to describe this cross between opening and editing with one application in another application's frame. The difference is that a DocObject server will take over the entire viewing window, whereas a regular OLE server will be edited where it is displayed, that is, "in place." Also, the menu merging between the container application and the running ActiveX Document server is slightly different from earlier OLE menu merging. In particular, the help pull-down menu is merged to have submenus for container and server help commands.

This should give you an idea about what a DocObject does. Let's look at an excellent example that illustrates a work flow centered on this Visual Editing technology. Intergraph's Imagineer Technical CAD drawing product leverages not just the DocObject technology, but the other OLE technology made available by ActiveX and the Internet Explorer. To start with, you saw Imagineer embedded inside the IE. But, as you see in Figure 11.5, Imagineer also has an instance of an IE browser window embedded.

Figure 11.5. Imagineer DocObject with IE embedded.

This second embedded IE provides the following capabilities:

With the hyperlinked tutorials, the user is slow-walked through creating CAD drawings. As the user moves through each step in the embedded IE, VBScript code drives Imagineer with OLE Automation. In each step, a portion of the tutorial's final CAD drawing is completed until the whole drawing has been created. An intermediate step is illustrated in Figure 11.6.

Figure 11.6. Embedded IE HTML tutorial drives drawing in Imagineer with VBScript.

The work flow in this case is that if you wish to come up to speed quickly, you work through the tutorials needed for the task at hand. Suppose you want to create a line and associate its endpoint with a tangent to a circle; their dimensions will depend on a tangent keypoint on the circle. You just bring up the tutorial for that type of operation, and it seamlessly guides you through the simple steps required for such an operation.

In the next work flow case, Imagineer symbol files (CAD symbols such as a door or a window) are displayed in the embedded IE. The embedded IE displays these symbols in an IE file view with large icons. Figure 11.7 shows a completed symbol drag-and-drop operation.

Figure 11.7. Drag-and-drop symbols from embedded IE into Imagineer.

The work flow in this case is that you can use the embedded IE to browse your company intranet to find the drawing or symbol you need. When you find what you want, just drag it into Imagineer's drawing window and bingo - you have a completed drawing. It is also likely that there will be external sites on the Internet that contain collections of Imagineer symbols that can just as simply be dragged and dropped into your drawing window.

The whole work flow idea is to leverage the OLE capabilities given to us by ActiveX in new and useful ways - making our jobs easier and therefore more productive.

Leveraging MFC DocObjects

The current software evolution/revolution now seems centered around ActiveX and the Internet. It is expected that many developers will want their products to support the ActiveX Document interfaces. Moreover, in recent years there has been a surge in OLE development. Therefore, many products are already in a good position to add support for ActiveX Document interfaces. This is even easier if your product already leverages off the Visual C++ (VC++) MFCs. Actually the term leverage really implies parentage in, or subclassing from, the MFC Document-View architecture (but more on that in the following paragraphs). Right now you need to realize the power and flexibility that the Visual C++ Application Wizard (AppWizard) provides. The AppWizard's MFC framework uses OOP methodologies, allowing you to create an ActiveX Document application with ease.

What you get by using the MFC DocObject framework is an ActiveX Document server application executable built from a set of MFC subclasses created automatically by the AppWizard. Microsoft uses the OOP methodology of subclassing to specify parent classes with DocObject support. The subclasses created by the AppWizard, when you specify you want ActiveX document server support, use MFC's DocObject support classes. If you do not specify ActiveX document server support in the AppWizard, you end up with a different set of parent classes. The MFC's DocObject support classes still define a Document-View architecture that is at the heart of what MFC has given us in the past. The difference is that activation model supports (DocObject) Visual Editing in addition to what has been supported in the past.

This set of parent MFC DocObject-support classes is really the only major difference between a normal MFC AppWizard application and one with DocObject support. To provide DocObject support using the MFC framework, Microsoft basically just added a new set of intermediate (or parent) classes for your application's classes to subclass from. When you select the ActiveX document server option, the AppWizard just uses these MFC DocObject support parent classes for your Document-View parent classes. Figure 11.8 illustrates how these new classes fit in with previous levels of MFC parent classes and what each level of parentage supplies.

Figure 11.8. MFC Document-View class hierarchy.

Some of the nice things about using MFC's Document-View architecture are that you are buffered from underlying changes and you get new capabilities with very little development effort. Let me explain these concepts a bit more. The idea of subclassing to get capabilities the parent class provides is not a new one; it is the main idea behind doing OOP.

The great thing about subclassing is that if a developer changes the code in the parent class, he or she doesn't (usually) have to change anything in the subclass; the subclass instantly receives the benefits (or detriments if a bug is introduced!) of the new code in the parent class. In addition, if a change is made at an even lower level, the chances are that any required change will be taken care of by one of the intermediate parent classes. Again the developer are buffered from underlying changes.

In the future, when Microsoft adds even more capabilities to the Document-View architecture, you can get those capabilities for little, if any, development cost. Typically in the past, MFC has just added another layer of abstraction by adding a new set of intermediate parent classes. For DocObject support, MFC added a new set of (abstract) parent classes to handle the differences in creation and activation. The new set of MFC classes integrate the additional DocObject-OLE interfaces in this abstraction layer to buffer applications from change. So if you already have an MFC AppWizard-generated application, virtually all you may need to do is change your parent classes to use these new MFC DocObject-support classes.

Tip

You should always save your original MFC AppWizard-generated project before making any changes. In addition, write down the answers you gave along the way. The point to doing this is that later on when the AppWizard adds support for another feature, you can go through the wizard, generate a base application with this additional support, and compare the differences with the original. The differences you find will guide you through adding this support manually. Perhaps in some future release of Visual C++, Microsoft might provide an upgrade wizard, but for now it may be wise to practice this tidbit of wizardry on you own!

 

This OOP methodology of using special parent classes provides the capabilities that you need with little effort in recoding. The Imagineer product uses the same OOP methodology to provide and manage common functionality used by multiple applications. There was a need for specialized initialization and display code, and it was needed by more than one application. What was needed was a set of Document-View subclasses that could act as another layer of abstraction from the MFC classes. That is exactly what was done: The new set of classes provided the common functionality for each application. The new set of classes fits right in between the AppWizard-generated classes and the MFC DocObject-support classes, as illustrated in Figure 11.9.

Figure 11.9. Imagineer's Document-View class hierarchy.

Again, this buffers the various applications from extreme changes in implementation and provides a clear migration path for future enhancements to both MFC's and Imagineer's framework. So all the applications built on top of Imagineer, which is built on top of MFC, will not suffer the slings and arrows of continuous code, or even design, changes.

ActiveX Document Specifications

You should now understand what to do with a DocObject in terms of work flow and have a feel for what MFC provides for DocObject support. Let's look at the specifications for the OLE interfaces required of a DocObject. There are two sides of the DocObject coin: the server side and the container side. Each side has DocObject interfaces that must be implemented. For instance, the IE has implemented the container-side ActiveX Document interfaces, and Imagineer has implemented the server-side ActiveX Document interfaces. Imagineer, being the server, serves up a picture into the browser window of the IE DocObject-container. The server also occupies most of the container's pull-down menu and adds its own toolbars to the frame while it (Imagineer as a DocObject Server) is active. All of this activity occurs through the interfaces described in the following "ActiveX Document Server-Side Interfaces" section.

ActiveX Document Server-Side Interfaces

The interfaces that the ActiveX Document server must implement are listed as follows:

For a particular DocObject, or document instance, there is one IOleDocument object. This IOleDocument object, which represents the actual document, is referred to as the document. Each document has one or more IOleDocumentView interfaces. Each one of these objects represents one view; each of these views is merely a different representation of the data in the document. For instance, the document may be viewed with different levels of detail. One view may have a high level of detail presented by zooming in on a CAD drawing of a house to look at the kitchen with all display layers turned on. Another view might be viewing the entire drawing with only an outline layer of detail displayed. These various views or representations of the data that your DocObject application can present are the views that are specified by the IOleDocumentView interfaces object.

If your server does maintain a set of views, then the IEnumOleDocumentViews interface can be implemented in your DocObject server. The container may initially ask your document for a status flag that indicates, among other things, whether your ActiveX Document server can provide more than one view. The ActiveX Document container may also ask your document to return this document view enumeration, an enumeration of IOleDocumentView interface objects. If your DocObject has only one view, then it returns that one view (IOleDocumentView interface object) and NULL for the enumeration. If multiple views are maintained, then your DocObject application should return its IEnumOleDocumentViews interface object.

Note

The idea of multiple views really applies to CAD products like Imagineer, but there has not yet been any kind of implementation of a standard GUI to allow the user to enumerate the views. The use of multiple views in DocObject containers appears to be left for specific applications. Neither the IE nor the Office Binder use multiple views (yet). One area where it has been considered is in the OLE for Design and Modeling specification (OLE for D&M). An OLE for D&M server serves up a 3D picture using an OpenGL interface wrapper for the rendering. The IOleDocumentView interface could have been used to allow the user to pick a view to be placed. The OLE for D&M DocObject server would use a small storage in the container to save information describing the view selected. The view information saved could be parameters used to drive the display in OpenGL. This was still under consideration as of this writing. To find out more about OLE for D&M browse http://www.dmac.orgTo contact the Design and Modeling council, e-mail owner-dmac@list.intergraph.comXXX and put the word "subscribe" in the body of the e-mail message to subscribe to the OLE for D&M mailing list.

ActiveX Document Container-Side Interfaces

On the client side - the container - there is an interface called IOleDocumentSite. It's just what it sounds like: the server's "view site" within the container. This interface represents where the server's ActiveX Document will "live" in the client's application, or where the DocObject will be placed. The IOleDocumentSite isn't really used in that manner, but rather tells your DocObject application that it's supposed to act like a DocObject and not like the typical OLE 2.0 server object. This makes a difference when the container asks your ActiveX Document to activate; you can tell the container to activate with a particular - favorite or default - view.

Optional Interfaces

There are a few other optional, yet important, DocObject interfaces that are part of the ActiveX DocObject specification. The following is a brief description of each of these interfaces:

OLE Interfaces

It is important to understand that being an OLE Document Object implies support and use of some standard OLE interfaces. The nice thing about OLE is that, even with the flurry of extensions for specific needs - like DocObjects - everything still fits into the OLE scenario. What I mean is that the scenario for activating a DocObject server is similar to the scenario for activating an OLE server. For instance, the IOleObject and IPersistFile interfaces are used slightly out of context from the way they are used in standard OLE. But the difference really makes sense in the context of what an ActiveX Document needs in order to activate. Specifically, the IPersistFile::Load() method is used, just as in OLE, by the container to supply the filename to the server. On the other hand, the IOleObject::SetClientSite() method is used by OLE Document Objects specifically to obtain the "view site" interface pointer (and therefore to know it is to act as a DocObject).

Although different from typical OLE, these are logical uses of existing OLE technology. And everything still works for all of the previous OLE scenarios. From my perspective, this is significant in that OLE interfaces have in the past been thought out carefully to provide a steady migration path for additions to the OLE specification. Adding features like DocObject support without breaking existing OLE applications would be almost inconceivable otherwise.

Interface and Method Specifics

Now that you know the basics, let's cover a few details that are essential. The following provides a method level of detail of the essential DocObject server-side interfaces:

- IOleDocument: This represents the file, or rather the "document." For both the Binder and IE, all QueryInterface() calls go through this interface. It represents the DocObject, is one-to-one with the file, and (possibly) one-to-many with views of the file. It has the following interface methods:

CreateView(): Returns a view object (IOleDocumentView), possibly pre-initializing it with a saved view state stored in a stream (IStream) passed in. The view site (IOleInPlaceSite) may also be passed in.

Parameters

[in] IOleInPlaceSite *pIPSite

[in] IStream *pstm

[in] DWORD dwReserved

[out] IOleDocumentView **ppView

GetDocMiscStatus(): Returns a status flag (actually a bit mask), duplicated in the object's registry under the DocObject key. The client is responsible for using this status to determine how to communicate with the DocObject. The values listed in Table 11.1 can be combined together with the | (OR) operator to create the status mask.

Parameter

[out] DWORD *pdwStatus

Table 11.1. The values that may be assigned to the status word returned.

DOCMISC enumerations = Meaning Interfaces

DOCMISC_CANCREATEMULTIPLEVIEWS 1 supports multiple IEnumOleDocumentViews
views interface supported

DOCMISC_SUPPORTCOMPLEXRECTANGLES 2 can use complex IOleDocumentView's
rectangles for SetRectComplex interface
scrollbar method not construction implemented
DOCMISC_CANTOPENEDIT 4 cannot "open" IOleDocumentView's Open
stand-alone view interface method not
application implemented

DOCMISC_NOFILESUPPORT 8 cannot read/ write IPersistFile interface
file not implemented, only
IPersistStorage
interface
implemented

EnumViews(): Returns either an enumeration of view objects (IEnumOleDocumentViews) if your DocObject wishes to support more than one view, or a single-view object (IOleDocumentView) if not.

Parameters

[out] IEnumOleDocumentViews **ppEnum

[out] IOleDocumentView **ppView

- IOleDocumentView: Represents a single view of your document. If your DocObject supports multiple views, there is a many-to-one relationship of views to document; otherwise, it's a one-to-one relationship. According to the specification: "This interface provides all the necessary operations for a container to manipulate, manage, and activate a view."

SetInPlaceSite(): Saves the view site's (IOleInPlaceSite) interface pointer in the container.

Parameter

[in] IOleInPlaceSite *pIPSite

GetInPlaceSite(): Returns the view site's (IOleInPlaceSite) interface pointer in the container.

Parameter

[out] IOleInPlaceSite **ppIPSite

GetDocument(): Returns an (IUnknown) interface pointer to the DocObject.

Parameter

[out] IUnknown **ppunk

SetRect(): Sets the view's rectangle for display inside the container or client.

Parameter

[in] LPRECT prcView

GetRect(): Returns the view's rectangle for display inside the container or client. An error (E_UNEXPECTED) is returned if no view rectangle has been set yet.

Parameter

[out] LPRECT prcView

SetRectComplex(): Same as SetRect() with additional rectangles for vertical and horizontal scrollbars.

Parameters

[in] LPRECT prcView

[in] LPRECT prcHScroll

[in] LPRECT prcVScroll

[in] LPRECT prcSizeBox

Show(): Either "Show" or "Hide." This is the same idea as OLE's "Show" and "Hide" verbs. To "Show" means to in-place activate the view without a user-interface activation and to show, or display, the view window. To "Hide" means to deactivate the user interface and hide the view.

Parameter

[in] BOOL fShow

UIActivate(): Activate or deactivate the user interface. Activating involves the menu merging/sharing, setting up the toolbar and accelerators, and so forth.

Parameter

[in] BOOL fUIActivate

Open(): Same as OLE's "Open" verb. Invokes the native application as a separate process or in a pop-up window.

Parameter

void - no parameters

CloseView(): Close down the view by hiding the view and user interface by calling Show(FALSE) and releasing the view site's (IOleInPlaceSite) interface pointer in the container by calling SetInPlaceSite(NULL).

Parameter

[in] DWORD dwReserved

SaveViewState(): Save view-specific data to a stream for use in reinitializing the view at a later time by ApplyViewState(). According to the DocObject specification: "Instructs the view to save its state into the given stream, where the state includes properties like the view type, zoom factor, insertion point, and so on."

Parameter

[in] IStream *pstm

ApplyViewState(): Reinitialize the view from data in a stream previously saved by SaveViewState(). According to the DocObject specification: "Instructs a view to reinitialize itself according to the data in a stream that was previously written through IOleDocumentView::SaveViewState."

Parameter

[in] IStream *pstm

Clone(): Create and return another view object exactly like itself.

Parameters

[in] IOleInPlaceSite *pIPSiteNew

[out] IOleDocumentView **ppViewNew

- IOleObject: This is the main interface used by containers to communicate to all OLE objects. The only interface methods of interest to this discussion are SetClientSite() and DoVerb().

SetClientSite(): This interface method is called by the container to set up the view site interface pointer. If the view site passed in has the IOleDocumentSite interface, then the server is supposed to act as a DocObject (set a boolean flag) as opposed to an ordinary OLE server.

DoVerb(): For a DocObject Server (a boolean flag was set in the SetClientSite() method), this interface method is called by the container to allow your document to specify which view to use. This view would typically be the default or favorite view.

- IPersistFile: This interface is a standard OLE server interface. The Load() interface method is called by the client with the filename to use for initialization.

- IPersistStorage: This interface, also a standard OLE interface, is used for OLE embedding (as opposed to linking). The container calls the Save() interface method with a pointer to a structured storage (IStorage) to let the OLE server save - or embed - its data in the container's document. Likewise, the Load() interface method is later called to let the OLE server initialize itself from the embedded data.

OOP, MFC, and COM/OLE Methodology

To actually do some useful OOP with COM and OLE, you need to start with MFC and build up. "Building up" means using the OOP methodology of adding at least one layer of abstract classes. An abstract class is used as a parent to an implementation class but is rarely created directly. There is almost always a little extra application-specific functionality to add at your implementation layer to make it work properly. The implementation layer is the top layer of classes that are actually created, or in OOP terminology, instantiated. The implementation classes are just what they sound like - the implementation code that your application is directly responsible for implementing. The lower layers of abstract classes provide common functionality and buffer implementation classes from changes at lower layers.

What all of this amounts to on your end is deciding what common functionality you want to provide. If you are developing a suite of applications, then you should provide an additional layer of abstraction. As stated in the previous paragraph, your added abstract classes would provide any common functionality that is needed.

In the case of the Imagineer product, we used this methodology. In our toolkit, we have an additional layer of abstraction where we provide support for reading Imagineer's .IGR file format and provide a common display path. There are many other pieces of common functionality implemented at this highest - or last - layer of abstract classes. This has saved a considerable amount of recoding effort in our suite of applications based on Imagineer. So virtually all a new Imagineer-based application has to do is subclass and go, because most of the implementation specifics have been done in Imagineer's abstract classes. Figure 11.10 illustrates how layers of abstraction provide common functionality and tend to buffer applications from drastic changes.

***15VBA10.PCX***

Figure 11.10. Implementation layer on top of layers of abstraction. Adding functionality and buffered changes validate OOP as cost-effective.

The nice part of this OOP methodology is that any number of applications can be completed very cost-effectively. Simply put, inherit the common functionality provided by layers of abstract classes, and implement anything left over. These layers of abstraction make application building simple, with an added bonus of low maintenance costs. The class definitions of each abstract layer don't change much, if at all. However, the actual code developed for the abstract classes can change to react to changes at lower layers, so the abstract layers effectively buffer the actual applications from changes at those lower levels.

Imagineer Viewer Application

As a proof of concept, I built an Imagineer IGR Viewer application that simply displays Imagineer files. This application was written initially using the MFC AppWizard and specifying the ActiveX document server option. All I did to create a working application was change it to use the abstract classes provided by Imagineer. Imagineer's abstract classes are, in turn, subclassed from the MFC DocObject support classes. This buys DocObject support plus Imagineer file reading and display support for next to nothing.

To describe the process of creating this viewer application, we will step through creating a new application with the MFC AppWizard. Make certain that you select the ActiveX document server checkbox so that your application uses MFC's DocObject support classes. When you are done, copy this project to a save directory; you can look back at this later to see the minimal changes that had to be made.

The following steps will guide you through the AppWizard. (Make certain you write down the steps you take for future reference. Also save the ReadMe.txt file, which contains related useful information.):

Bring up Visual C++, choose File[hr]|[hr]New (or Ctrl+N), and select the Project Workspace option from the New dialog box. This will bring up the New Project Workspace dialog box. Select MFC AppWizard (exe), enter the name Ex, and click the Create button to start the MFC AppWizard.

2. Check either the "Multiple documents" (default) or "Single document" checkbox depending on which you prefer, then click the Next button. Click the Next button again on this database support step, taking the default of "none."

3. Check the "Both container and server" option, then select the "ActiveX document server" option checkbox. Make sure the "Yes, please" checkbox is selected under the "Would you like support for OLE compound files?" option checkbox; then click the Next button. Figure 11.11 shows how this dialog should look with everything checked off. Note in figure 11.11 I also have the "OLE automation" and the "OLE controls" option checkboxes selected, but these are not required.

Figure 11.11. MFC AppWizard step 3 or 6 dialog with the "ActiveX document server" option checkbox selected.
























4. Click the "Advanced" button to bring up the Advanced Options dialog box. On the Document Template Strings option tab in the "File extension" field, enter .DVW. Now click the Close button to close the Advanced Options dialog box, then click the Next button

5. Click the Next button to go to the next step, taking the defaults for source file comments and using the shared MFC library. If you want to change the default names of classes or source files, this dialog is the place to do it. When you are done changing names, click the Finish button to bring up the New Project Information dialog box. Finally, click the OK button to complete the Wizardry.

You can compile this application and run it stand-alone without a file, but you cannot open and display a file yet. Now you have to change the parent class specified in a few instances and add just a bit of code, and then you'll be able to open and display files. In addition, you'll be able to Visual Edit as a DocObject in the IE.

To change the class hierarchy so you can use the Imagineer classes, edit and change the parent classes as indicated in the following files:

The Imagineer toolkit provides a complete implementation of a "DocObject server item," subclassed from CDocObjectServerItem. The SrvrItem.cpp and SrvrItem.h files can be deleted from the project. Wherever the code refers to the AppWizard-created CExSrvrItem class, change to use the CIngrSrvrItem instead. The following code snippets show what the GetEmbeddedItem() and OnGetEmbeddedItem() methods looks like after this change:

In exdoc.h:

class CIngrSrvrItem; // forward declaration of class

class CExDoc : public CIngrDoc {

.
.
.

public:

CIngrSrvrItem* GetEmbeddedItem()

{return (CIngrSrvrItem*)COleServerDoc::GetEmbeddedItem();}

.
.
.

}

In exdoc.cpp:

COleServerItem* CExDoc::OnGetEmbeddedItem() {

CIngrSrvrItem * pItem = new CIngrSrvrItem (this);

}

First we'll discuss what support the MFC framework provides for OLE Document Objects. Then we'll take a peek at what Imagineer's abstraction layer does to complete a Document-View puzzle. This should give you the ability to analyze how to design an abstraction layer of your own.

The three MFC classes that provide most of what we need follow:

For DocObject activation, your AppWizard-generated implementation classes are subclassed from these three classes. The Imagineer toolkit classes provide much of the needed implementation that you normally would have to code yourself. In particular, the Imagineer classes provide code for reading/saving files, displaying files, and a platform for easily adding commands. There are four main Imagineer subclasses, which provide the following functionality:

Using this framework, the IGR Viewer can read and display Imagineer (.IGR) files with very little implementation code of its own. The IGR Viewer is included on the CD that accompanies this book, as well as a variety of Imagineer files (.IGR) and symbols (.SYM) included for you to test the IGR Viewer. In addition, because the IGR Viewer was built to support the ActiveX Document server interfaces, it can be activated in the IE. You will find some HTML files that allow you to perform the DocObject style of Visual Editing. Experience the DocObject-in-IE scenario for yourself and realize its usefulness.

Summary

You should now be able to take the ideas presented in these two abstract framework layers and design a framework layer of your own. In your own abstract framework, you must decide what functionality you need for reading, writing, and displaying your file format. While you are designing this, try to determine how these capabilities relate to initialization, reading, and displaying. The way the Imagineer framework was laid out should help you determine these relationships.

For instance, the initialization usually will go in the CWinApp subclass. Proprietary code for managing and reading your document's file format fits with a COleServerDoc subclass. Specific functionality needed in relation to activation and displaying as a DocObject, in a container such as the Internet Explorer, should be in a subclass of the CDocObjectServerItem class.

When you have an MFC-based application with ActiveX Document support, you must think about a work flow that you can use with your DocObject application. The work flow used by Intergraph's Imagineer CAD product fits right in with Microsoft's vision of an Active Desktop. Tighter integration through the Active Desktop's seamless view makes DocObject server products more useful and therefore more appealing. All DocObject-enabled applications will integrate as if they were one. This ease of use and tight integration provided by DocObject servers and containers is just what users are calling for these days. So get started now on a design using the methodology and work flow presented here. Your DocObject server application will join Microsoft Office, Intergraph Imagineer, and many other DocObject servers on the Active Desktop very soon.