Special Edition Using Visual C++ 5 - Chapter 15

## Chapter 15

## Building an ActiveX Server Application

Just as AppWizard builds ActiveX containers, it also builds ActiveX servers. However, unlike containers, the AppWizard code is complete, so there isn't much work to do for improving the AppWizard code. This chapter builds a version of ShowString that is only a server and discusses how to build another version that is both a container and a server. You also learn about ActiveX documents and how they can be used in other applications.

- Transforming ShowString into a Server

  An ActiveX server can supply its documents to ActiveX container applications like the one in the previous chapter. This section shows you how to build one and how they work.

- Container and Server Applications

  An application can be both a container and a server at the same time. This section discusses the implications of nested objects.

- ActiveX Documents

  ActiveX document objects take being an ActiveX server one step further. This section explains what an ActiveX document object is, how to make one, and gives you a peek at what Windows will become.

### Adding Server Capabilities to ShowString

Like Chapter 14, "Building an ActiveX Container Application," this chapter starts by building an ordinary server application with AppWizard and then adds the functionality that makes it ShowString. This is far quicker than adding ActiveX functionality to ShowString, because ShowString doesn't have much code and can be written quickly.

AppWizard's Server Boilerplate

Build the new ShowString in a different directory, making almost exactly the same AppWizard choices as when you built versions of ShowString in Chapter 9, "Building a Complete Application: ShowString," and Chapter 14, "Building an ActiveX Container Application." Call it ShowString, and choose an MDI application with no database support. In AppWizard's Step 3, select full server as your compound document support. This enables the check box for ActiveX document support. Leave this deselected for now. Later in this chapter you see the consequences of selecting this option. Continue the AppWizard process, selecting a docking toolbar, status bar, printing and print preview, context sensitive Help, and 3-D controls. Finally, select source file comments and a shared DLL.

> Even though the technology is now called ActiveX, the AppWizard dialog boxes refer to compound document support. Many of the class names that are used throughout this chapter have Ole in their names as well. While Microsoft has changed the name of the technology, it has not propagated that change throughout Visual C++ yet. You will have to live with these contradictions until the next release of Visual C++.

There are many differences between the application you have just generated and a do-nothing application without ActiveX server support. These differences are explained in the next few sections.

Menus

There are two new menus in an ActiveX server application. The first, called IDR_SHOWSTTYPE_SRVR_IP, is shown in Figure 15.1. When an item is being edited in place, the container in-place menu (called IDR_SHOWSTTYPE_CNTR_IP in the container version of ShoeString) is combined with the server in-place menu, IDR_SHOWSTTYPE_SRVR_IP, to build the in-place menu as shown in Figure 15.2. The double separators in each partial menu show where the menus are joined.

Fig. 15.1

AppWizard adds another menu for editing in place.

Fig. 15.2

The container and server in-place menus are interlaced during in place editing.

The second new menu is IDR_SHOWSTTYPE_SRVR_EMB, used when an embedded item is being edited in a separate window. Figure 15.3 shows this new menu next to the more familiar IDR_SHOWSTTYPE menu, which is used when ShowString is acting not as a server but as an ordinary application. The File menus have different items: IDR_SHOWSTTYPE_SRVR_EMB has Update in place of Save, and Save Copy As in place of Save As. This is because the item the user is working on in the separate window is not a document of its own, but is embedded in another document. File, Update updates the embedded item; File, Save Copy As does not save the whole document, just a copy of this embedded portion.

Fig. 15.3

The embedded menu has different items under File than the usual menu.

CShowStringApp

Another member variable has been added to this class. It is declared in showstring.h as:

    COleTemplateServer m_server;

COleTemplateServer handles the majority of the work involved in connecting documents to code, as you will see.

The following line is added at the top of showstring.cpp:

    #include "IpFrame.h"

This sets up the class CInPlaceFrame, discussed later in this chapter. Just before InitInstance(), the lines shown in Listing 15.1 are added:

Listing 15.1—excerpt from ShowString.cpp—CLSID

    // This identifier was generated to be statistically unique for

    // your app. You may change it if you prefer to choose a specific

    // identifier.

    // {0B1DEE40-C373-11CF-870C-00201801DDD6}

    static const CLSID clsid =

    { 0xb1dee40, 0xc373, 0x11cf,

         { 0x87, 0xc, 0x0, 0x20, 0x18, 0x1, 0xdd, 0xd6 } };

The numbers will be different in your code. This Class ID identifies your server application and document type. Applications that support several kinds of documents (for example, text and graphics) use a different CLSID for each type of document.

As it did for the OLE container version of ShowString, CShowStringApp::InitInstance() has several changes from the non-ActiveX ShowString you developed in Chapter 9, “Building a Complete Application: ShowString.” The code in Listing 15.2 initializes the ActiveX (OLE) libraries:

Listing 15.2—excerpt from ShowString.cpp—Initializing Libraries

         // Initialize OLE libraries

         if (!AfxOleInit())

         {

              AfxMessageBox(IDP_OLE_INIT_FAILED);

              return FALSE;

         }

While still in CShowStringApp::InitInstance(), after the MultiDocTemplate is initialized, but before the call to AddDocTemplate(), the following line is added to register the menu used for in-place editing and for separate-window editing:

    pDocTemplate->SetServerInfo(

              IDR_SHOWSTTYPE_SRVR_EMB, IDR_SHOWSTTYPE_SRVR_IP,

              RUNTIME_CLASS(CInPlaceFrame));

A change that was not in the container version is connecting the template for the document to the class ID, like this:

    // Connect the COleTemplateServer to the document template.

         //  The COleTemplateServer creates new documents on behalf

         //  of requesting OLE containers by using information

         //  specified in the document template.

         m_server.ConnectTemplate(clsid, pDocTemplate, FALSE);

Now when a user chooses Create New when inserting an object, the document used for that creation will be available.

When a server application is launched to edit an item in place or in a separate window, the system DLLs add /Embedded to the invoking command line. But if the application is already running, and it is an MDI application, a new copy is not launched. Instead, a new MDI window is opened in that application. That particular piece of magic is accomplished with one function call, as shown in Listing 15.3.

Listing 15.3—excerpt from ShowString.cpp—Registering running MDI apps

         // Register all OLE server factories as running.  This enables the

         //  OLE libraries to create objects from other applications.

         COleTemplateServer::RegisterAll();

         // Note: MDI applications register all server objects without regard

         //  to the /Embedding or /Automation on the command line.

After parsing the command line, the AppWizard boilerplate code checks to see if this application is being launched as an embedded (or automation) application. If so, there is no need to continue with the initialization, so this function returns, as shown in Listing 15.4.

Listing 15.4—excerpt from ShowString.cpp—checking how the app was launched

         // Check to see if launched as OLE server

         if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)

         {

              // Application was run with /Embedding or /Automation.

              // Don't show the main window in this case.

              return TRUE;

         }

If the application is being run stand-alone, execution continues with a registration update:

    // When a server application is launched stand-alone, it is a good idea

         //  to update the system registry in case it has been damaged.

         m_server.UpdateRegistry(OAT_INPLACE_SERVER);

ActiveX information is stored in the Registry. (The Registry is discussed in Chapter 8, “Persistence and File I/O.”) When a user chooses Insert, Object or Edit, Insert Object, the Registry provides the list of object types that can be inserted. So, before ShowString can appear in such a list, it must be registered. Many developers add code to their install programs to register their server applications, and MFC takes this one step further, registering the application every time it is run. If the application files are moved or changed, the registration is automatically updated the next time the application is run stand-alone.

CShowStringDoc

The document class, CShowStringDoc, now inherits from COleServerDoc rather than CDocument. As well, the following line is added at the top of showstringdoc.cpp:

    #include "SrvrItem.h"

This header file describes the server item class, CShowStringSrvrItem, discussed in the CShowStringSrvrItem subsection of this section. The constructor, CShowStringDoc::CShowStringDoc(), has the following line added:

         EnableCompoundFile();

This turns on the use of compound files.

There is a new public function, inlined in the header file, so that other functions can access the server item:

    CShowStringSrvrItem* GetEmbeddedItem()

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

This calls the base class GetEmbeddedItem(), which in turn calls the virtual function OnGetEmbedded Item(). That function must be overridden in the ShowString document class as shown in Listing 15.5

Listing 15.5— ShowStringDoc.cpp— CShowStringDoc::OnGetEmbeddedItem()

    COleServerItem* CShowStringDoc::OnGetEmbeddedItem()

    {

         // OnGetEmbeddedItem is called by the framework to get the COleServerItem

         //  that is associated with the document.  It is only called when necessary.

         CShowStringSrvrItem* pItem = new CShowStringSrvrItem(this);

         ASSERT_VALID(pItem);

         return pItem;

    }

This makes a new server item from this document and returns a pointer to it.

CShowStringView

The view class has a new entry in the message map:

         ON_COMMAND(ID_CANCEL_EDIT_SRVR, OnCancelEditSrvr)

This catches ID_CANCEL_EDIT_SRVR, and the cancellation of editing is in place. An accelerator has already been added to connect this message to Esc. The function that catches it looks like this:

    void CShowStringView::OnCancelEditSrvr()

    {

         GetDocument()->OnDeactivateUI(FALSE);

    }

This function simply deactivates the item. There are no other view changes—server views are so much simpler than container views.

CShowStringSrvrItem

The server item class is a completely new addition to ShowString. It provides an interface between the container application that causes ShowString to launch and a ShowString document. It describes an entire ShowString document that is embedded into another document, or a portion of a ShowString document that is linked to part of a container document. It has no member variables other than those inherited from the base class, COleServerItem. It has overrides for eight functions. They are as follows:

- A constructor
- A destructor
- GetDocument()
- AssertValid()
- Dump()
- Serialize()
- OnDraw()
- OnGetExtent()

The constructor simply passes the document pointer along to the base class. The destructor does nothing. GetDocument() is an inline function that calls the base class function with the same name and casts the result. AssertValid() and Dump() are debug functions that simply call the base class functions. Serialize() actually does some work, as shown in Listing 15.6.

Listing 15.6— SrvrItem.cpp— CShowStringSrvrItem::Serialize()

    void CShowStringSrvrItem::Serialize(CArchive& ar)

    {

         // CShowStringSrvrItem::Serialize will be called by the framework if

         //  the item is copied to the clipboard.  This can happen automatically

         //  through the OLE callback OnGetClipboardData.  A good default for

         //  the embedded item is simply to delegate to the document's Serialize

         //  function.  If you support links, then you will want to serialize

         //  just a portion of the document.

         if (!IsLinkedItem())

         {

              CShowStringDoc* pDoc = GetDocument();

              ASSERT_VALID(pDoc);

              pDoc->Serialize(ar);

         }

There is no need to duplicate effort here. If the item is embedded, then it is an entire document, and that document has a perfectly good Serialize() that can handle the work. AppWizard doesn't provide boilerplate to handle serializing a linked item, because it is application specific. You would save just enough information to describe what part of the document has been linked in, for example, cells A3 to D27 in a spreadsheet. This doesn't make sense for ShowString, so don't add any code to Serialize().

You may feel that OnDraw() is out of place here. It is normally thought of as a view function. But this OnDraw() draws a depiction of the server item when it is inactive. It should look very much like the view when it is active, and it makes sense to share the work between CShowStringView::OnDraw() and CShowStringSrvrItem::OnDraw(). The boilerplate that AppWizard provides is in Listing 15.7.

Listing 15.7— SrvrItem.cpp—CShowStringSrvrItem::OnDraw()

    BOOL CShowStringSrvrItem::OnDraw(CDC* pDC, CSize& rSize)

    {

         CShowStringDoc* pDoc = GetDocument();

         ASSERT_VALID(pDoc);

         // TODO: set mapping mode and extent

         //  (The extent is usually the same as the size returned from OnGetExtent)

         pDC->SetMapMode(MM_ANISOTROPIC);

         pDC->SetWindowOrg(0,0);

         pDC->SetWindowExt(3000, 3000);

         // TODO: add drawing code here.  Optionally, fill in the HIMETRIC extent.

         //  All drawing takes place in the metafile device context (pDC).

         return TRUE;

This will change a great deal, but it's worth noting now that unlike CShowStringView::OnDraw(), this function takes two parameters. The second is the size in which the inactive depiction is to be drawn. The extent, as mentioned in the boilerplate comments, typically comes from OnGetExtent(), which is shown in Listing 15.8.

Listing 15.8— SrvrItem.cpp— CShowStringSrvrItem:: OnGetExtent()

    BOOL CShowStringSrvrItem::OnGetExtent(DVASPECT dwDrawAspect, CSize& rSize)

    {

         // Most applications, like this one, only handle drawing the content

         //  aspect of the item.  If you wish to support other aspects, such

         //  as DVASPECT_THUMBNAIL (by overriding OnDrawEx), then this

         //  implementation of OnGetExtent should be modified to handle the

         //  additional aspect(s).

         if (dwDrawAspect != DVASPECT_CONTENT)

              return COleServerItem::OnGetExtent(dwDrawAspect, rSize);

         // CShowStringSrvrItem::OnGetExtent is called to get the extent in

         //  HIMETRIC units of the entire item.  The default implementation

         //  here simply returns a hard-coded number of units.

         CShowStringDoc* pDoc = GetDocument();

         ASSERT_VALID(pDoc);

         // TODO: replace this arbitrary size

         rSize = CSize(3000, 3000);   // 3000 x 3000 HIMETRIC units

         return TRUE;

    }

You will replace this with real code very shortly.

CInPlaceFrame

The in-place frame class, which inherits from COleIPFrameWnd, handles the frame around the server item and the toolbars, status bars, and dialog-box bars, collectively known as control bars, that it displays. It has the following three protected member variables:

         CToolBar    m_wndToolBar;

         COleResizeBar   m_wndResizeBar;

         COleDropTarget m_dropTarget;

The CToolBar class is discussed in Chapter 10, "Status Bars and Toolbars." COleDropTarget is discussed in the drag and drop section of Chapter 14, "Building an ActiveX Container Application." COleResizeBar looks just like a CRectTracker, which was used extensively in Chapter 14, but allows the resizing of a server item rather than a container item.

The following are the seven member functions of CInPlaceFrame:

- A constructor
- A destructor
- AssertValid()
- Dump()
- OnCreate()
- OnCreateControlBars()
- PreCreateWindow()

The constructor and destructor do nothing. AssertValid() and Dump() are debug functions that simply call the base class functions. OnCreate() actually has code, shown in Listing 15.9.

Listing 15.9—IPFrame.cpp—CInPlaceFrame::OnCreate()

    int CInPlaceFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

    {

         if (COleIPFrameWnd::OnCreate(lpCreateStruct) == -1)

              return -1;

         // CResizeBar implements in-place resizing.

         if (!m_wndResizeBar.Create(this))

         {

              TRACE0("Failed to create resize bar\n");

              return -1;      // fail to create

         }

         // By default, it is a good idea to register a drop-target that does

         //  nothing with your frame window.  This prevents drops from

         //  "falling through" to a container that supports drag-drop.

         m_dropTarget.Register(this);

         return 0;

    }

This function catches the WM_CREATE message that is sent when an in-place frame is created and drawn on the screen. It calls the base class function, then creates the resize bar. Finally, it registers a drop target, so that if anything is dropped over this in-place frame, it is dropped on this server rather than the underlying container.

When a server document is activated in place, COleServerDoc::ActivateInPlace() calls CInPlaceFrame::OnCreateControlBars(), which is shown in Listing 15.10.

Listing 15.10—IPFrame.cpp—CInPlaceFrame::OnCreateControlBars()

    BOOL CInPlaceFrame::OnCreateControlBars(CFrameWnd* pWndFrame, CFrameWnd* pWndDoc)

    {

         // Set owner to this window, so messages are delivered to correct app

         m_wndToolBar.SetOwner(this);

         // Create toolbar on client's frame window

         if (!m_wndToolBar.Create(pWndFrame) ||

              !m_wndToolBar.LoadToolBar(IDR_SHOWSTTYPE_SRVR_IP))

         {

              TRACE0("Failed to create toolbar\n");

              return FALSE;

         }

         // TODO: Remove this if you don't want tool tips or a resizeable toolbar

         m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |

              CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

         // TODO: Delete these three lines if you don't want the toolbar to

         //  be dockable

         m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);

         pWndFrame->EnableDocking(CBRS_ALIGN_ANY);

         pWndFrame->DockControlBar(&m_wndToolBar);

         return TRUE;

    }

This function creates a docking, resizable toolbar with tool tips, docked against the edge of the main frame window for the application.

> If you are developing an MDI application and prefer the toolbar against the document frame, use pWndDoc instead of PWndFrame, in the call to m_wndToolBar.Create() but be sure to check that it is not NULL.

The last function in CInPlaceFrame is PreCreateWindow(). At the moment, it just calls the base class, as shown in Listing 15.11.

Listing 15.11—IPFrame.cpp—CInPlaceFrame::PreCreateWindow()

    BOOL CInPlaceFrame::PreCreateWindow(CREATESTRUCT& cs)

    {

         // TODO: Modify the Window class or styles here by modifying

         //  the CREATESTRUCT cs

         return COleIPFrameWnd::PreCreateWindow(cs);

    }

This function is called before OnCreate() and sets up the styles for the frame window through a CREATESTRUCT.

> Modifying these styles is not for the faint of heart. The Microsoft documentation recommends reading the source code for all the classes in the hierarchy of your CInPlaceFrame (Cwnd, CFrameWnd, COleIPFrameWnd) to see what CREATESTRUCT elements are already set before making any changes. For this sample application, don't change the CREATESTRUCT.

Shortcomings of This Server

Apart from the fact that the starter application from AppWizard doesn't show a string, what's missing from this server? The OnDraw() and GetExtent() TODOs are the only significant tasks left for you by AppWizard. Try building ShowString, then run it once stand-alone just to register it.

Figure 15.4 shows the Object dialog box in Microsoft Word, reached by choosing Insert, Object. ShowString appears in this list as ShowSt Document—not surprising considering the menu name was IDR_SHOWSTTYPE. Developer Studio calls this document a ShowSt document. This setting could have been overriden in AppWizard by choosing the Advanced button in Step 4 of AppWizard. Figure 15.5 shows this dialog box and the long and short names of the file type.

Fig. 15.4

The ShowString document type, called ShowSt document, now appears in the Insert Object dialog box when inserting a new object into a Word document.

Fig. 15.5

The Advanced options dialog box of Step 4 in AppWizard provides an opportunity to change the name of the file type.

So, the file type names used by the Registry have been set incorrectly for this project. The next few pages take you on a tour of the way file type names are stored and show you how difficult they are to change.

The file type name has been stored in the string table. It is the caption of the IDR_SHOWSTTYPE resource, and AppWizard has set it to:

    \nShowSt\nShowSt\n\n\nShowString.Document\nShowSt Document

To look at this string, choose String Table from the Resource View, open the only string table there, click IDR_SHOWSTTYPE once to highlight it, and choose View, Properties. This string is saved in the document template when a new one is constructed in CShowStringApp::InitInstance(), like this:

Listing 15.12— ShowString.cpp— excerpt from ShowStringApp::InitInstance()

         pDocTemplate = new CMultiDocTemplate(

              IDR_SHOWSTTYPE,

              RUNTIME_CLASS(CShowStringDoc),

              RUNTIME_CLASS(CChildFrame), // custom MDI child frame

              RUNTIME_CLASS(CShowStringView));

The caption of the menu resource holds seven strings, and each is used by a different part of the framework. They are separated by the newline character, \n. The seven strings, their purposes, and the values provided by AppWizard for ShowString are as follows:

- Window Title.Used by SDI apps in the title bar. For ShowString: not provided.
- Document Name.Used as the root for default document names. For ShowString: ShowSt, so that new documents will be ShowSt1, ShowSt2, and so on.
- File New Name.Prompt in the File New dialog box for file type. (For example, in Developer Studio there are eight file types, including Text File and Project Workspace.) For ShowString: ShowSt.
- Filter Name.An entry for the drop-down box List File of Type in the File Open dialog box. For ShowString: not provided.
- Filter Extension.The extension that matches the filter name. For ShowString: not provided.
- Registry File Type ID.A short string to be stored in the Registry. For ShowString: ShowString.Document.
- Registry File Type Name.A longer string that shows in dialog boxes involving the Registry. For Showstring: ShowSt Document.

(Look again at Figure 15.5 and you can see where these values came from.) Try changing the last entry. In the Properties dialog box, change the caption so that the last element of the string is ShowString Document. Build the project. Run it once and exit. In the output section of Developer Studio you see these messages:

    Warning: Leaving value 'ShowSt Document' for key 'ShowString.Document'

     in registry

     intended value was 'ShowString Document'.

    Warning: Leaving value 'ShowSt Document' for key

     'CLSID\{0B1DEE40-C373-11CF-870C-00201801DDD6}' in registry

     intended value was 'ShowString Document'.

This means that the call to UpdateRegistry() did not change these two keys. There is a way to provide parameters to UpdateRegistry() to insist that the keys be updated, but it's even more complicated than the route you will follow. Because no code has been changed from that provided by AppWizard, it's much quicker just to delete the ShowString directory and create it again, this time setting the long file type to ShowString Document.

> Always test AppWizard-generated code before you add changes of your own. Until you are familiar with every default you are accepting, it is worth a few moments to see what you have before moving on. Rerunning AppWizard is easy, but if you've made several hours worth of changes and then decide to rerun it, it's not such a simple thing.

Delete the ShowString folder entirely and generate a new application with AppWizard as before. This time, in Step 4, click the Advanced button and change the file type names as shown in Figure 15.6. After you click Finish, AppWizard asks whether you wish to reuse the existing CLSID, as shown in Figure 15.7. Click Yes and then OK to create the project. This makes a new showstring.reg file for you with the correct Registry values.

Fig. 15.6

The Advanced options dialog box of Step 4 of AppWizard is the place to improve the file type names.

Fig. 15.7

AppWizard makes sure that you don't accidentally reuse a CLSID.

This changes the string table as well as the showstring.reg file, so you might be tempted to build and run the application to make this fix complete. And it's true, when you run the application, it will update the Registry for you, using the values from the new string table. Alas, the registration update will fail yet again. If you were to try it, these messages would appear in the output window:

    Warning: Leaving value 'ShowSt Document' for key

     'ShowString.Document' in registry

     intended value was 'ShowString Document'.

    Warning: Leaving value 'ShowSt Document' for key

     'CLSID\{0B1DEE40-C373-11CF-870C-00201801DDD6}' in registry

     intended value was 'ShowString Document'.

    Warning: Leaving value 'ShowSt' for key

     'CLSID\{0B1DEE40-C373-11CF-870C-00201801DDD6}\AuxUserType\2'

     in registry

     intended value was 'ShowString'.

So, how do you get out of this mess? You have to edit the Registry. If that sounds intimidating, it should be. Messing with the Registry can leave your system unusable. But you are not going to go in by hand and change keys; instead, you are going to use the registry file that AppWizard generated for you. Here's what to do:

1. Choose Start, Run

2. Type regedit and press Enter.

3. Choose Registry, Import Registry File from the Registry Editor menu.

4. Using the Import Registry File dialog box, move through your folders until you reach the one where the replacement ShowString server was just generated by AppWizard, as shown in Figure 15.8. Click Open.

5. A success message is shown. Click OK.

6. Close the Registry Editor.

Fig. 15.8

Registry files generated by AppWizard have the extension .reg.

Now if you run ShowString again, those error messages do not appear. Run Word again, and choose Insert Object. The Object dialog box now has a more meaningful ShowString entry, as shown in Figure 15.9.

Fig. 15.9

The updated long file type name appears in the Insert Object dialog box of other applications.

> There are three morals to this side trip. The first is that you should think really carefully before clicking Finish on the AppWizard dialog box. The second is that you cannot ignore the Registry if you are an ActiveX programmer. The third is that anything can be changed if you have the nerve for it.

Click OK on the Object dialog box to insert a ShowString object into the Word document. You can immediately edit it in place, as shown in Figure 15.10. You can see that the combined server and container in-place menus are being used. There's not much you can do to the embedded object at this point, because the ShowString code that actually shows a string has not been added. Press Esc to finish editing in place and the menus return to the usual Word menus, as shown in Figure 15.11.

Fig. 15.10

While editing in place, the in-place menus replace the Word menus.

Fig. 15.11

When the object is inactive, Word reminds the user of the object type.

Although this server doesn't do anything, it is a perfectly good server. You can resize and move the embedded item while it is active or inactive, and everything operates exactly as you expect. All that remains is to restore the ShowString functionality.

Showing a String Again

As you did in Chapter 14, "Building an ActiveX Container," it is time to add the ShowString functionality to this version of the program. If you went through this process before, it will be even quicker this time. Remember to open the ShowString files from Chapter 9, "Building a Complete Application: ShowString," so that you can copy code and resources from the functional ShowString to the do-nothing ActiveX server you have just created and explored. Here's what to do:

1. In ShowStringDoc.h, add the private member variables and public Get functions to the class.

2. In CShowStringDoc::Serialize(), paste in the code that saves or restores these member variables.

3. In CShowStringDoc::OnNewDocument(), paste in the code that initializes the member variables.

4. Copy the entire Tools menu from the old ShowString to the new server ShowString: choose File, Open to open the old ShowString.rc, open the IDR_SHOWSTTYPE menu, click the Tools menu, and choose Edit, Copy. Open the new ShowString's IDR_SHOWSTTYPE menu, click the Window menu, and choose Edit, Paste.

5. Paste the Tools menu into the IDR_SHOWSTTYPE_SRVR_IP (before the separator bars) and IDR_SHOWSTTYPE_SRVR_EMB menus in the same way.

6. Add the accelerator Ctrl+T for ID_TOOLS_OPTIONS as described in Chapter 9, "Building a Complete Application: ShowString." Add it to all three accelerators.

7. Delete the IDD_ABOUTBOX dialog box from the new ShowString. Copy IDD_ABOUTBOX and IDD_OPTIONS from the old ShowString to the new.

8. While IDD_OPTIONS has focus, choose View, ClassWizard. Create the COptionsDialog class as in the original ShowString.

9. Use ClassWizard to arrange for CShowStringDoc to catch the ID_TOOLS_OPTIONS command.

10. In ShowStringDoc.cpp, replace the ClassWizard version of CShowStringDoc::OnToolsOptions() with the one that puts up the dialog box.

11. In ShowStringDoc.cpp, add #include "OptionsDialog.h" after the #include statements already present.

12. Use ClassWizard to connect the dialog box controls to COptionsDialog member variables as before.

You haven't restored CShowStringView::OnDraw() yet, because there are actually going to be two OnDraw() functions. The first is in the view class, shown in Listing 15.13. It draws the string when ShowString is running stand-alone and when the user is editing in place, and it's the same as in the old version of ShowString. Just copy it into the new one.

Listing 15.13—ShowStringView.cpp—CShowStringView::OnDraw()

    void CShowStringView::OnDraw(CDC* pDC)

    {

         CShowStringDoc* pDoc = GetDocument();

         ASSERT_VALID(pDoc);

         COLORREF oldcolor;

         switch (pDoc->GetColor())

         {

         case 0:

              oldcolor = pDC->SetTextColor(RGB(0,0,0)); //black

              break;

         case 1:

              oldcolor = pDC->SetTextColor(RGB(0xFF,0,0)); //red

              break;

         case 2:

              oldcolor = pDC->SetTextColor(RGB(0,0xFF,0)); //green

              break;

         }

         int DTflags = 0;

         if (pDoc->GetHorizcenter())

         {

              DTflags |= DT_CENTER;

         }

         if (pDoc->GetVertcenter())

         {

              DTflags |= (DT_VCENTER|DT_SINGLELINE);

         }

         CRect rect;

         GetClientRect(&rect);

         pDC->DrawText(pDoc->GetString(), &rect, DTflags);

         pDC->SetTextColor(oldcolor);

    }

When the embedded ShowString item is inactive, CShowStringSrvrItem::OnDraw() draws it. The code in here should be very like the view's OnDraw, but because it is a member of CShowStringSrvrItem rather than CShowStringView, it doesn't have access to the same member variables. So although there is still a GetDocument() function you can call, GetClientRect doesn't work. It's a member of the view class but not of the server item class. You use a few CDC member functions instead. It's a nice touch to draw the item slightly differently, to help remind the user that it is not active, as shown in Listing 15.14.

Listing 15.14—SrvrItem.cpp—CShowStringSrvrItem::OnDraw()

    BOOL CShowStringSrvrItem::OnDraw(CDC* pDC, CSize& rSize)

    {

         CShowStringDoc* pDoc = GetDocument();

         ASSERT_VALID(pDoc);

         // TODO: set mapping mode and extent

         //  (The extent is usually the same as the size returned from OnGetExtent)

         pDC->SetMapMode(MM_ANISOTROPIC);

         pDC->SetWindowOrg(0,0);

         pDC->SetWindowExt(3000, 3000);

         COLORREF oldcolor;

         switch (pDoc->GetColor())

         {

         case 0:

              oldcolor = pDC->SetTextColor(RGB(0x80,0x80,0x80)); //gray

              break;

         case 1:

              oldcolor = pDC->SetTextColor(RGB(0xB0,0,0)); // dull red

              break;

         case 2:

              oldcolor = pDC->SetTextColor(RGB(0,0xB0,0)); // dull green

              break;

         }

         int DTflags = 0;

         if (pDoc->GetHorizcenter())

         {

              DTflags |= DT_CENTER;

         }

         if (pDoc->GetVertcenter())

         {

              DTflags |= (DT_VCENTER|DT_SINGLELINE);

         }

         CRect rect;

         rect.TopLeft() = pDC->GetWindowOrg();

         rect.BottomRight() = rect.TopLeft() + pDC->GetWindowExt();

         pDC->DrawText(pDoc->GetString(), &rect, DTflags);

         pDC->SetTextColor(oldcolor);

         return TRUE;

    }

The function starts with the boilerplate from AppWizard. With an application that doesn't just draw itself in whatever space is provided, you would want to add code to determine the extent rather than just using (3000,3000). (You'd want to add the code to OnGetExtent(), too.) But hardcoding the numbers works for this simple example. Next, paste in the drawing code from the view's OnDraw(), but change the colors slightly to give the user a reminder.

Build the application, fix any typos or other simple errors, and then start Excel and insert a ShowString document into your worksheet. ShowString should run as before, with Hello, world! in the center of the view. Convince yourself that the Options dialog box still works and that you have restored all the old functionality. Be sure to change at least one thing: the string, the color, or the centering. Then, press Esc to finish editing in place. Oops! It still draws the old Hello, world! in gray in the center of the server area. Why?

Remember that in CShowStringDoc::OnToolsOptions(), after the user clicks OK, you tell the document that it has been changed and arrange to have the view redrawn:

         SetModifiedFlag();

         UpdateAllViews(NULL);

You need to add another line there to make sure that any containers that are containing this document are also notified:

         NotifyChanged();

Now, build it again, and insert a different ShowString object into a Word document. This time the changes are reflected in the inactive server display as well. Figure 15.12 shows a ShowString item being edited in place, and Figure 15.13 shows the same item inactive.

Fig. 15.12

This ShowString item is being edited in place.

Fig. 15.13

This ShowString item is inactive.

> There is one oddity you may notice as you edit. If you choose to have the string centered horizontally when it is inactive, the first character of the string is centered, but when it is active the entire string is centered. Because the code is identical for these cases, this behavior has to be blamed on MFC.

Good old ShowString has been through a lot. It's time for one more transformation.

### Applications that Are both Container and Server

As you might expect, adding container features to this version of ShowString is as difficult as adding them to the ordinary ShowString of the previous chapter. If you add these features, you gain an application that can tap the full power of ActiveX to bring extraordinary power to your work and your documents.

Building Another Version of ShowString

The way to get a ShowString that is both a container and a server is to follow these steps:

1. Build a new ShowString with AppWizard that is a container and a full server. Run AppWizard as usual, but in a different directory than the one where you created the server-only ShowString. Be sure to select the Both Container And Server radio button in Step 3. In Step 4, be sure to click the Advanced button and change the file name types as you did earlier in this chapter. And finally, when asked whether you want to use the same CLSID, say No. This is a different application.

2. Make the container changes from the previous chapter. When adding the Tools Options menu item and accelerator, add it to the main menu, the server in-place menu, and the server-embedded menu.

3. Make the server changes from this chapter.

4. Add the ShowString functionality.

This section does not present the process of building a container and server application in detail; that is covered in the "Adding Server Capabilities to ShowString" section of this chapter and all of the previous chapter. Rather, the focus here is on the consequences of building such an application.

Nesting and Recursion Issues

After an application is both a server (meaning its documents can be embedded in other applications) and a container, it is possible to create nested documents. For example, an Excel spreadsheet might contain a Word document, which, in turn, contains a bitmap, as shown in Figure 15.14.

Fig. 15.14

This Excel spreadsheet contains a Word document that contains a bitmap.

Within Excel, you can double-click the Word document to edit it in place, as shown in Figure 15.15, but you cannot go on to double-click the bitmap and edit it in place, too. You can edit it in a window of its own, as shown in Figure 15.16. It is a limitation of ActiveX that you cannot nest in-place editing sessions indefinitely.

Fig. 15.15

This Word document is being edited in place.

Fig. 15.16

This bitmap is nested within a Word document within an Excel spreadsheet, and so cannot be edited in place. Instead, it is edited in a separate window.

### ActiveX Documents

The final, important recent addition to ActiveX is ActiveX documents, also known as ActiveX Document Objects. An ordinary ActiveX server takes over the menus and interface of a container application when the document is being edited in place, but does so in cooperation with the container application. An ActiveX Document server takes over far more dramatically.

What ActiveX Documents Do

The first application to demonstrate the use of ActiveX Documents is the Microsoft Office Binder, shown in Figure 15.17. To the user, it appears that this application can open any Office document. In reality, the documents are opened with their own server applications while the frame around them and the list of other documents remains intact. Microsoft Internet Explorer 3.0 is also an ActiveX Document container—Figure 15.18 shows a Word document open in Explorer. Notice the menus are Word menus, but the Explorer toolbar can still be used. For example, clicking the Back button closes this Word document and opens the document that was loaded previously.

Fig. 15.17

The Microsoft Office Binder makes it simple to pull Office documents together.

Fig. 15.18

Microsoft Internet Explorer is also a container for ActiveX documents.

What this means to users is a complete transition to a document-centered approach. No matter what application the user is working with, any kind of document can be opened and edited, using the code written to work with that document but the interface that the user has learned for his or her own application.

Making ShowString an ActiveX Document Server

Making yet another version of ShowString, this one as an ActiveX document server, is pretty simple. Follow the instructions from the "AppWizard's Server Boilerplate" subsection at the beginning of this chapter, with two exceptions: in AppWizard's Step 3, select ActiveX document support, and in AppWizard's Step 4, click the Advanced button. Fix the file type names, and fill in the file extension as .sst, as shown in Figure 15.19. This helps ActiveX document containers determine what application to launch when you open a ShowString file.

Fig. 15.19

The Advanced Options dialog box of App Wizard's Step 4 is where you specify the extension for ShowString files.

Document Extension Boilerplate

Any one of the versions of ShowString built up to this point could have had a document extension specified. AppWizard adds these lines to CShowStringApp::InitInstance() when you specify a document extension for an ActiveX document server application:

         // Enable drag/drop open

         m_pMainWnd->DragAcceptFiles();

         // Enable DDE Execute open

         EnableShellOpen();

         RegisterShellFileTypes(TRUE);

It is the call to RegisterShellFileTypes that matters here, though the drag and drop is a nice touch. You're able to drag files from your desktop or a folder onto the ShowString icon or an open copy of ShowString, and the file opens in ShowString.

ActiveX Document Server Boilerplate

Selecting ActiveX document support makes remarkably little difference to the code generated by AppWizard. In CShowStringApp::InitInstance(),the versions of ShowString that were not ActiveX document servers had this call to update the Registry:

         m_server.UpdateRegistry(OAT_INPLACE_SERVER);

The ActiveX document version of Showstring has this line:

         m_server.UpdateRegistry(OAT_DOC_OBJECT_SERVER);

In both cases, m_server is a CShowStringSrvrItem, but now the ActiveX document server version has a server item that inherits from CDocObjectServerItem. This causes a number of little changes throughout the source and includes files for CShowStringSrvrItem, where base class functions are called. Similarly, the in-place frame object, CInPlaceFrame, now inherits from COleDocIPFrameWnd.

Showing Off the Newest ShowString

Restore the ShowString functionality once again as described in the section "Showing a String Again," earlier in this chapter. Build the application, run it once to register it, and then run the Microsoft Binder (if you have Office installed). Choose Section Add to bring up the Add Section dialog box shown in Figure 15.20. Highlight ShowString Document and click OK.

Fig. 15.20

Not many applications on the market are ActiveX document servers, but you can write one in minutes.

The menus include ShowString's Tools menu, as before. Choose Tools Options and change something—for example, in Figure 15.21, the string has been changed to "Hello from the Binder" and the vertical centering has been turned off. You have access to all of ShowString's functionality, although it doesn't look as though you are running ShowString.

Fig. 15.21

All of ShowString's functionality is available from within the Binder.

Now run ShowString alone and save a document by choosing File, Save. You do not need to enter an extension: the extension .sst is used automatically. Open Internet Explorer 3.0 and choose File, Open. On the Open dialog box, shown in Figure 15.22, click Browse and explore until you reach the file you saved, then click Open.

Fig. 15.22

The Internet Explorer Open dialog box is used to open files on your hard drive or the Internet.

Your ShowString document opens in Explorer, as you can see in Figure 15.23. The toolbar is clearly the Explorer toolbar, but the menu has the Tools item, and you can change the string, centering, and color as before. If you use the Back button on the Explorer toolbar, you reload the previous document you had open. If you change the ShowString document before clicking Back, you'll even be prompted to save your changes! Microsoft plans to integrate the desktop in the next generation of Windows with the Internet Explorer interface. What you see here is a sneak preview of how that will work.

Fig. 15.23

Internet Explorer appears to be able to read and write ShowString files now.

### From Here...

This chapter built a third version of ShowString that can act as an ActiveX server. The AppWizard boilerplate did not need to be modified much, and now you can embed a ShowString document in any ActiveX container. You also saw how to construct a ShowString that is both a server and a container. Your glimpse into the future of Windows came with the ActiveX Document Objects, the Microsoft Office Binder, and the idea of opening a Word document in another application like Microsoft Internet Explorer. Eventually Windows will look very much like Internet Explorer, and it's ActiveX Document Objects that will make that possible.

To explore some related material, try these chapters:

- Chapter 9, "Building a Complete Application: ShowString," introduced the ShowString application.
- Chapter 13, "ActiveX Concepts," is a roadmap to Part IV of this book and defines many of the concepts used in this and related chapters.
- Chapter 14, "Building an ActiveX Container Application," builds the second version of ShowString, which acts as an ActiveX container.
- Chapter 16, "Building an Automation Server”," builds a fourth version of ShowString; this time it's an ActiveX Automation server that can be controlled from Visual Basic.
- Chapter 17, "Building an ActiveX Control," leaves ShowString behind and builds a control you can include in any Visual C++ or Visual Basic program.

© 1997, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.