AppWizard is a very effective tool. It copies in code that almost all Windows applications need. After all, you aren't the first programmer who has needed an application with resizable edges, minimize and maximize buttons, and a File menu with Open, Close, Print Setup, Print, and Exit options, are you? In this chapter you will see what code AppWizard copies in for you, and what the choices on the AppWizard dialogs really mean.
A dialog-based application has no menus other than the system menu, and cannot save or open a file. This makes it good for simple utilities like the Windows Character Map, shown in Figure 16.1. (If you are using Windows 95, this utility may not be installed. Use Add/Remove programs under Control Panel to add it if you want to try using it.)
Figure 16.1 : Character Map is a dialog-based application.
To build an empty dialog-based application, follow the steps first shown in Chapter 3 "AppWizard and ClassWizard." Choose File, New, Project Workspace to bring up the New Project Workspace of Figure 3.1. Choose MFC AppWizard (exe) from the list box on the left, enter a project name, and click Create to move to Step 1 of the AppWizard process, shown in Figure 16.2.
See "Creating a Windows Application,"
Choose Dialog Based and click Next to move to Step 2, shown in Figure 16.3.
If you would like an About item on the system menu, check the About box item. To have AppWizard lay the framework for Help, check the Context sensitive help option. The third checkbox, 3D controls, should be checked for most Windows 95 and Windows NT applications. If you want your application to surrender control to other applications through OLE Automation, check the OLE Automation checkbox. If you want your application to contain OLE controls, check the OLE Controls checkbox. If you are planning to have this application work over the Internet with sockets, check the Windows Sockets box. (Dialog-based apps can't use MAPI because they have no document.) Click Next to move to the third step, shown in Figure 16.4.
As always, you want comments in your code. The decision between static linking and a shared DLL was discussed in Chapter 3 "AppWizard and ClassWizard." If your users are likely to have the MFC DLLs already (because they are developers or because they have another product that uses the DLL), or if they won't mind installing the DLLs as well as your executable, go with the shared DLL to make a smaller executable file and a faster link. Otherwise choose As a statically linked library. Click Next to move to the final step, shown in Figure 16.5.
See "Creating a Windows Application,"
In this step you can change the names AppWizard chooses for files and classes. This is rarely a good idea, since it will confuse people who maintain your code if the file names can't be easily determined from the class names, and vice versa. If you realize, looking at this dialog, that you made a poor choice of project name, use Back to move all the way back to the New Project Workspace dialog, change the name, click Create, and then use Next to come back to this dialog. Click Finish to see the summary of the files and classes to be created, like that in Figure 16.6.
Figure 16.6 : AppWizard confirms the files and classes it will create before creating them.
If any of the information on this dialog is not what you wanted, click Cancel and then use Back to move to the appropriate step and change your choices. When the information is right, click OK and watch as the application is created. Choose Build Build to compile and link the application, and then it's ready to be tested! Choose Build Execute to see it in action. Figure 16.7 shows the empty dialog-based application running, with the class view in the project workspace behind it in Developer Studio.
What did AppWizard do there? It created three classes: CAboutDlg, CDialog16Dlg, and CDialog16App. Dialog classes will be discussed in Chapter 17, "Building Menus and Dialogs," but you're going to look at CDialog16App now. The header file is shown in Listing 16.1.
Listing 16.1 dialog16.h_Main Header File
// dialog16.h : main header file for the DIALOG16 application // #ifndef __AFXWIN_H__ #error include 'stdafx.h' before including this file for PCH #endif #include "resource.h" // main symbols /////////////////////////////////////////////////////////////////// // CDialog16App: // See dialog16.cpp for the implementation of this class // class CDialog16App : public CWinApp { public: CDialog16App(); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CDialog16App) public: virtual BOOL InitInstance(); //}}AFX_VIRTUAL // Implementation //{{AFX_MSG(CDialog16App) // 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() };
CDialog16App inherits from CWinApp, which provides most of the functionality. CWinApp has a constructor and overrides the virtual function InitInstance(). Here's the constructor:
CDialog16App::CDialog16App() { // TODO: add construction code here, // Place all significant initialization in InitInstance }
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 different 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 16.2.
Listing 16.2 CDialog16App::InitInstance()
BOOL CDialog16App::InitInstance() { // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif CDialog16Dlg dlg; m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application_s message pump. return FALSE; }
This enables 3D controls, since you asked for them, and then puts up the dialog box that is the entire application. Next, the function declares an instance of CDialog16Dlg, dlg, and then calls the DoModal() function of the dialog, which displays the dialog box on the screen and returns IDOK if the user clicks OK, or IDCANCEL if the user clicks Cancel. It's up to you to make that dialog box actually do something. Finally, InitInstance() returns FALSE because this is a dialog-based application and when the dialog box is closed, the application is over. As you'll see in a moment for ordinary applications, InitInstance() usually returns TRUE to mean "everything is fine, run the rest of the application," or FALSE to mean "something went wrong while initializing." But since there is no "rest of the application," dialog-based apps always return FALSE from their InitInstance().
An SDI application does not bring up a dialog when it first runs, and then exit when that dialog is closed, as a dialog-based app does. Instead, it has menus that the user uses to open one document at a time and work with that document. The steps to create an empty SDI application are essentially identical to those presented for an MDI application in Chapter 3 "AppWizard and ClassWizard," in the "Creating a Windows Application" section. Rather than go through them again, this section presents the code that is generated when you choose an SDI application with no database or OLE support, with a toolbar, a status bar, Help, 3D controls, and source file comments, with the MFC library as a shared DLL.
Five classes have been created for you: if the application name was Chap16sdi the classes would be called CAboutDlg, CChap16sdiApp, CChap16sdiDoc, CChap16sdiView and Cmainframe. Dialog classes will be discussed in Chapter 17, "Building Menus and Dialogs," and the View, Doc, and Frame classes were discussed in Chapter 6 "The Document/View Paradigm." The App class header is shown in Listing 16.3.
Listing 16.3 Chap16sdi.h_Main Header File for the CHAP16SDI Application
// Chap16sdi.h : main header file for the CHAP16SDI application // #ifndef __AFXWIN_H__ #error include 'stdafx.h' before including this file for PCH #endif #include "resource.h" // main symbols ///////////////////////////////////////////////////////////////////////////// // CChap16sdiApp: // See Chap16sdi.cpp for the implementation of this class // class CChap16sdiApp : public CWinApp { public: CChap16sdiApp(); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CChap16sdiApp) public: virtual BOOL InitInstance(); //}}AFX_VIRTUAL // Implementation //{{AFX_MSG(CChap16sdiApp) 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() };
So, CChap16dsiApp inherits from CWinApp, it has a constructor, it overrides InitInstance(), and there is an entry in the message map for OnAppAbout. The constructor is as simple as the earlier constructors in this chapter:
CChap16sdiApp::CChap16sdiApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance }
InitInstance() is shown in Listing 16.4.
Listing 16.4 CChap16sdiApp::InitInstance()
BOOL CChap16sdiApp::InitInstance() { // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif 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(CChap16sdiDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CChap16sdiView)); 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; return TRUE; }
This sets up 3D controls, calls LoadStdProfileSettings()
to build the list of most recently used (MRU) files, and registers
the document template as discussed in Chapter 6 _The Document/View
Paradigm._ Next, it sets up an empty CCommandLineInfo
object to hold any parameters that may have been passed to the
application when it was run, and 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, without
effort on your part. For example, if the user types at the command
line Chap16sdi fooble then the application will start
and will open the file called fooble. The command line parameters
that ProcessShellCommand() supports are:
Parameter | Action |
none | Start app and open new file. |
Filename | Start app and open file. |
/p filename | Start app and print file to default printer. |
/pt filename printer | Start app and print file to the |
driver port | specified printer. |
/dde | Start app and await DDE command. |
/Automation | Start app as an OLE automation server. |
/Embedding | Start app to edit an embedded OLE item. |
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.
TIP |
You may have already known that you could invoke many Windows programs from the command line; for example, typing Notepad blah.txt at a DOS prompt will open blah.txt in Notepad. Other command line options work too, so typing Notepad /p blah.txt will open blah.txt in Notepad, print it, and then close Notepad. |
That_s the end of InitInstance(). It returns TRUE to indicate that the rest of the application should now run.
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:
BEGIN_MESSAGE_MAP(CChap16sdiApp, CWinApp) //{{AFX_MSG_MAP(CChap16sdiApp) 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()
This message map catches commands from menus, as discussed in
Chapter 5 _Messages and Commands._ When the user chooses Help
About, CChap16sdiApp::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:
void CChap16sdiApp::OnAppAbout() { CAboutDlg aboutDlg; aboutDlg.DoModal(); }
It simply declares an object that is an instance of CAboutDlg and calls its DoModal() function to display the dialog on the screen. There_s no need to handle OK or Cancel in any special way: this is just an About box.
If you selected Context Sensitive Help, AppWizard generates.HPJ files and a number of .RTF files to give some context sensitive help. These files are discussed in Chapter 21, in the _Components of the Help System_ section.
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 file names start to get confusing.
A Multiple Document Interface Application also has menus, and allows the user to have more than one document open at once. The steps to create an empty MDI application were presented in Chapter 3, _AppWizard and ClassWizard,_ in the _Creating a Windows Application_ section. Rather than go through them again, this section presents the code that is generated when you choose an MDI application with no database or OLE support, but with a toolbar, a status bar, Help, 3D controls, source file comments, and the MFC library as a shared DLL. The focus here is on what is different from the SDI application in the previous section.
Five classes have been created for you: if the application name was Chap16mdi, the classes would be called CAboutDlg, CChap16mdiApp, CChap16mdiDoc, CChap16mdiView, CChildFrame, and CMainFrame. Dialog classes will be discussed in Chapter 17, _Building Menus and Dialogs,_ and the View, Doc, and Frame classes were discussed in Chapter 6 _The Document/View Paradigm._ The App class header is shown in Listing 16.5.
Listing 16.5 Chap16mdi.h_Main Header File for the CHAP16MDI Application
// Chap16mdi.h : main header file for the CHAP16MDI application // #ifndef __AFXWIN_H__ #error include 'stdafx.h' before including this file for PCH #endif #include "resource.h" // main symbols ///////////////////////////////////////////////////////////////////////////// // CChap16mdiApp: // See Chap16mdi.cpp for the implementation of this class // class CChap16mdiApp : public CWinApp { public: CChap16mdiApp(); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CChap16mdiApp) public: virtual BOOL InitInstance(); //}}AFX_VIRTUAL // Implementation //{{AFX_MSG(CChap16mdiApp) 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() };
How does this differ from Chap16sdi.h? Only in the class names. The constructor is also the same as before. And OnAppAbout() is just like the SDI version. How about InitInstance()? It is in Listing 16.6.
Listing 16.6 CChap16mdiApp::InitInstance()
BOOL CChap16mdiApp::InitInstance() { // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif 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. CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_CHAP16TYPE, RUNTIME_CLASS(CChap16mdiDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CChap16mdiView)); AddDocTemplate(pDocTemplate); // create main MDI Frame window CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame; // 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 main window has been initialized, so show and update it. pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow(); return TRUE; }
What's different here? Using WinDiff can help. WinDiff is a tool that comes with Visual C++ and is discussed in Chapter 32,"Additional Advanced Topics," in the "WinDiff" section. It confirms that other than the class names, the differences are:
This shows a major advantage of the Document/View paradigm: it allows an enormous design decision to affect only a small amount of the code in your project, and hides that decision as much as possible.
AppWizard asks a lot of questions, and starts you down a lot of
roads at once. This chapter has explained InitInstance
and shown some of the code affected by the very first AppWizard
decision: whether to have AppWizard generate a Dialog-based, SDI,
or MDI application. Most of the other AppWizard decisions are
about topics that take an entire chapter. This table summarizes
those choices and where you can learn more:
| Decision | Chapter |
|
| MFC DLL or non MFC DLL | 32, Additional Advanced Topics |
|
| OCX Control | 26, Building an ActiveX Control |
|
| Console Application | 32, Additional Advanced Topics |
|
| Custom AppWizard | 29, Power-User Features in Developer Studio |
|
| ISAPI Extension Wizard | 13, Sockets, MAPI, and the Internet |
|
| Language support | 32, Additional Advanced Topics |
|
| Database support | 14, Database Access |
|
| OLE Container | 23, Building an ActiveX Container Application |
|
| OLE Mini-server | 24, Building an ActiveX Server Application |
|
| OLE Full server | 24, Building an ActiveX Server Application |
|
| Compound files | 23, Building an ActiveX Container Application |
|
| OLE Automation | 25, ActiveX Automation |
|
| Using OLE Controls | 26, Building an ActiveX Control |
|
| Docking toolbar | 18, Interface Issues |
|
| Status bar | 18, Interface Issues |
|
| Printing | 19, Printing and Print Preview |
|
| Context sensitive help | 21, Help |
|
| 3D Controls | _ |
|
| MAPI | 13, Sockets, MAPI, and the Internet |
|
| Windows Sockets | 13, Sockets, MAPI, and the Internet |
|
| Files in MRU list | _ |
|
| Comments in code | _ |
|
| MFC library | 3, AppWizard and ClassWizard |
|
Base class for View | 6, The Document/View Paradigm | ||
About box | 17, Building Menus and Dialogs |
Since some of these questions are not applicable for dialog-based applications, this table has a Dialog column: "yes" indicates that this decision applies to dialog-based applications too; "only" means that this is not a decision for SDI or MDI applications, which must have a Help About menu item. An entry of - in the column means that this decision doesn't really warrant discussion. These topics get a sentence or two in passing in this chapter or Chapter 3 "AppWizard and ClassWizard."
By now you know how to create applications that don't do much of anything. To make them do something, you need menus or dialog controls that give commands, and other dialog controls that gather more information. These are the subject of the next chapter, "Building Menus and Dialogs."
Once you have seen how to get an application to do something, you can move through the rest of Part III adding features like these: