Chapter 27

Creating an ActiveX Server Application


In the previous chapter, “Creating an ActiveX Client Application,” you created an ActiveX container application, which can link or embed ActiveX items. A container isn't much good, however, unless there is an ActiveX server registered with the system. It is, after all, the server that provides the linkable and embeddable items to the container. When you tested your container application, you used Microsoft Paint, one of the ActiveX servers that comes with Windows 95. In this chapter, you learn to create your own servers.

Creating the Basic ActiveX Server Application

Just as AppWizard can create a functional container application in a few steps, so too can it create a functional server application. The skeleton application created by AppWizard features all the basic functionality of an ActiveX server application, including the capability to embed, link, and edit ActiveX objects in-place in a container application's window. Later in this section, you'll see how to use the server and container applications together. But first you must create the basic server application, which you can do by completing the following steps.

1. Start a new AppWizard project workspace called ActiveXServ.

2. Give the new project the following settings in the AppWizard dialog boxes. The New Project Information dialog box should then look like Figure 27.1.

Step 1: Single document

Step 2: Default settings

Step 3: Select Full Server

Step 4: Default settings

Step 5: Default settings

Step 6: Default settings


FIG. 27.1

These are the AppWizard settings for the ActiveXServ project.

The complete source code and executable file for this version of the ActiveXServ application is located in the Chap27\ActiveXServ, Part 1 folder on this book's CD-ROM.

You've now created the basic ActiveXServ application. To compile the program, select Developer Studio's Build, Build command. Then, select Build, Execute to run the program. When you do, you see the window shown in Figure 27.2. Thanks to AppWizard and MFC, the program is already a powerful server application that can provide linked and embedded items to a container application.

Running the ActiveXServ application is all you have to do to register the application with the system as an ActiveX server.


FIG. 27.2

The ActiveXServ application's main window looks like this.

To see that the new server application actually works, close ActiveXServ and then run the completed ActiveXCont1 application that you created in Chapter 27, “Creating an ActiveX Server Application.” Select the Edit, Insert New Object command. The Insert Object dialog box appears. In the Object Type box, you'll see a new object called Active Document (Figure 27.3). This is the type of item that is supplied by the new ActiveXServ server application.


FIG. 27.3

The Insert Object dialog box lists the item that ActiveXServ supplies.

You can actually embed an Active Document item into the container application. Just double-click Active Document in the Object Type box. When you do, the ActiveXServ application supplies a new Active Document item and opens the item for editing, changing the toolbar and menus to that of the server application (Figure 27.4). To prove that the menus and toolbar really come from the server, click the question mark button on the toolbar. The ActiveXServ server application's About dialog box appears, as shown in Figure 27.5.


FIG. 27.4

ActiveXServ supports in-place editing of Active Document items.


FIG. 27.5

The About dialog box proves that the server application is running in-place.

When you're finished experimenting with ActiveXCont1 and its new Active Document item, close the application.

Understanding the ActiveXServ Skeleton Application

As you saw in the section “Creating the Basic ActiveX Container Application,” AppWizard has already supplied ActiveXServ with basic server functionality. To do this, AppWizard generated a lot of source code for the ActiveXServ project. In this section, you'll examine much of that code to see how the server application works.

As you look through MFC's ActiveX code, you'll notice that the word OLE comes up a lot. This is because MFC's classes and member functions were named before OLE was changed to ActiveX.

Exploring the CActiveXServApp Class

To understand the source code, the first place to look is in InitInstance(), where the application performs its initialization at startup. That function, which is defined as part of the CActiveXServApp class, performs its normal initialization, as well as some special initialization for ActiveX. To initialize the ActiveX libraries, InitInstance() calls AfxOleInit(), as shown in Listing 27.1:

Listing 27.1 lst27_01.cpp Calling AfxOleInit()

if (!AfxOleInit())
{
    AfxMessageBox(IDP_OLE_INIT_FAILED);
    return FALSE;
}

The InitInstance() function also calls the AfxEnableControlContainer() global function, which enables the application to act as a container for ActiveX controls. That function call looks like this:

AfxEnableControlContainer();

When creating the template for the application's document class, InitInstance() must call the CSingleDocTemplate class's SetServerInfo() member function to supply information about the server:

pDocTemplate-> SetServerInfo(
        IDR_SRVR_EMBEDDED, IDR_SRVR_INPLACE,
        RUNTIME_CLASS(CInPlaceFrame));

Here, the IDR_SRVR_EMBEDDED is the ID that represents the server application's menu and accelerator resources. These resources are used when the server application has been run in its own window from the container application. The IDR_SRVR_INPLACE ID represents the menus, toolbar, and accelerators needed by the container when an Active Document item is being edited in-place. The CInPlaceFrame class represents the window that is supplied by the server to the container application when an Active Document item is activated in-place.

Next, InitInstance() calls the server object's ConnectTemplate() member function to register the document with the system:

m_server.ConnectTemplate(clsid, pDocTemplate, TRUE);

The clsid argument is the GUID for the server that was created automatically when the application was generated. (GUID stands for Globally Unique Identifier, and each application receives its own so that the application can be registered in the system.) This GUID will be different for every Visual C++ server project. For the version of ActiveXServ that's on this book's CD-ROM, clsid is defined like this:

static const CLSID clsid =
    { 0xb28d4202, 0x6a0f, 0x11d0,
    { 0x84, 0x7f, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0 } };

The m_server data member, an object of the COleTemplateServer class, is defined in the CActiveXServApp class's header file.

Now, if the server application is being run in place or as an automation server (you'll learn about automation in Chapter 28, "Discovering OLE Automation"), the application shouldn't display its own window. InitInstance() returns before creating a window if this is the case, thanks to the program lines shown in Listing 27.2:

Listing 27.2 lst27_02.cpp Code that Handles In-place or Automation Servers

// Check to see if launched as OLE server
if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)
{
    // Register all OLE server (factories) as running.  This enables the
    //  OLE libraries to create objects from other applications.
    COleTemplateServer::RegisterAll();
    // Application was run with /Embedding or /Automation.  Don't show the
    //  main window in this case.
    return TRUE;
}

The call to COleTemplateServer::RegisterAll() in Listing 27.2 performs more required OLE initialization for MFC.

Finally, before creating and displaying the application's window, the following line registers the application as an ActiveX server:

m_server.UpdateRegistry(OAT_INPLACE_SERVER);

Exploring the CActiveXServSrvrItem Class

Like everything else in MFC, an Active Document item (which is the type of item that ActiveXServ can embed into a container application's document) is represented by an instance of a class. MFC provides the COleServerItem class to represent these types of objects. In the ActiveXServ application's source code, the CActiveXServSrvrItem class is derived from COleServerItem and represents embedded objects for this specific application.

The CActiveXServSrvrItem class, as provided by AppWizard, contains a number of member functions important to the process of embedding or linking Active Document items. First, the Serialize() function shown in Listing 27.3, doesn't serialize the item to disk, as usually happens with other Serialize() functions. Instead, the function handles embedded or linked items that are being copied to the Clipboard. Because an embedded item represents an entire document, AppWizard can provide default code for serializing the item. For linked items, however, you have to supply the code that serializes only the portion of the document that the user chose to link. For example, the user might have linked a paragraph from a word processor's document. Because the ActiveXServ application cannot supply partial documents for linking, you don't need to modify Serialize().

Listing 27.3 lst27_03.cpp The Serialize() Member Function

void CActiveXServSrvrItem::Serialize(CArchive& ar)
{
    // CActiveXServSrvrItem::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())
    {
        CActiveXServDoc* pDoc = GetDocument();
        ASSERT_VALID(pDoc);
        pDoc->Serialize(ar);
    }
}

Next is the OnGetExtent() member function, which is shown in Listing 27.4. OnGetExtent() returns the item's extent (size). In most cases, the extent will be the size of the entire item as it normally appears on the screen. However, you can change the extent to accommodate things like drawing thumbnail or iconic images of the item. As you can see, the AppWizard-generated version of this function using a hard-coded extent of 3000x3000, measured in HIMETRIC units.

Listing 27.4 lst27_04.cpp The OnGetExtent() Member Function

BOOL CActiveXServSrvrItem::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);
    // CActiveXServSrvrItem::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.
    CActiveXServDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // TODO: replace this arbitrary size
    rSize = CSize(3000, 3000);   // 3000 x 3000 HIMETRIC units
    return TRUE;
}

Finally, the OnDraw() member function, as shown in Listing 27.5, is responsible for displaying the item in the container when the item is not active. The default version of this function supplied by AppWizard simply sets up the mapping mode and window extent. You must add your own code to actually draw the item in the container application's window, which you'll do later in this chapter, in the section entitled "Completing the ActiveXServ Server Application."

Listing 27.5 lst27_04.cpp The OnDraw() Member Function

BOOL CActiveXServSrvrItem::OnDraw(CDC* pDC, CSize& rSize)
{
    CActiveXServDoc* 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;
}

Exploring the CActiveXServView Class

The application's view class doesn't look much different from other AppWizard-generated view classes. One big difference is that the class is now able to draw the application's document in its own window, as well as in a container application's window when activated in-place. All of this activity, however, happens in the background. There's little extra you need to do besides implement OnDraw(), as you've always done for any view class.

About the only thing you'll see in the view class that's directly related to being an ActiveX server is the OnCancelEditSrvr() member function (Listing 27.6), which responds when the user presses the Esc key when editing an Active Document item in-place in a container application. This action cancels the editing and closes the item. Later in this chapter, in the section entitled "Completing the ActiveXServ Server Application," you'll add program lines to the view class that draw an Active Document and that enable the user to edit the document.

Listing 27.6 lst27_06.cpp The OnCancelEditSrvr() Member Function

void CActiveXServView::OnCancelEditSrvr()
{
    GetDocument()->OnDeactivateUI(FALSE);
}

Exploring the CActiveXServDoc Class

One important difference between the document classes you're used to working with and the CActiveXServDoc class is that the latter is derived from MFC's COleServerDoc class, rather than from CDocument. Much of the ActiveX server functionality is built into the COleServerDoc class.

The class's constructor, as shown in Listing 27.7, is responsible for enabling compound files, which are very important to documents that contain linked or embedded items in addition to native data. You'll add additional construction code to the constructor when you complete the ActiveXServ application.

Listing 27.7 lst27_07.cpp The CActiveXServSrvrDoc Class's Constructor

CActiveXServDoc::CActiveXServDoc()
{
    // Use OLE compound files
    EnableCompoundFile();
    // TODO: add one-time construction code here
}

When the container application activates the server to edit a new linked or embedded item, MFC calls the document class's OnGetEmbeddedItem() member function. As you can see in Listing 27.8, OnGetEmbeddedItem() creates a new item of the appropriate type (in the case of the ActiveXServ application, the type is CActiveXServSrvrItem) and returns a pointer to the item from the function.

Listing 27.8 lst27_08.cpp The OnGetEmbeddedItem() Member Function

COleServerItem* CActiveXServDoc::OnGetEmbeddedItem()
{
    // OnGetEmbeddedItem is called by the framework
    //  to get the COleServerItem
    //  that is associated with the document.
    //  It is only called when necessary.
    CActiveXServSrvrItem* pItem = new CActiveXServSrvrItem(this);
    ASSERT_VALID(pItem);
    return pItem;
}

Exploring the CInPlaceFrame Class

The last class of interest is CInPlaceFrame, which represents the window in which an item is edited within a container application. That is, when the user opens an item for in-place editing, MFC creates a CInPlaceFrame window and positions the window in the container application's frame window. However, even though the CInPlaceFrame window is located in the container's window, the server is responsible for it. The CInPlaceFrame window must also manage the server's control bars. This occurs when the class's OnCreate() calls the OnCreateControlBars() member function, which is shown in Listing 27.9.

Listing 27.9 lst27_09.cpp The OnCreateControlBars() Member Function

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_SRVR_INPLACE))
    {
        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;
}

Completing the ActiveXServ Server Application

You're now ready to add the application-specific program lines that will complete the ActiveXServ application. These program lines draw the data that represent an Active Document item, as well as enable the user to edit the item, both in the application's main window and as an in-place item in a container application's window. Just complete the following steps to finish the application.

1. Load the ActiveXServDoc.h header files, and add the following line to the class's Attributes section, right after the public keyword:

CString m_string;

This line defines the string that holds the data for an ActiveXServ document.

2. Load the ActiveXServDoc.cpp file, and add the following line to the class's constructor, right after the TODO: add one-time construction code here comment:

m_string = "Default String";

This line initializes the data string to its default value.

3. Add the following line to the Serialize() function, right after the TODO: add storing code here comment:

ar << m_string;

4. Add the following line to the Serialize() function, right after the TODO: add loading code here comment:

ar >> m_string;

5. Use ClassWizard to add the OnEditCopy() message-response function to the CActiveXServDoc class, as shown in Figure 27.6.


FIG. 27.6

Use ClassWizard to add the OnEditCopy() function.

6. Click the Edit Code button, and add the lines shown as follows to the OnEditCopy() function, right after the TODO: Add your command handler code here comment:

    CActiveXServSrvrItem* pItem = GetEmbeddedItem();
    pItem->CopyToClipboard(TRUE);

These lines enable the server to copy its items to the Clipboard when the user selects the Paste Link command.

7. Load the SrvrItem.cpp file, and add the following lines to the OnDraw() function, right after the All drawing takes place in the metafile device context (pDC) comment:

    pDC->SetMapMode(MM_TEXT);
    pDC->TextOut(10, 10, pDoc->m_string);

These lines set up the mapping mode for the item, as well as display the item's data, which is a string that's stored as a member of the document class. The server item's OnDraw() function displays the item's data when the item is embedded, but inactive, in a container application. The mapping mode should usually be the same as the mapping mode used in the application's view window, to ensure that the item looks the same when linked or embedded in a container or when viewed in its native window.

8. Comment out the three lines that follow the (The extent is usually the same as the size returned from OnGetExtent) comment. You won't be using the default mapping mode and extent.

9. Load the ActiveXServView.cpp file, and add the following line to the OnDraw() function, right after the TODO: add draw code for native data here comment:

pDC->TextOut(10, 10, pDoc->m_string);

This line displays the document's data when the document is being displayed in the application's main window or when the document is embedded and activated in a container application's window. The default mapping mode for a view window is MM_TEXT, which is the mapping mode you used in the item class's OnDraw() function.

10. Use Developer Studio's dialog box editor to create the dialog box shown in Figure 27.7. Use all of the default IDs for the dialog box and its controls.


FIG. 27.7

This is the dialog box that you use to change the application's display string.

11. Double-click the dialog box to create a new class. The Adding a Class dialog box appears (Figure 27.8).


FIG. 27.8

This is the Adding a Class dialog box.

12. Make sure the Create a New Class option is selected, and click OK. The New Class dialog box appears. Fill in the dialog box as shown in Figure 27.9, and click OK to dismiss the dialog box.


FIG. 27.9

Enter the information shown here into the New Class dialog box.

13. Select the Member Variables tab of the MFC ClassWizard property sheet, and add the m_string member variable to the class, as shown in Figure 27.10. Click OK to dismiss the property sheet.


FIG. 27.10

Adding the m_string data member.

14. Add the following line to the top of the ActiveXServView.cpp file, right after the #endif line:

#include "stringdlg.h"

15. Load the IDR_MAINFRAME menu into Developer Studio's menu editor. Add a Set menu, as shown in Figure 27.11. Give the S et String command an ID of ID_SET_SETSTRING.


FIG. 27.11

Use the menu editor to add the Set menu.

16. Add identical Set menus to the IDR_SRVR_EMBEDDED and IDR_SRVR_INPLACE menus, as shown in Figures 27.12 and 27.13.

Adding the Set menu to all three menu resources ensures that the menu will be available both in the application's main window and in a container application's window when an Active Document item is selected or activated.


FIG. 27.12

Here's the IDR_SRVR_EMBEDDED menu.


FIG. 27.13

Here's the IDR_SRVR_INPLACE menu.

17. Use ClassWizard to add the OnSetSetstring() message-response function to the view class, as shown in Figure 27.14.


FIG. 27.14

The OnSetSetstring() message-response function responds to your new Set String menu command.

18. Click the Edit Code button, and add the lines shown in Listing 27.10 to the OnSetSetstring() function:

Listing 27.10 lst27_10.cpp Program Lines for the OnSetSetstring() Function

    CStringDlg dlg;
    int result = dlg.DoModal();
    if (result == IDOK)
    {
        // Reset the string's value.
        CActiveXServDoc* pDoc = GetDocument();
        pDoc->m_string = dlg.m_string;
        // Repaint the window.
        Invalidate();
        // Tell the document that it needs to be saved.
        pDoc->SetModifiedFlag();
        // Notify the document that the container
        // needs to be updated.
        pDoc->NotifyChanged();
    }

The complete source code and executable file for this version of the ActiveXServ application is located in the Chap27\ActiveXServ, Part 2 folder on this book's CD-ROM.

Using the Final Version of ActiveXServ

You've now created the final version of the ActiveXServ application. To compile the program, select Developer Studio's Build, Build command. Then, select Build, Execute to run the program. When you do, you see the application's main window. You can use the Set menu to change the value of the displayed string. However, things get really interesting when you run ActiveXServ as a server application.

To run ActiveXServ as a server application, first close the application's window. Then, run the container application you designed in the previous chapter. Finally, use the Edit, Insert Object command to insert an Active Document object into the container window, as shown in Figure 27.15. As you can see, not only does the new object appear in the container application's window, but also the server application's menu and toolbar appear, as well. This enables you to edit the object almost as if you were still running the main ActiveXServ application, which, for all intents and purposes, you are.


FIG. 27.15

You can run ActiveXServ as an ActiveX server by inserting one of its objects into a container application.

To see how the in-place editing works, select the Set, Set String command. The Set String dialog box appears (Figure 27.16). Type a new string into the dialog box and click OK. The new string appears in the container application's window (Figure 27.17).


FIG. 27.16

The Set String dialog box enables you to edit the string.


FIG. 27.17

The container application displays any changes you make to the data string.

When you click outside of the Active Document object, the object is deselected and the container application's menu and toolbar reappear, as shown in Figure 27.18.


FIG. 27.18

Deactivating an object brings back the container's menus and toolbar.

And that's all there is to it! There's not much else to discuss about the basic ActiveX server application. The details are described in the steps you used to build the application, as well as in the comments found in the program lines. Take some time now to experiment with the server application, changing program lines to see how they affect the application. That's the best way to learn MFC's many secrets.