Chapter 16

Building an Automation Server


Automation, fomerly called OLE Automation and then ActiveX Automation, is about writing code that other programs can call. Other programs call your code, not in the insulated manner of a DLL, but directly. The jargon is that your code exposes both methods (functions) and properties (variables) to other applications. The good part is that if your application is an automation server, you don't have to create a macro language for your application; you only have to make hooks for a more universal macro language, Visual Basic for Applications, to grab onto.

Designing ShowString Again

If you've been building the sample applications throughout this book, you can probably design ShowString in your sleep by now, but it's time to do it once again. This time, ShowString is not going to have a Tools, Options menuóinstead, other programs will directly set the string and other display options. The member variables in the document will be the same, and the code in OnDraw() will be the same as in all the other implementations of ShowString.

AppWizard's Automation Boilerplate

To build the version of ShowString that is an automation server, first use AppWizard to create an empty shell. Run AppWizard as usual, but in a different directory from your other versions of ShowString. Make almost exactly the same AppWizard choices as before: call it ShowString and then choose an MDI application and no database support. In AppWizard's Step 4, choose No Compound Ddocument Support(the radio buttons at the top of the dialog box) but turn on support for Automation. Continue through 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, and ActiveX Automation is starting to be known simply as Automation, the AppWizard dialog boxes refer to Compound Document Support. As well, many of the class names that are used throughout this chapter have Ole in their names, and comments refer to OLE. 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 just a few differences in this application from the do-nothing application without Automation support, primarily in the application object and the document.

CShowStringApp

The application object, CShowStringApp, has a number of changes. In the source file, just before InitInstance(), the code shown in Listing 16.1 has been added:

Listing 16.1ó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.
// {61C76C05-70EA-11D0-9AFF-0080C81A397C}
static const CLSID clsid =
{ 0x61c76c05, 0x70ea, 0x11d0, { 0x9a, 0xff, 0x0, 
0x80, 0xc8, 
    0x1a, 0x39, 0x7c } };

The numbers will be different in your code. This class ID identifies your automation application.

CShowStringApp::InitInstance() has several changes. The lines of code in Listing 16.2 initialize the ActiveX (OLE) libraries.

Listing 16.2óShowString.cppóInitializing Libraries

     
// Initialize OLE libraries
     if (!AfxOleInit())
     {
          AfxMessageBox(IDP_OLE_INIT_FAILED);
          return FALSE;
     }

As with the server application, InitInstance() goes on to connect the document template to the COleTemplateServer, after the document template has been initialized:

     m_server.ConnectTemplate(clsid, pDocTemplate, FALSE);

Then InitInstance() checks to see if the server is being launched to edit an embedded object or as an automation server; if so, there is no need to display the main window, so the function returns early, as shown in Listing 16.3.

Listing 16.3óShowString.cppó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;
     }
     // 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_DISPATCH_OBJECT);
     COleObjectFactory::UpdateRegistryAll();

If ShowString is being run as a stand-alone application, the code in Listing 16.3 updates the Registry as discussed in Chapter 15, "Building an ActiveX Server Application."

CShowStringDoc

The document class, CShowStringDoc, still inherits from CDocument rather than from any OLE document class, but that's where the similarities to the old non-OLE CShowStringDoc end. The first block of new code in ShowStringDoc.cpp is right after the message map and is shown in Listing 16.4

Listing 16.4óShowStringDoc.cppóDispatch Map

BEGIN_DISPATCH_MAP(CShowStringDoc, 
CDocument)
     //{{AFX_DISPATCH_MAP(CShowStringDoc)
          // NOTE - the ClassWizard will add and remove mapping macros here.
          
//      DO NOT EDIT what you see in these blocks of generated code!
     //}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()

This is an empty dispatch map. A dispatch map is like a message map, in that it maps events in the real world into function calls within this C++ class. When you expose methods and properties of this document with ClassWizard, the dispatch map will be updated.

After the dispatch map is another unique identifier, the IID (interface identifier). As Listing 16.5 shows, the IID is added as a static member, like the CLSID.

Listing 16.5óShowStringDoc.cppóIID

// Note: we add support for IID_IShowString to support typesafe binding
//  from VBA.  This IID must match the GUID that is attached to the 
//  dispinterface in the .ODL file.
// {61C76C07-70EA-11D0-9AFF-0080C81A397C}
static const IID IID_IShowString =
{ 0x61c76c07, 0x70ea, 0x11d0, { 0x9a, 0xff, 0x0, 0x80, 
    0xc8, 0x1a, 0x39, 0x7c } };

Then the interface map looks like this:

BEGIN_INTERFACE_MAP(CShowStringDoc, CDocument)
     INTERFACE_PART(CShowStringDoc, IID_IShowSt, Dispatch)
END_INTERFACE_MAP()

An interface map hides ActiveX functions like QueryInterface() from you, the programmer, and, like a message map, allows you to think at a more abstract level. ShowString will not have multiple entries in the interface map, but many applications do. Entries in the interface map are managed for you by ClassWizard.

The document constructor has some setting up to do. The AppWizard code is in Listing 16.6.

Listing 16.6óShowStringDoc.cppóConstructor

CShowStringDoc::CShowStringDoc()
{
     // TODO: add one-time construction code here
     
EnableAutomation();
     AfxOleLockApp();
}

EnableAutomation() does just what its name suggests: it enables automation for this document. AfxOleLockApp() is used to ensure that an application is not closed while one of its documents is still in use elsewhere. Imagine that a user has two applications open that use ShowString objects. When the first application is closed, ShowString should not be closed, because it is needed by the other application. ActiveX technology implements this by keeping a count, within the framework, of the number of active objects. AfxOleLockApp() increases this count. If it is nonzero when the user tries to close an application, the application is hidden but not actually closed.

It shouldn't be surprising, then, to see the destructor for ShowString's document:

CShowStringDoc::~CShowStringDoc()
{
     AfxOleUnlockApp();
}

AfxOleUnlockApp() decreases the count of active objects so that eventually, the application can be closed.

Properties to Expose

At this point, you have an automation server that does not expose any methods or properties. Also, the four member variables of the document that have been in all the previous versions of ShowString have not been added to this version. These member variables are the following:

These variables will be added as automation properties, so you will not type their names into the class definition for CShowStringDoc. Bring up ClassWizard by clicking its toolbar button or choosing View, ClassWizard. Click the Automation tab, shown in Figure 16.1, to add properties and methods. Make sure that CShowStringDoc is selected in the Class name box.

Fig. 16.1 ClassWizard's Automation tab handles most of the work of building an Automation server.

The first step in restoring the old ShowString functionality is to add member variables to the document class that will be exposed as properties of the automation server. There are two ways to expose properties: as a variable, and with functions. Exposing a property as a variable is rather like declaring a public member variable of a C++ class; other applications can look at the value of the property and change it directly. A notification function within your server is called when the variable is changed from the outside. Exposing with Get and Set functions is like implementing a private member variable with public access functions. Other applications appear to access the variable directly, but the framework arranges for a call to your functions to Get and Set the property. Your Get may make sure that the object is in a valid state (for example, that a sorted list is currently sorted or that a total has been calculated) before returning the property value. Your Set function may do error checking (validation) or may calculate other variables that depend on the property that the outside application is changing. To make a property read-only, you add it as a Get/Set function property and then do not implement a Set function.

For the purposes of this chapter, you will add the two centering flags to the CShowStringDoc class with Get and Set functions, and the string and color properties as direct-access properties. To do so, follow these steps:

  1. Make sure that CShowStringDoc is the selected class, then click the Add Property button to bring up the Add Property dialog box.
  2. Type String in the External name box, and ClassWizard types along with you, filling in the Variable name and Notification function boxes for you.
  3. Choose CString from the drop-down list box for Type, and then the dialog box should resemble Figure 16.2.

Fig. 16.2 Add String as a direct-access property.

  1. Click OK, click Add Property again, and then add Color as a direct-access property, as shown in Figure 16.3. Use short as the data type.

Fig. 16.3 Add Color as a direct-access property.

  1. Click OK, click Add Property again, and then add HorizCenter.

  2. Choose BOOL for the type and then select the Get/Set methods radio button. The Variable name and Notification function boxes are replaced by Get function and Set function, already filled in, as shown in Figure 16.4. (If the type changes from BOOL, choose BOOL again.) Click OK.
  3. Add VertCenter in the same way that you added HorizCenter.

Fig. 16.4 Add HorizCenter as a Get/Set method property.

Once you have clicked OK to add a property, you cannot change the type, external name, or other properties of the property. You will have to delete it and then add one that has the new type or external name, or whatever. Always look over the Add Property dialog box before clicking OK.

Figure 16.5 shows the ClassWizard summary of exposed properties and methods. The details of each property are shown in the Implementation box below the list of properties. In Figure 16.5, VertCenter is highlighted, and the Implementation box reminds you that VertCenter has a Get function and a Set function, showing their declarations. Click OK to close ClassWizard.

Fig. 16.5 ClassWizard provides a summary of the properties you have added.

It should come as no surprise that as a result of these additions, ClassWizard has made changes to the header and source files for CShowStringDoc. The new dispatch map in the header file is in Listing 16.7.

Listing 16.7óShowStringDoc.hóDispatch Map

     //{{AFX_DISPATCH(CShowStringDoc)
     CString m_string;
     afx_msg void 
OnStringChanged();
     short m_color;
     afx_msg void OnColorChanged();
     afx_msg BOOL GetHorizCenter();
     afx_msg void SetHorizCenter(BOOL bNewValue);
     afx_msg BOOL GetVertCenter();
     afx_msg void SetVertCenter(BOOL 
bNewValue);
     //}}AFX_DISPATCH
     DECLARE_DISPATCH_MAP()

Two new member variables have been added: m_string and m_color.

It's natural to wonder if these are actually public member variables; they are not. Just above this dispatch map is this line:

DECLARE_MESSAGE_MAP()

And that macro, when it expands, declares a number of protected variables. Since these declarations are immediately afterward, they are protected member variables and protected functions. They are accessed in just the same way that protected message-catching functions are: they are called by a member function hidden in the class that directs traffic using these maps.

A block of code has been added in the source file, but it's pretty boring, as you can see by looking at listing 16.8.

Listing 16.8óShowStringDoc.cppóNotification, Get, and Set functions

/////////////////////////////////////////////////////////
// CShowStringDoc commands
void CShowStringDoc::OnColorChanged() 
{
     // TODO: Add notification handler code
}
void CShowStringDoc::OnStringChanged() 
{
     // TODO: Add notification handler code
}
BOOL CShowStringDoc::GetHorizCenter() 
{
     // TODO: Add your property handler here
     return 
TRUE;
}
void CShowStringDoc::SetHorizCenter(BOOL bNewValue) 
{
     // TODO: Add your 
property handler here
}
BOOL CShowStringDoc::GetVertCenter() 
{
     // TODO: Add your 
property handler here
     return TRUE;
}
void CShowStringDoc::SetVertCenter(BOOL bNewValue) 
{
     // TODO: Add your property handler here
}

The class still does not have member variables for the centering flags. Add them by hand to the header file, as private member variables:

// Attributes
private:
     BOOL m_horizcenter;
     
BOOL m_vertcenter;

Now you can write their Get and Set functions; Listing 16.9 shows the code.

Listing 16.9óShowStringDoc.cppóGet and Set functions for the centering flags

BOOL CShowStringDoc::GetHorizCenter() 
{
     return m_horizcenter;
}
void CShowStringDoc::SetHorizCenter(BOOL bNewValue) 
{
     m_horizcenter = bNewValue;
}
BOOL CShowStringDoc::GetVertCenter() 
{
     return m_vertcenter;
}
void CShowStringDoc::SetVertCenter(BOOL bNewValue) 
{
     m_vertcenter = bNewValue;
}

The OnDraw() function

Restoring the member variables takes you halfway to the old functionality of ShowString. Changing the view's OnDraw() function will take you most of the rest of the way.

To write a version of OnDraw() that shows a string properly, open an old version of ShowStringóeither from your own work in Chapter 9, "Building a Complete Application: ShowString," or from the CD that comes with this bookóand then paste in the following bits of code. (If any of this code is unfamiliar to you, Chapter 9 explains it fully.) First, CShowStringDoc::OnNewDocument() (Listing 16.10) should initialize the member variables.

Listing 16.10óShowStringDoc.cppóCShowStringDoc::OnNewDocument()

BOOL CShowStringDoc::OnNewDocument()
{
     if (!CDocument::OnNewDocument())
          return FALSE;
     m_string = "Hello, world!";
     
m_color = 0;     //black
     m_horizcenter = TRUE;
     m_vertcenter = TRUE;
     return TRUE;
}

Next, edit the document's Serialize function: the new code is shown in Listing 16.11.

Listing 16.11óShowStringDoc.cppó CShowStringDoc::Serialize()

void CShowStringDoc::Serialize(CArchive& ar)
{
     if (ar.IsStoring())
     {
          ar << m_string;
          ar << m_color;
          ar << m_horizcenter;
          ar 
<< m_vertcenter;
     }
     else
     {
          ar >> m_string;
          ar >> m_color;
          ar >> m_horizcenter;
          ar >> m_vertcenter;
     
}
}

Finally, the view's OnDraw() function (Listing 16.12) actually shows the string.

Listing 16.12ó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 you added m_string, m_color, m_horizcenter, and m_vertcenter to the document with ClassWizard, they were added as protected member variables. This view code needs access to them. As you can see, the view calls public functions to get to these member variables of the document.

You could have chosen instead to make the view a friend to the document so that it could access the member variables directly, but that would give view functions the ability to use and change all the private and protected member variables of the document. This more limited access is more appropriate, and better preserves encapsulation.

There are already several functions in the document class that access these variables, but they are protected functions for use by ActiveX. The four public functions you will add have not-so-good names. Add them inline, as shown in Listing 16.13, to ShowStringDoc.h.

Listing 16.13óShowStringDoc.hóPublic Access Functions

public:
     CString GetDocString() {return m_string;}
     int     GetDocColor() {return m_color;}
     BOOL 
GetHorizcenter() {return m_horizcenter;}
     BOOL GetVertcenter() {return m_vertcenter;}

In CShowStringView::OnDraw(), change the call to GetColor() to a call to GetDocColor(), and the change the call to GetString() to a call to GetDocString(). Build the project to check for any typing mistakes or forgotten changes. While it may be tempting to run ShowString now, it's not going to do what you expect until you make a few more changes.

Showing the Window

By default, automation servers do not have a main window. Remember the little snippet from CShowStringApp::InitInstance() in Listing 16.14.

Listing 16.14óShowString.cppó 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;
     }

This code returns before showing the main window. While you could remove this test so that ShowString always shows its window, it's more common to add a ShowWindow() method for the controller application to call. You'll also need to add a RefreshWindow() method that updates the view after a variable is changed; ClassWizard makes it simple to add these functions. Bring up ClassWizard, click the Automation tab, make sure that CShowStringDoc is still the selected class, and then click Add Method. Fill in the external name as ShowWindow. ClassWizard fills in the internal name for you, and there's no need to change it. Choose void from the Return type drop-down list box. Figure 16.6 shows the dialog box after it has been filled in.

Fig. 16.6 ClassWizard makes it simple to add a ShowWindow() method.

Click OK the dialog box, and ShowWindow() appears in the middle of the list of properties, which turns out to be a list of properties and methods in alphabetical order. The C next to the properties reminds you that these properties are custom properties (other types of properties are discussed in the "Displaying the Current Value" section of the next chapter, "Building an ActiveX Control"). The M next to the methods reminds you that these are methods. With ShowWindow() highlighted, click Edit Code and then type in the function, as shown in Listing 16.15

Listing 16.15óShowStringDoc.cppóCShowStringDoc::ShowWindow()

void CShowStringDoc::ShowWindow() 
{
    POSITION pos = 
GetFirstViewPosition();
    CView* pView = GetNextView(pos);
    if (pView != NULL)
    {
        
CFrameWnd* pFrameWnd = pView->GetParentFrame();
        pFrameWnd->ActivateFrame(SW_SHOW);
        pFrameWnd = pFrameWnd->GetParentFrame();
        if (pFrameWnd != NULL)
            pFrameWnd->ActivateFrame(SW_SHOW);
    }
}

This code activates the view and asks for it to be shown. Bring up ClassWizard again, click Add Method, and add RefreshWindow(), returning void. Click OK and then Edit Code. The code for RefreshWindow(), shown in Listing 16.16, is even simpler.

Listing 16.16óShowStringDoc.cppóCShowStringDoc::RefreshWindow()

void CShowStringDoc::RefreshWindow() 
{
     UpdateAllViews(NULL);
     SetModifiedFlag();
}

This arranges for the view (now that it's active) and its parent frame to be redrawn. And, because a change to the document is almost certainly the reason for the redraw, this is a handy place to put the call to SetModifiedFlag(), though if you prefer, you can put it in each Set function and the notification functions for the direct-access properties. You will add a call to RefreshWindow() to each of those functions now. For example, SetHorizCenter() is shown in Listing 16.17.

Listing 16.17óShowStringDoc.cppóCShowStringDoc::SetHorizCenter()

void CShowStringDoc::SetHorizCenter(BOOL bNewValue) 
{
     m_horizcenter = bNewValue;
     RefreshWindow();
}

And OnColorChanged() looks like this:

void CShowStringDoc::OnColorChanged() 
{
     RefreshWindow();
}

Add the same RefreshWindow() call to SetVertCenter() and OnStringChanged(). Now you are ready to build and test. Build the project and correct any typing errors. Run ShowString as a stand-alone application, both to register it and to test your drawing code. You cannot change the string, color, or centering as you could with older versions of ShowString, because this version does not implement the Tools, Options menu item and its dialog box. The controller application is going to do that for this version.

Visual Basic

This chapter has mentioned a controller application several times, and you may have wondered where it's going to come from. You are going to put it together in Visual Basic. Figure 16.7 shows the Visual Basic interface.

Fig. 16.7 Visual Basic makes Automation controller applications very quickly.

If you donít have Visual Basic, but you do have an earlier release of Visual C++, you can use DispTest, a watered-down version of Visual Basic that once came with Visual C++. It was never added to the Start menu, but you can run DISPTEST.EXE from the C:\MSDEV\BIN folder or from your old Visual C++ CD-ROM's \MSDEV\BIN folder. If you've written VBA macros in Excel and have a copy of Excel, you can use that, too. For testing OLE Automation servers, it doesn't matter which you choose.

To build a controller application for the ShowString Automation server, start by running Visual Basic. In the window at the upper-right labeled Project1, click the View Code button. Choose Form from the left-hand drop-down list box in the new window that appears, and the Form_Load() subroutine is displayed. Enter the code in Listing 16.18 into that subroutine.

Listing 16.18óForm1.frmó Visual Basic code

Private Sub Form_Load ()
   Set ShowTest = CreateObject("ShowString.Document")
   ShowTest.ShowWindow
   
ShowTest.HorizCenter = False
   ShowTest.Color = 1
   ShowTest.String = "Hello from VB"
   Set ShowTest = 
Nothing
End Sub

Choose (General) from the left-hand drop-down list box and then enter this line of code:

Dim ShowTest As Object

For those of you who don't read Visual Basic, this code will be easier to understand if you execute it one line at a time. Choose Run, Step Into to execute the first line of code. Then repeatedly press F8 to move through the routine. (Wait after each press until the cursor is back to normal.) The line in the general code sets up an object called ShowTest. When the form is loaded (which is whenever you run this little program), an instance of the ShowString object is created. The next line calls the ShowWindow method to display the main window on the screen. Whenever the debugger pauses, the dashed box is around the line of code that will run next. You should see something like Figure 16.8 with the default ShowString behavior.

Fig. 16.8 The ShowWindow method displays the main ShowString window.

Press F8 again to run the line that turns off horizontal centering. Notice that you do not call the function SetHorizCenter. You exposed HorizCenter as a property of the OLE Automation server, and from Visual Basic, you access it as a property. The difference is that the C++ framework code calls SetHorizCenter to make the change, rather than just making the change and then calling a notification function to tell you that it was changed. After this line has executed, your screen will resemble Figure 16.9, because the SetHorizCenter method calls RefreshWindow() to immediately redraw the screen.

Fig. 16.9 The Visual Basic program has turned off centering.

As you continue through this program, pressing F8 to move a step at a time, the string will turn red and then change to Hello from VB. Notice that the change to these directly exposed properties looks no different than the change to the Get/Set method property, HorizCenter. When the program finishes, the window goes away. You have successfully controlled your Automation server from Visual Basic.

Type Libraries and ActiveX Internals

Many programmers are intimidated by ActiveX, and the last thing they want is to know what's happening under the hood. There's nothing wrong with that attitude at all. It's quite object-oriented, really, to trust the already-written ActiveX framework to handle the black magic of translating ShowTest.HorizCenter = False into a call to CShowStringDoc::SetHorizCenter(). But if you want to know how that "magic" happens, or what to do if it doesn't, you need to add one more piece to the puzzle. You have already seen the dispatch map for ShowString, but you haven't seen the type library. It is not meant for humans to read, but it's for ActiveX and the Registry. It is generated for you as part of a normal build from your Object Definition Language (ODL) file. This file was generated by AppWizard and is maintained by ClassWizard.

Perhaps youíve noticed, as you built this application, a new entry in the ClassView pane. Figure 16.10 shows this entry expanded: it contains all the properties and methods exposed in the IShowString interface of your Automation Server. If you right-click IShowString in this list, you can use the shortcut menu to add methods or properties. If you double-click any of the properties or methods, the old file is opened for you to view. Listing 16.19 shows ShowString.odl.

Fig. 16.10 Automation servers have an entry in the ClassView for each of their interfaces

Listing 16.19óShowString.odlóShowString type library

// ShowString.odl : type library source for 
ShowString.exe
// This file will be processed by the MIDL compiler to produce the
// type library (ShowString.tlb).
[ 
uuid(61C76C06-70EA-11D0-9AFF-0080C81A397C), version(1.0) ]
library ShowString
{
     
importlib("stdole32.tlb");
     
     //  Primary dispatch interface for CShowStringDoc
     
     [ uuid(61C76C07-70EA-11D0-9AFF-0080C81A397C) ]
     dispinterface IShowString
     {
          
properties:
               // NOTE - ClassWizard will maintain property information here.
               //    Use extreme caution when editing this section.
               //{{AFX_ODL_PROP(CShowStringDoc)
               [id(1)] BSTR String;
               [id(2)] short Color;
               [id(3)] boolean HorizCenter;
               [id(4)] boolean VertCenter;
               //}}AFX_ODL_PROP
               
          methods:
               // NOTE - ClassWizard will maintain method information here.
               
//    Use extreme caution when editing this section.
               //{{AFX_ODL_METHOD(CShowStringDoc)
               [id(5)] void ShowWindow();
               [id(6)] void RefreshWindow();
               //}}AFX_ODL_METHOD
     };
     //  Class information 
for CShowStringDoc
     
     [ uuid(61C76C05-70EA-11D0-9AFF-0080C81A397C) ]
     coclass Document
     {
          [default] dispinterface IShowString;
     };
     //{{AFX_APPEND_ODL}}
     //}}AFX_APPEND_ODL}}
};

This explains why Visual Basic just thought of all four properties as properties; that's how they are listed in this ODL file. The two methods are here, too, in the methods section. The reason you passed "ShowString.Document" to CreateObject() is that there is a coclass Document section here. It points to a dispatch interface (dispinterface) called IShowString. Here's the interface map from ShowStringDoc.cpp:

BEGIN_INTERFACE_MAP(CShowStringDoc, 
CDocument)
     INTERFACE_PART(CShowStringDoc, IID_IShowString, Dispatch)
END_INTERFACE_MAP()

So a call to CreateObject("ShowString.Document") leads to the coclass section of the odl file, which points to IShowString. The interface map points from IShowString to CShowStringDoc, which has a dispatch map that connects the properties and methods in the outside world to C++ code. You can see that editing any of these sections by hand could have disastrous results. Trust the wizards to do this for you.

From Here...

In this chapter, you built an Automation server and controlled it from Visual Basic. Automation servers are far more powerful than older ways of application interaction, but your server doesn't have any user interaction. If the Visual Basic program wanted to allow the user to choose the color, that would have to be built into the Visual Basic program. The next logical step is to allow the little embedded object to react to user events like clicks and drags, and to report to the controller program what has happened. That's what ActiveX controls do, as you'll see in the next chapter. Some chapters you may want to read include:


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