?? ch16.htm
字號(hào):
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!--
function popUp(pPage) {
var fullURL = document.location;
var textURL = fullURL.toString();
var URLlen = textURL.length;
var lenMinusPage = textURL.lastIndexOf("/");
lenMinusPage += 1;
var fullPath = textURL.substring(0,lenMinusPage);
popUpWin = window.open('','popWin','resizable=yes,scrollbars=no,width=525,height=394');
figDoc= popUpWin.document;
zhtm= '<HTML><HEAD><TITLE>' + pPage + '</TITLE>';
zhtm += '<link rel="stylesheet" href="/includes/stylesheets/ebooks.css"></head>';
zhtm += '<BODY bgcolor="#FFFFFF">';
zhtm += '<IMG SRC="' + fullPath + pPage + '">';
zhtm += '<P><B>' + pPage + '</B>';
zhtm += '</BODY></HTML>';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}
//-->
</SCRIPT>
<link rel="stylesheet" href="/includes/stylesheets/ebooks.css">
<TITLE>Special Edition Using Visual C++ 6 -- Ch 16 -- Building an Automation Server</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF">
<CENTER>
<H1><IMG SRC="../button/que.gif" WIDTH="171" HEIGHT="66" ALIGN="BOTTOM" BORDER="0"><BR>
Special Edition Using Visual C++ 6</H1>
</CENTER>
<CENTER>
<P><A HREF="../ch15/ch15.htm"><IMG SRC="../button/previous.gif" WIDTH="128" HEIGHT="28"
ALIGN="BOTTOM" ALT="Previous chapter" BORDER="0"></A><A HREF="../ch17/ch17.htm"><IMG
SRC="../button/next.gif" WIDTH="128" HEIGHT="28" ALIGN="BOTTOM" ALT="Next chapter"
BORDER="0"></A><A HREF="../index.htm"><IMG SRC="../button/contents.gif" WIDTH="128"
HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A>
<HR>
</CENTER>
<CENTER>
<H1>- 16 -</H1>
</CENTER>
<CENTER>
<H1>Building an Automation Server</H1>
</CENTER>
<UL>
<LI><A HREF="#Heading1">Designing ShowString Again</A>
<UL>
<LI><A HREF="#Heading2">AppWizard's Automation Boilerplate</A>
<LI><A HREF="#Heading3">Properties to Expose</A>
<LI><A HREF="#Heading4">The OnDraw() Function</A>
<LI><A HREF="#Heading5">Showing the Window</A>
</UL>
<LI><A HREF="#Heading6">Building a Controller Application in Visual Basic</A>
<LI><A HREF="#Heading7">Type Libraries and ActiveX Internals</A>
</UL>
<P>
<HR SIZE="4">
<CENTER>
<H1></H1>
</CENTER>
<H2><A NAME="Heading1"></A>Designing ShowString Again</H2>
<P>Automation, formerly called OLE Automation and then ActiveX Automation, is about
writing code that other programs can call. Other programs call your code directly,
not in the insulated manner of a DLL. The jargon is that your code exposes <I>methods</I>
(functions) and <I>properties</I> (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 on to.</P>
<P>All Microsoft Office applications are Automation servers, so you may have seen
for yourself what a nice feature it is for a program to expose its methods and properties
in this way. What's more, Developer Studio itself is an Automation server, easy to
control with VBScript.</P>
<P>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 won't 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.</P>
<P>
<H3><A NAME="Heading2"></A>AppWizard's Automation Boilerplate</H3>
<P>To build the Automation server version of ShowString, first use AppWizard to create
an empty shell 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 3, choose
No Compound Document Support (the None 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 3D controls. Finally, select source file comments and a shared DLL.</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>NOTE:</strong> 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 classes used throughout this chapter
have Ole in their names, and comments refer to OLE. Although Microsoft has changed
the name of the technology, it hasn't propagated that change throughout Visual C++
yet. You'll have to live with these contradictions until the next release of Visual
C++.
<HR>
</BLOCKQUOTE>
<P>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.</P>
<P><B><I>CShowStringApp  </I></B>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:</P>
<P>
<H4>Listing 16.1  ShowString.cpp--CLSID</H4>
<PRE>// 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,
</PRE>
<PRE> 0x1a, 0x39, 0x7c } };
</PRE>
<P>The numbers will be different in your code. This class ID identifies your Automation
application.</P>
<P>CShowStringApp::InitInstance() has several changes. The lines of code in Listing
16.2 initialize the ActiveX (OLE) libraries.</P>
<P>
<H4>Listing 16.2  ShowString.cpp--Initializing Libraries</H4>
<PRE>// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
</PRE>
<PRE>}
</PRE>
<P>As with the server application of Chapter 15, "Building an ActiveX Server
Application," InitInstance() goes on to connect the document template to the
COleTemplateServer after the document template is initialized:</P>
<P>
<PRE>m_server.ConnectTemplate(clsid, pDocTemplate, FALSE);
</PRE>
<P>Then InitInstance() checks whether the server is being launched as an Automation
server or to edit an embedded object. If so, there's no need to display the main
window, so the function returns early, as shown in Listing 16.3.</P>
<P>
<H4>Listing 16.3  ShowString.cpp--How the App Was Launched</H4>
<PRE>// 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);
</PRE>
<PRE>COleObjectFactory::UpdateRegistryAll();
</PRE>
<P>If ShowString is being run as a standalone application, the code in Listing 16.3
updates the Registry as discussed in Chapter 15.</P>
<P><B><I>CShowStringDoc  </I></B>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 (see Listing 16.4).</P>
<P>
<H4>Listing 16.4  ShowStringDoc.cpp--Dispatch Map</H4>
<PRE>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
</PRE>
<PRE>END_DISPATCH_MAP()
</PRE>
<P>This is an empty dispatch map. A <I>dispatch map </I>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.</P>
<P>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.</P>
<P>
<H4>Listing 16.5  ShowStringDoc.cpp--IID</H4>
<PRE>// 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,
</PRE>
<PRE> 0xc8, 0x1a, 0x39, 0x7c } };
</PRE>
<P>Then the interface map looks like this:</P>
<P>
<PRE>BEGIN_INTERFACE_MAP(CShowStringDoc, CDocument)
INTERFACE_PART(CShowStringDoc, IID_IShowSt, Dispatch)
END_INTERFACE_MAP()
</PRE>
<P>An <I>interface map </I>hides COM functions such as QueryInterface() from you,
the programmer, and, like a message map, enables you to think at a more abstract
level. ShowString won't have multiple entries in the interface map, but many applications
do. ClassWizard manages entries in the interface map for you.</P>
<P>The document constructor has some setting up to do. The AppWizard code is in Listing
16.6.</P>
<P>
<H4>Listing 16.6  ShowStringDoc.cpp--Constructor</H4>
<PRE>CShowStringDoc::CShowStringDoc()
{
// TODO: add one-time construction code here
EnableAutomation();
AfxOleLockApp();
</PRE>
<PRE>}
</PRE>
<P>EnableAutomation() does just what its name suggests--enables Automation for this
document. AfxOleLockApp() is used to ensure that an application isn't 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
shouldn't be closed because it's 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's nonzero when users finish
using a server application, the application is hidden but not actually closed.</P>
<P>It shouldn't be surprising, then, to see the destructor for ShowString's document:</P>
<P>
<PRE>CShowStringDoc::~CShowStringDoc()
{
AfxOleUnlockApp();
}
</PRE>
<P>AfxOleUnlockApp() decreases the count of active objects so that eventually ShowString
can be closed.</P>
<P>
<H3><A NAME="Heading3"></A>Properties to Expose</H3>
<P>At this point, you have an Automation server that doesn't expose any methods or
properties. Also, the four member variables of the document that have been in all
the previous versions of ShowString haven't been added to this version. These member
variables are</P>
<UL>
<LI>string--The string to be shown
<P>
<LI>color--0 for black, 1 for red, and 2 for green
<P>
<LI>horizcenter--TRUE if the string should be centered horizontally
<P>
<LI>vertcenter--TRUE if the string should be centered vertically
</UL>
<P>These variables will be added as Automation properties, so you won't 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 (see Figure
16.1) to add properties and methods. Make sure that CShowStringDoc is selected in
the Class Name box.</P>
<P>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 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 now 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
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -