?? ch16.htm
字號:
</PRE>
<P>In CShowStringView::OnDraw(), change the code that calls GetColor() to call GetDocColor()
and then change the code that calls GetString() to call GetDocString(). Build the
project to check for any typing mistakes or forgotten changes. Although it may be
tempting to run ShowString now, it won't do what you expect until you make a few
more changes.</P>
<P>
<H3><A NAME="Heading5"></A>Showing the Window</H3>
<P>By default, Automation servers don't have a main window. Remember the little snippet
from CShowStringApp::InitInstance() in Listing 16.14.</P>
<P>
<H4>Listing 16.14  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;
</PRE>
<PRE>}
</PRE>
<P>This code returns before showing the main window. Although 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 <B>ShowWindow</B>. 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's filled in.</P>
<P><A HREF="javascript:popUp('16uvc06.gif')"><B>FIG. 16.6</B></A><B> </B><I>ClassWizard
makes it simple to add a ShowWindow() method.</I></P>
<P>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 <I>C</I> next to the properties reminds you that these properties are
custom properties. The <I>M </I>next to the methods reminds you that these are methods.
With ShowWindow() highlighted, click Edit Code and then type the function, as shown
in Listing 16.15.</P>
<BLOCKQUOTE>
<P>
<HR>
<B>See</B> "Displaying the Current Value," <A HREF="../ch17/ch17.htm"><B>ch.
17</B></A>
<HR>
</BLOCKQUOTE>
<H4>Listing 16.15  ShowStringDoc.cpp--CShowStringDoc::ShowWindow()</H4>
<PRE>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);
}
</PRE>
<PRE>}
</PRE>
<P>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.</P>
<P>
<H4>Listing 16.16  ShowStringDoc.cpp--CShowStringDoc::RefreshWindow()</H4>
<PRE>void CShowStringDoc::RefreshWindow()
{
UpdateAllViews(NULL);
SetModifiedFlag();
</PRE>
<PRE>}
</PRE>
<P>This arranges for the view (now that it's active) and its parent frame to be redrawn.
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(); however, if you prefer, you
can put it in each Set function and the notification functions for the direct-access
properties. You'll add a call to RefreshWindow() to each of those functions now--for
example, SetHorizCenter():</P>
<P>
<PRE>void CShowStringDoc::SetHorizCenter(BOOL bNewValue)
{
m_horizcenter = bNewValue;
RefreshWindow();
}
</PRE>
<P>And OnColorChanged() looks like this:</P>
<P>
<PRE>void CShowStringDoc::OnColorChanged()
{
RefreshWindow();
}
</PRE>
<P>Add the same RefreshWindow() call to SetVertCenter() and OnStringChanged(). Now
you're ready to build and test. Build the project and correct any typing errors.
Run ShowString as a standalone application to register it and to test your drawing
code. You can't change the string, color, or centering as you could with older versions
of ShowString because this version doesn't implement the Tools, Options menu item
and its dialog box. The controller application will do that for this version.</P>
<P>
<H2><A NAME="Heading6"></A>Building a Controller Application in Visual Basic</H2>
<P>This chapter has mentioned a controller application several times, and you may
have wondered where it will come from. You'll put it together in Visual Basic. Figure
16.7 shows the Visual Basic interface.</P>
<P><A HREF="javascript:popUp('16uvc07.gif')"><B>FIG. 16.7</B></A><B> </B><I>Visual
Basic makes Automation controller applications very quickly.</I></P>
<P>
<BLOCKQUOTE>
<P>
<HR>
<strong>TIP:</strong> If you don't have Visual Basic but Visual C++ version 4.x or earlier,
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.
<HR>
</BLOCKQUOTE>
<P>To build a controller application for the ShowString Automation server, start
by running Visual Basic. Create and empty project by choosing File, New, and double-clicking
Standard EXE. In the window at the upper-right labeled Project1, click the View Code
button. Choose Form from the left drop-down list box in the new window that appears;
the Form_Load() subroutine is displayed. Enter the code in Listing 16.17 into that
subroutine.</P>
<P>
<H4>Listing 16.17  Form1.frm--Visual Basic Code</H4>
<PRE>Private Sub Form_Load ()
Set ShowTest = CreateObject("ShowString.Document")
ShowTest.ShowWindow
ShowTest.HorizCenter = False
ShowTest.Color =
ShowTest.String = "Hello from VB"
Set ShowTest = Nothing
</PRE>
<PRE>End Sub
</PRE>
<P>Choose (General) from the left drop-down list box and then enter this line of
code:</P>
<P>
<PRE>Dim ShowTest As Object
</PRE>
<P>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 Debug, 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 onscreen. Whenever the debugger pauses,
the line of code that will run next is highlighted in yellow. Also notice that there
is an arrow beside the highlighted line to further mark it. You will see something
like Figure 16.8 with the default ShowString behavior.</P>
<P><A HREF="javascript:popUp('16uvc08.gif')"><B>FIG. 16.8</B></A><B> </B><I>The ShowWindow
method displays the main ShowString window.</I></P>
<P>Press F8 again to run the line that turns off horizontal centering. Notice that
you don't 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 make the change and then call a notification function to tell you
that it was changed. After this line executes, your screen will resemble Figure 16.9
because the SetHorizCenter method calls RefreshWindow() to immediately redraw the
screen.</P>
<P><A HREF="javascript:popUp('16uvc09.gif')"><B>FIG. 16.9</B></A><B> </B><I>The Visual
Basic program has turned off centering.</I></P>
<P>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<I> </I>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've
successfully controlled your Automation server from Visual Basic.</P>
<P>
<H2><A NAME="Heading7"></A>Type Libraries and ActiveX Internals</H2>
<P>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(). 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've already seen the dispatch map for ShowString, but you haven't seen the <I>type
library</I>. It's not meant for humans to read, but it is for ActiveX and the Registry.
It's 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.</P>
<P>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 properties or methods, the .ODL file is opened for you to
view. Listing 16.18 shows ShowString.odl.</P>
<P><A HREF="javascript:popUp('16uvc10.gif')"><B>FIG. 16.10</B></A><B> </B><I>Automation
servers have an entry in the ClassView for each of their interfaces.</I></P>
<P>
<H4>Listing 16.18  ShowString.odl--ShowString Type Library</H4>
<PRE>// 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}}
</PRE>
<PRE>};
</PRE>
<P>This explains why Visual Basic just thought of all four properties as properties;
that's how they're listed in this .ODL file. The two methods are here, too, in the
methods section. You passed "ShowString.Document" to CreateObject() because
there is a coclass Document section here. It points to a dispatch interface (dispinterface)
called IShowString. Here's the interface map from ShowStringDoc.cpp:</P>
<P>
<PRE>BEGIN_INTERFACE_MAP(CShowStringDoc, CDocument)
INTERFACE_PART(CShowStringDoc, IID_IShowString, Dispatch)
END_INTERFACE_MAP()
</PRE>
<P>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.</P>
<P>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 enable users 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 such as 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.</P>
<H1></H1>
<CENTER>
<P>
<HR>
<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> <BR>
</P>
<P>© <A HREF="../copy.htm">Copyright</A>, Macmillan Computer Publishing. All
rights reserved.
</CENTER>
</BODY>
</HTML>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -