📄 ch01.htm
字号:
// Implementation
//{{AFX_MSG(CFirstSDIApp)
afx_msg void OnAppAbout();
// NOTE - The ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations
// immediately before the previous line.
</PRE>
<PRE>#endif //!defined(AFX_FIRSTSDI_H__CDF38D8A_8718_11D0_B02C_0080C81A3AA2__INCLUDED_)
</PRE>
<P>This code is confusing at the beginning. The #if(!defined) followed by the very
long string (yours will be different) is a clever form of include guarding. You may
have seen a code snippet like this before:</P>
<P>
<PRE>#ifndef test_h
#include "test.h"
#define test_h
#endif
</PRE>
<P>This guarantees that the file test.h will never be included more than once. Including
the same file more than once is quite likely in C++. Imagine that you define a class
called Employee, and it uses a class called Manager. If the header files for both
Employee and Manager include, for example, BigCorp.h, you will get error messages
from the compiler about "redefining" the symbols in BigCorp.h the second
time it is included.</P>
<P>There is a problem with this approach: If someone includes test.h but forgets
to set test_h, your code will include test.h the second time. The solution is to
put the test and the definition in the header file instead, so that test.h looks
like this:</P>
<P>
<PRE>#ifndef test_h
... the entire header file
#define test_h
#endif
</PRE>
<P>All AppWizard did was generate a more complicated variable name than test_h (this
wild name prevents problems when you have several files, in different folders and
projects, with the same name) and use a slightly different syntax to check the variable.
The #pragma once code is also designed to prevent multiple definitions if this file
is ever included twice.</P>
<P>The actual meat of the file is the definition of the class CFirstSDIApp. This
class inherits from CWinApp, an MFC class that provides most of the functionality
you need. AppWizard has generated some functions for this class that override the
ones inherited from the base class. The section of code that begins //Overrides is
for virtual function overrides. AppWizard generated the odd-looking comments that
surround the declaration of InitInstance(): ClassWizard will use these to simplify
the job of adding other overrides later, if they are necessary. The next section
of code is a message map and declares there is a function called OnAppAbout. You
can learn all about message maps in Chapter 3, "Messages and Commands."</P>
<P>AppWizard generated the code for the CFirstSDIApp constructor, InitInstance(),
and OnAppAbout() in the file firstsdi.cpp. Here's the constructor, which initializes
a CFirstSDIApp object as it is created:</P>
<P>
<PRE>CFirstSDIApp::CFirstSDIApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
</PRE>
<P>This is a typical Microsoft constructor. Because constructors don't return values,
there's no easy way to indicate that there has been a problem with the initialization.
There are several ways to deal with this. Microsoft's approach is a two-stage initialization,
with a separate initializing function so that construction does no initialization.
For an application, that function is called InitInstance(), shown in Listing 1.2.</P>
<P>
<H4>Listing 1.2  CFirstSDIApp::InitInstance()</H4>
<PRE>BOOL CFirstSDIApp::InitInstance()
{
AfxEnableControlContainer();
// Standard initialization
// If you are not using these features and want to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you don't need.
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
// Change the registry key under which our settings are stored.
// You should modify this string to be something appropriate,
// such as the name of your company or organization.
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings(); // Load standard INI file options (including // MRU)
// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows, and views.
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CFirstSDIDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CFirstSDIView));
AddDocTemplate(pDocTemplate);
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The one and only window has been initialized, so show and update it.
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
</PRE>
<PRE>}
</PRE>
<P>InitInstance gets applications ready to go. This one starts by enabling the application
to contain ActiveX controls with a call to AfxEnableControlContainer() and then turns
on 3D controls. It then sets up the Registry key under which this application will
be registered. (The Registry is introduced in Chapter 7, "Persistence and File
I/O." If you've never heard of it, you can ignore it for now.)</P>
<P>InitInstance() goes on to register single document templates, which is what makes
this an SDI application. Documents, views, frames, and document templates are all
discussed in Chapter 4.</P>
<P>Following the comment about parsing the command line, InitInstance() sets up an
empty CCommandLineInfo object to hold any parameters that may have been passed to
the application when it was run, and it calls ParseCommandLine() to fill that. Finally,
it calls ProcessShellCommand() to do whatever those parameters requested. This means
your application can support command-line parameters to let users save time and effort,
without effort on your part. For example, if the user types at the command line <B>FirstSDI
fooble</B>, the application starts and opens the file called <I>fooble</I>. The command-line
parameters that ProcessShellCommand() supports are the following:</P>
<P>
<TABLE BORDER="1">
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT"><B>Parameter</B></TD>
<TD ALIGN="LEFT"><B>Action</B></TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">None</TD>
<TD ALIGN="LEFT">Start app and open new file.</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">Filename</TD>
<TD ALIGN="LEFT">Start app and open file.</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">/p filename</TD>
<TD ALIGN="LEFT">Start app and print file to default printer.</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">/pt filename printer driver port</TD>
<TD ALIGN="LEFT">Start app and print file to the specified printer.</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">/dde</TD>
<TD ALIGN="LEFT">Start app and await DDE command.</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">/Automation</TD>
<TD ALIGN="LEFT">Start app as an OLE automation server.</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">/Embedding</TD>
<TD ALIGN="LEFT">Start app to edit an embedded OLE item.</TD>
</TR>
</TABLE>
</P>
<P>If you would like to implement other behavior, make a class that inherits from
CCommandLineInfo to hold the parsed command line; then override CWinApp:: ParseCommandLine()
and CWinApp::ProcessShellCommand() in your own App class.</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>TIP::</strong> You may already know that you can invoke many Windows programs from
the command line; for example, typing <B>Notepad blah.txt</B> at a DOS prompt will
open blah.txt in Notepad. Other command line options work, too, so typing <B>Notepad
/p blah.txt</B> will open blah.txt in Notepad, print it, and then close Notepad.
<HR>
</BLOCKQUOTE>
<P>That's the end of InitInstance(). It returns TRUE to indicate that the rest of
the application should now run.</P>
<P>The message map in the header file indicated that the function OnAppAbout() handles
a message. Which one? Here's the message map from the source file:</P>
<P>
<PRE>BEGIN_MESSAGE_MAP(CFirstSDIApp, CWinApp)
//{{AFX_MSG_MAP(CFirstSDIApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// NOTE - The ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// Standard file-based document commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
</PRE>
<P>This message map catches commands from menus, as discussed in Chapter 3. When
the user chooses Help About, CFirstSDIApp::OnAppAbout() will be called. When the
user chooses File New, File Open, or File Print Setup, functions from CWinApp will
handle that work for you. (You would override those functions if you wanted to do
something special for those menu choices.) OnAppAbout() looks like this:</P>
<P>
<PRE>void CFirstSDIApp::OnAppAbout()
{
CAboutDlg aboutDlg;
aboutDlg.DoModal();
}
</PRE>
<P>This code declares an object that is an instance of CAboutDlg, and calls its DoModal()
function to display the dialog onscreen. (Dialog classes and the DoModal() function
are both covered in Chapter 2.) There's no need to handle OK or Cancel in any special
way--this is just an About box.</P>
<P>
<H3><A NAME="Heading27"></A>Other Files</H3>
<P>If you selected Context-Sensitive Help, AppWizard generates an .HPJ file and a
number of .RTF files to give some context-sensitive help. These files are discussed
in Chapter 11 in the "Components of the Help System" section.</P>
<P>AppWizard also generates a README.TXT file that explains what all the other files
are and what classes have been created. Read this file if all the similar filenames
become confusing.</P>
<P>There are also a number of project files used to hold your settings and options,
to speed build time by saving partial results, and to keep information about all
your variables and functions. These files have extensions like .ncb, .aps, .dsw,
and so on. You can safely ignore these files because you will not be using them directly.</P>
<P>
<H2><A NAME="Heading28"></A>Understanding a Multiple Document Interface Application</H2>
<P>A multiple document interface application also has menus, and it enables the user
to have more than one document open at once. This section presents the code that
is generated when you choose an MDI application with no database or compound document
support, but instead with a toolbar, a status bar, Help, 3D controls, source file
comments, and the MFC library as a shared DLL. As with the SDI application, these
are the defaults after Step 1. The focus here is on what differs from the SDI application
in the previous section.</P>
<P>Five classes have been created for you. For the application FirstMDI, they are</P>
<UL>
<LI>CAboutDlg, a dialog class for the About dialog box
<P>
<LI>CFirstMDIApp, a CWinApp class for the entire application
<P>
<LI>CFirstMDIDoc, a document class
<P>
<LI>CFirstMDIView, a view class
<P>
<LI>CMainFrame, a frame class
</UL>
<P>The App class header is shown in Listing 1.3.</P>
<P>
<H4>Listing 1.3  FirstMDI.h--Main Header File for the FirstMDI Application</H4>
<PRE>// FirstMDI.h : main header file for the FIRSTMDI application
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -