Program Examples 1
Let go straight to the program example using MFC.
This is a Single Document Interface
(SDI) "Hello, world!"
classic example. The application has only one window, an object of a class derived
from
CFrameWnd.
All drawing occurs inside the frame window and all messages are handled there.
This just a warm up session and we are using AppWizard that can automate most
of our task in building Windows application. Follow the steps one-by-one.
Launch your Visual C++, click the
File menu and click the
New… sub menu. Click the
Projects tab for the AppWizard as
shown below. Select the MFC AppWizard (exe),
type your project name and set the project location as needed. Leave other setting
as default and click the OK button.
Figure 19: AppWizard dialog.
The wizard has 6 steps and you can exit at any step.
Select the Single document radio
button and uncheck the Document/View architecture
support then click Next
button.
Figure 20: Visual C++ AppWizard step 1 of 6.
Select the None
radio button for database support. Click the
Next button. We are going to create
the simplest project.
Figure 21: Visual C++ AppWizard step 2 of 6.
Uncheck the ActiveX
Controls. Just to make our code simpler.
Figure 22: Visual C++ AppWizard step 3 of 6.
Accept the defaults and click the
Next button.
Figure 23: Visual C++ AppWizard step
4 of 6.
|
|
Accept the defaults and click
Next button.
Figure 24: Visual C++ AppWizard step 5 of 6.
The following are the classes that will be generated
(also the related files created) for our project. Click the
Next button.
Figure 25: Visual C++ AppWizard step 6 of 6.
Finally the summary of the project settings. Click
the OK button.
Figure 26: Visual C++ AppWizard project summary.
Expand all folders in the
FileView and double click the
ChildView.cpp. FileView displays all
the project files that can be edited, logically. Physically, there are more
projects’ files.
Figure 27: Visual C++ FileView.
Add code to paint in the dialog. Add the following
code to the
CChildView::OnPaint
function in the ChildView.cpp source
code file:
void CChildView::OnPaint(){CPaintDC dc(this); // device context for paintingdc.TextOut(0, 0, "Hello, world!");// Do not call CWnd::OnPaint() for painting messages}
Listing 1.
Compile and run. You now have a complete SDI application
that has no dependencies on the document-view architecture.
Figure 28: Visual C++
"Hello
world" output.
Program Examples 2
The following is another classic example using MFC
AppWizard, and Visual C++ to create, build, and run a simple MFC-based single
document interface (SDI) Scribble
application. We just dealing with the View part of the Document/View architecture.
We will learn more detail about the Document/View architecture later.
Use MFC AppWizard to create a new project
-
In Visual C++/Visual Studio, on the File menu, click New.
-
In the New dialog box, click the Projects tab, and then do the following:
For the project type, select MFC AppWizard (exe). For the project name, type myscribble. Set the location for your project as needed. Accept the default platform Win32. Click OK to create the new project workspace. MFC AppWizard starts.
-
In MFC AppWizard Step 1, click Single document and English, leave the Document/View architecture support checkbox selected, and then click Next to go to Step 2.
-
Because this project does not need database support, accept the default None for database support and click Next to go to Step 3.
-
In Step 3, accept None for the document support, because we are not going to create a compound document that using Object Linking and Embedding (OLE), clear the ActiveX Controls check box, and then click Next to go to Step 4.
-
In Step 4, clear the Printing and print preview check box, accept the defaults (Docking toolbar, Initial status bar, 3D controls, and 4 files for the recent file list) and then click Next to go to Step 5.
-
In Step 5, leave the project style as MFC Standard, click Yes, please for generating source file comments and select As a shared DLL for MFC support. Click Next to go to Step 6.
-
In Step 6, click Finish to display the New Project Information dialog box summarizing your choices.
-
To create the application files with MFC AppWizard, click OK.
When MFC AppWizard is finished, you will be returned
to Visual C++/Visual Studio. To see the classes that MFC AppWizard created,
click the ClassView tab in the
Project Workspace window.
Add message handlers that will handle several mouse
clicking and dragging events. This just the skeleton, we will add the real codes
later – myscribbleView.cpp file.
-
On the View menu, click ClassWizard. The MFC ClassWizard property page appears.
-
In the Message Maps tab, in the combo boxes provided on the property page, select the myscribble project, the CMyscribbleView class, and the CMyscribbleView Object ID.
-
We are going to add member functions in the CMyscribbleView class. In the Messages list box, select the WM_LBUTTONDOWN message and click Add Function.
-
Repeat Step 3 for the WM_MOUSEMOVE and WM_LBUTTONUP messages.
-
Click OK to close ClassWizard.
Implement
OnLButtonDown,
OnMouseMove, and
OnLButtonUp
handlers. This is the real codes that will implement the mouse clicking and
dragging events.
-
Declaring variables in the declaration part. Open MyscribbleView.h (or use the ClassView).
-
In the public attributes section of the CMyscribbleView class declaration, define startpt and endpt variables as follows:
CPoint
startpt, endpt;
Or by using the ClassView, select the
CMyscribbleView
and right click, select the Add Member Variable:
Figure 29: Adding a member variable through the
ClassView.
Figure 30: Add member variable dialog.
-
Save MyscribbleView.h.
-
Then, the implementation codes in the implementation part. Open MyscribbleView.cpp.
-
In the constructor, initialize startpt and endpt coordinates to –1 as follows:
startpt
= -1;
endpt
= -1;
Figure 31: Scribble code segment.
-
Scroll to the OnLButtonDown handler.
-
In the OnLButtonDown handler, save the point where the mouse button is pressed as the start point:
startpt.x
= point.x;
startpt.y
= point.y;
-
Scroll to the OnMouseMove handler.
-
In the OnMouseMove handler, add the following code to draw a line from the previous detected point in the mouse drag to the current point:
CClientDC
dc(this);
endpt.x
= point.x;
endpt.y
= point.y;
if (startpt.x
!= -1)
{
dc.MoveTo(startpt.x, startpt.y);
dc.LineTo(endpt.x, endpt.y);
startpt.x = endpt.x;
startpt.y = endpt.y;
}
-
In the OnLButtonUp handler, add code to reinitialize the variable startpt as follows:
startpt
= -1;
Listing 3: Scribbles’ C++ code segment.
Build and run the project
-
On the Build menu, click Build Myscribble.exe. Visual Studio displays the status of the build process as it builds your project.
-
After the build is complete, on the Build menu, click Execute Myscribble.exe. The Scribble application starts. Try writing something using your mouse.
Figure 32: Visual C++’s Scribble program output.
Since its introduction in 2002, people’s attention
has focused on the many new features that have formed part of Microsoft .NET,
such as the major changes to Microsoft Visual Basic (Visual Basic .Net), the
introduction of C#, the new ASP.NET and ADO.NET models, and the increased use
of XML. So, from application framework we are introduced the next level, .Net
framework (currently version 2.0). C++ developers need not feel left out,
however, because a lot of the new features in Microsoft Visual C++ .NET make
C++ a first-class member of the .NET family of programming languages. This new
functionality is called the Managed Extensions for C++, and as well as
providing C++ programmers with access to all the functionality in the .NET class
libraries, it also lets you interoperate with existing C++ code, COM objects,
and the Win32 API.
Managed vs. Unmanaged Code
Code and data that live in the .NET world are called
managed because locations and lifetimes are managed by the common language
runtime (CLR). Code and data that exist outside of .NET are called unmanaged,
because there is no central mechanism for managing their lifetimes.
Figure 33: Enabling/disabling the /clr for
managed/unmanaged code in Visual C++ .Net through the project properties.
Figure 34: Enabling/disabling the /clr in
Visual C++ .Net.
Sometimes you have to mix the two, calling existing
unmanaged code from within .NET. The .NET Framework is the new library of classes
that you use to build Windows applications. It is large, quite complex, and
far-reaching in its scope. For MFC programming we do not use the /clr
(can be enabled in the Visual C++ .Net: Project menu
→ your_project_name
Properties →
General →
Use Managed Extensions →
Yes/No, as shown in the above Figures), that is unmanaged. The
managed code will be used in the .Net programming and don’t confused that .Net
framework is not compiler, it is framework. All program examples used in this
Tutorial series are unmanaged and we are using Visual C++ 6.0. The managed code
used in Visual C++ .Net provided that the /clr is used.
| Tenouk C & C++ | MFC Home | MFC AppWizard 1 | Event Handling, Mapping Modes, and a Scrolling View 1 | Download | Site Index |
Module 2:
Getting Started with Visual C++ 6.0 AppWizard 2
This is a continuation
from the previous module... Program examples compiled using Visual
C++ 6.0 (MFC 6.0) compiler on Windows XP Pro machine with Service Pack 2. Topics
and sub topics for this Tutorial are listed below:
-
Drawing Inside the View Window: The Windows Graphics Device Interface
-
The OnDraw() Member Function
-
The Windows Device Context
-
Adding Draw Code to the MYMFC Program
-
A Preview of the Resource Editors
-
The Contents of mymfc.rc file
-
Running the Dialog Resource Editor
-
Enabling the Diagnostic Macros
-
Two Ways to Run a Program
-
AFX Functions
-
Precompiled Headers Story
Compile and link the generated code. AppWizard,
in addition to generating code, creates custom project and workspace
files for your application. The project file,
mymfc.dsp, specifies all the
file dependencies together with the compile and link option flags. Because
the new project becomes Visual C++'s current project, you can now build
the application by choosing Build
mymfc.exe from the Build
menu (or by clicking the Build
toolbar button) shown here.
Figure 14: Building
mymfc.exe.
If the build is successful, an executable
program named mymfc.exe
is created in a new Debug
subdirectory underneath \mfcproject\mymfc. The OBJ files and other intermediate
files are also stored in Debug.
Figure 15: Files generated after the building
process under the Debug directory.
|
Compare the file structure on disk with the structure
in the Workspace window's FileView
page shown here. The FileView page contains a logical view of your project.
The header files show up under Header Files,
even though they are in the same subdirectory as the CPP files. The resource
files are stored in the \res subdirectory.
Figure 16: mymfc project FileView.
Test the resulting application. Choose
Execute mymfc.exe from the Build
menu. Experiment with the program. It doesn't do much because there is no coding
yet. Actually, as you might guess, the program has a lot of features - you simply
haven't activated them yet. Close the program window when you've finished experimenting.
Figure 17: Executing/running the
mymfc.exe program.
Figure 18: mymfc program output.
The most important
CMymfcView
base classes are
CWnd
and
CView.
CWnd provides
CMymfcView's
"windowness," and
CView provides the hooks to
the rest of the application framework, particularly to the document and to the
frame window.
Now you're ready to write code to draw inside the
view window. You'll be making a few changes directly to the MYMFC source code.
The
OnDraw()
Member Function
Specifically, you'll be fleshing out
OnDraw()
in mymfcView.cpp.
OnDraw()
is a virtual member function of the
CView
class that the application framework calls every time the view window needs
to be repainted. A window needs to be repainted
if the user resizes the window or reveals a previously hidden part of the window,
or if the application changes the window's data. If the user resizes the window
or reveals a hidden area, the application framework calls
OnDraw(),
but if a function in your program changes the data, it must inform Windows of
the change by calling the view's inherited
Invalidate()
(or
InvalidateRect())
member function. This call to
Invalidate() triggers a later
call to
OnDraw().
Even though you can draw inside a window at any
time, it's recommended that you let window changes accumulate and then process
them all together in the
OnDraw()
function. That way your program can respond both to program-generated events
and to Windows-generated events such as size changes.
The Windows Device Context
Windows doesn't allow direct access to the display
hardware but communicates through an abstraction called a "device context" that is associated with the window. In the MFC
library, the device context is a C++ object of class
CDC that
is passed (by pointer) as a parameter to
OnDraw().
After you have the device context pointer, you can call the many
CDC
member functions that do the work of drawing.
Adding Draw Code to the MYMFC
Program
Now let's write the code to draw some text and a
circle inside the view window. Be sure that the project MYMFC is open in Visual
C++. You can use the Workspace window's ClassView to locate the code for the
function and double-clicking the file to edit the
OnDraw()
function in mymfcView.cpp. So, find the AppWizard-generated
OnDraw()
function in mymfcView.cpp:
-----------------------------------------------------------------------------------------------
Listing 1: mymfc C++ code segment.
The following code replaces the previous code:
void
CMymfcView::OnDraw(CDC* pDC)
{
//
TODO: add draw code for native data here
// prints in default font and size, top left corner
pDC->TextOut(5, 0, "Hello MFC world!");
// selects a brush for the circle interior
pDC->SelectStockObject(GRAY_BRUSH);
// draws a gray circle 100 units in diameter
pDC->Ellipse(CRect(0, 20, 100, 120));
}
You can safely remove the call to
GetDocument()
because we're not dealing with documents yet. The functions
TextOut(),
SelectStockObject() and
Ellipse()
are all member functions of the application framework's device context class
CDC. The
Ellipse()
function draws a circle if the bounding rectangle's length is equal to its width.
The MFC library provides a handy utility class,
CRect,
for Windows rectangles. A temporary
CRect
object serves as the bounding rectangle argument for the ellipse drawing function.
Recompile and test MYMFC. Choose
Build mymfc.exe sub menu from
Build menu and if there are no compile
errors, test the application again. Now you have a program that visibly does
something!
Figure 19: mymfc program output.
A Preview of the Resource
Editors
Now that you have a complete application program,
it's a good time for a quick look at the resource editors. Although the application's
resource script, mymfc.rc, is an ASCII file, modifying it with a text editor
is not a good idea. That's the resource editors' job.
The Contents of
mymfc.rc
The resource file determines much of the MYMFC application's
"look and feel." The file mymfc.rc
contains (or points to) the Windows resources listed here. You can open the
rc file with text editor.
Resource
|
Description
|
Accelerator
|
Definitions for keys that simulate menu
and toolbar selections.
|
Dialog
|
Layout and contents of dialog boxes.
MYMFC has only the About dialog box.
|
Icon
|
Icons (16-by-16-pixel and 32-by-32-pixel
versions), such as the application icon you see in Microsoft Windows
Explorer and in the application's About dialog box. MYMFC uses the
MFC logo for its application icon.
|
Menu
|
The application's top-level menu and
associated pop-up menus.
|
String
table
|
Strings that are not part of the C++
source code.
|
Toolbar
|
The row of buttons immediately below
the menu.
|
Version
|
Program description, version number,
language, and so on.
|
Table 3: Project resources.
|
|
Figure 20: mymfc
files in ResourceView.
Figure 21: mymfc
resource file (RC).
In addition to the resources listed above, mymfc.rc
contains the statements:
#include "afxres.h"#include "afxres.rc"
Which bring in some MFC library resources common
to all applications. These resources include strings, graphical buttons, and
elements needed for printing and OLE. If you're using the shared DLL version
of the MFC library, the common resources are stored inside the MFC DLL. The
mymfc.rc file also contains the statement:
#include "resource.h"
This statement brings in the application's three
#define constants,
which are:
Object ID
|
Meaning
|
IDR_MAINFRAME
|
Identifying the menu, icon, string list,
and accelerator table.
|
IDR_MYMFCTYPE
|
Identifying the default document icon,
which we won't use in this program.
|
IDD_ABOUTBOX
|
Identifying the About dialog box.
|
Table 4.
|
|
This same resource.h file is included indirectly
by the application's source code files. If you use a resource editor to add
more constants (symbols), the definitions ultimately show up in
resource.h. Be careful if you edit this file in text mode because
your changes might be removed the next time you use a resource editor.
Running the Dialog Resource
Editor
Open the project's RC file. Click the
ResourceView button in the Workspace
window. If you expand each item, you will see the following in the resource
editor window.
Figure 22:
mymfc's resources in ResourceView
|
|
Examine the application's resources. Now take some
time to explore the individual resources. When you select a resource by double-clicking
on it, another window opens with tools appropriate for the selected resource.
If you open a dialog resource, the control palette should appear. If it doesn't,
right-click inside any toolbar, and then check
Controls.
Figure 23:
IDD_ABOUTBOX
dialog in resource editor.
Modify the
IDD_ABOUTBOX
dialog box by making some changes to the About
mymfc dialog box, shown here.
Figure 24: Changing the
About dialog properties.
You can change the size of the window by dragging
the right and bottom borders, move the OK button, change the text, and so forth.
Simply click on an element to select it, and then right-click to change its
properties.
Rebuild the project with the modified resource file.
In Visual C++, choose Build mymfc.exe
from the Build menu. Notice that
no actual C++ recompilation is necessary. Visual C++ saves the edited resource
file, and then the Resource Compiler
(rc.exe)
processes mymfc.rc to produce a
compiled version, mymfc.res, which
is fed to the linker. The linker runs quickly because it can link the project
incrementally.
Test the new version of the application. Run the
MYMFC program again, and then choose About
from the application's Help menu
to confirm that your dialog box was changed as expected.
Figure 25: mymfc
program output.
Win32 Debug Target vs Win32
Release Target
If you open the drop-down list on the
Build toolbar, you'll notice two items:
Win32 Debug and
Win32 Release. The
Build toolbar is not present by default,
but you can choose Customize from
the Tools menu to display it.
Figure 26: Release vs Debug target.
These items are targets that represent distinct
sets of build options. When AppWizard generates a project, it creates two default
targets with different settings. These settings are summarized in the following
table.
Option
|
Release Build
|
Debug Build
|
Source code debugging
|
Disabled
|
Enabled for both compiler and linker
|
MFC diagnostic macros
|
Disabled (NDEBUG
defined)
|
Enabled (_DEBUG
defined)
|
Library linkage
|
MFC Release library
|
MFC Debug libraries
|
Compiler optimization
|
Speed optimization (not available in
Learning Edition)
|
No optimization (faster compile)
|
Table 5.
|
||
You develop your application in
Debug mode, and then you rebuild in
Release mode prior to delivery.
The Release build EXE will be smaller and faster because of the optimization
and assuming that you have fixed all the bugs. You select the configuration
from the build target window in the Build
toolbar or Build minibar.
Figure 27: Build toolbar and minibar.
By default, the Debug output files and intermediate
files are stored in the project's Debug subdirectory; the Release files are
stored in the Release subdirectory. You can change these directories (and other
project settings as well) on the General
tab in the Project Settings dialog
box.
Figure 28: Visual C++ project settings.
You can create your own custom configurations if
you need to by choosing Configurations
from Visual C++'s Build menu.
Enabling the Diagnostic Macros
The application framework
TRACE
macros are particularly useful for monitoring program activity. They require
that tracing be enabled, which is the default setting. If you're not seeing
TRACE output from your program, first make
sure that you are running the debug target from the debugger and then run the
TRACER utility. If you check the
Enable Tracing checkbox,
TRACER will insert the statement:
TraceEnabled = 1
in the
[Diagnostics]
section of a file named Afx.ini.
(No, it's not stored in the Registry.) You can also use
TRACER to enable other MFC diagnostic
outputs, including message, OLE, database, and Internet information.
AFX Functions
Not all of the functions that MFC offers are members
of classes. MFC provides an API of sorts all its own in the form of global functions
whose names begin with
Afx. Class member
functions can be called only in the context of the objects to which they belong,
but AFX functions are available anytime and anywhere.
The following table lists some of the more commonly
used AFX functions.
AfxBeginThread()
simplifies the process of creating threads of execution.
AfxMessageBox()
is the global equivalent of the Windows
MessageBox()
function and, unlike
CWnd::MessageBox,
can be called just as easily from a document class as from a window class.
AfxGetApp() and
AfxGetMainWnd()
return pointers to the application object and the application's main window
and are useful when you want to access a function or data member of those objects
but don't have a pointer readily available.
AfxGetInstanceHandle()
is handy when you need an instance handle to pass to a Windows API function.
Even MFC programs call API functions every now and then! The following Table
list the commonly use Afx functions.
Function Name
|
Description
|
AfxAbort()
|
Unconditionally terminates an application;
usually called when an unrecoverable error occurs.
|
AfxBeginThread()
|
Creates a new thread and begins executing
it.
|
AfxEndThread()
|
Terminates the thread that is currently
executing.
|
AfxMessageBox ()
|
Displays a Windows message box.
|
AfxGetApp()
|
Returns a pointer to the application
object.
|
AfxGetAppName()
|
Returns the name of the application.
|
AfxGetMainWnd()
|
Returns a pointer to the application's
main window.
|
AfxGetInstanceHandle()
|
Returns a handle identifying the current
application instance.
|
AfxRegisterWndClass()
|
Registers a custom
WNDCLASS
for an MFC application.
|
Table 6
|
|
Precompiled Headers Story
Figure 29: Invoking the Visual C++ project settings
dialog.
Figure 30: Visual C++ project settings dialog.
When AppWizard generates a project, it generates
switch settings and files for precompiled headers. You must understand how the
make system processes precompiled headers in order to manage your projects effectively.
Visual C++ has two precompiled header "systems:" automatic and manual. Automatic
precompiled headers, activated with the /Yx
compiler switch, store compiler output in a "database" file. Manual precompiled
headers are activated by the /Yc
and /Yu switch settings and are central to all AppWizard-generated
projects. Precompiled headers represent compiler "snapshots" taken at a particular
line of source code. In MFC library programs, the snapshot is generally taken
immediately after the following statement:
#include
"StdAfx.h"
The file
StdAfx.h
contains
#include
statements for the MFC library header files. The file's contents depend on the
options that you select when you run AppWizard, but the file always contains
these statements:
#include
<afxwin.h>
#include
<afxext.h>
If you're using compound documents,
StdAfx.h also
contains the statement:
#include
<afxole.h>
And if you're using Automation or ActiveX Controls,
it contains:
#include
<afxdisp.h>
If you're using Internet Explorer 4 Common Controls,
StdAfx.h contains the statement:
#include
<afxdtctl.h>
Occasionally you will need other header files -
for example, the header for template-based collection classes that is accessed
by the statement:
#include
<afxtempl.h>
The source file StdAfx.cpp contains only the statement:
#include
"StdAfx.h"
And is used to generate the precompiled header file
in the project directory. The MFC library headers included by
StdAfx.h never change,
but they do take a long time to compile. The compiler switch
/Yc,
used only with
StdAfx.cpp,
causes creation of the precompiled header (PCH) file. The switch
/Yu,
used with all the other source code files, causes use of an existing PCH file.
The switch
/Fp
specifies the PCH filename that would otherwise default to the project name
(with the PCH extension) in the target's output files subdirectory. Figure 31
illustrates the whole process.
AppWizard sets the
/Yc and
/Yu switches for you, but you can make changes
if you need to. It's possible to define compiler switch settings for individual
source files. On the C/C++ tab in the Project Settings dialog box, if you select
only
StdAfx.cpp, you'll see the
/Yc setting. This overrides the
/Yu
setting that is defined for the target.
Be aware that PCH files are big - 5 MB is typical.
If you're not careful, you'll fill up your hard disk. You can keep things under
control by periodically cleaning out your projects' Debug directories, or you
can use the
/Fp
compiler option to reroute PCH files to a common directory.
Figure 31: The Visual C++ precompiled header process.
Two Ways to Run a Program
Visual C++ lets you run your program directly (by
pressing Ctrl-F5) or through the debugger (by pressing F5). Running your program
directly is much faster because Visual C++ doesn't have to load the debugger
first. If you know you don't want to see diagnostic messages or use breakpoints,
start your program by pressing Ctrl-F5 or use the "exclamation point" button
on the Build toolbar.
First: C++ Using MFC
C++ programs that use the Microsoft Foundation Class (MFC) application
framework can be easily created with Fastgraph's MFC AppWizard for
Visual C++ 5.0 or later. To use Fastgraph's MFC AppWizard, copy the file
FGwiz.awx from the Fastgraph utilities directory to your Visual C++
Template directory. Once installed, "Fastgraph MFC AppWizard" will be
one of the options listed when you create a new Visual C++ project. If
you wish to uninstall the AppWizard, just delete FGwiz.awx from the
Template directory.
MFC programs consist of application and window class declarations (both
derived from MFC base classes), a global application object, message
response functions (event handlers), and possibly your own additional
functions. Like programs that use the Windows API, MFC programs have a WinMain() function, a WindowProc() function, and a message loop, but they are hidden inside MFC.
Our first example is a single-window program without a menu (such
programs are called simple frame window applications in MFC). For this
example, Fastgraph's MFC AppWizard created First.cpp, First.h,
MainFrame.cpp, MainFrame.h, and other files. First.cpp instantiates the
application object theApp from the CFirstApp class (CFirstApp is derived
from MFC's CWinApp base class). MainFrame.cpp contains "canned" message
response functions for the Windows events we typically must handle in a
Fastgraph program. We can, of course, modify these message response
functions as needed.
On program startup, Windows calls MFC's built-in WinMain() function, which in turn calls the CFirstApp::InitInstance() and CWinApp::Run() member functions. The CFirstApp::InitInstance()
function instantiates the frame window object pFrame from the
CMainFrame class (CMainFrame is derived from MFC's CFrameWnd base
class). The pFrame object's Create() member function creates the program's main window (this also generates the WM_CREATE message), while its ShowWindow() and UpdateWindow() member functions initially display and paint the main window. CWinApp::Run()
is hidden in MFC's CWinApp base class and implements the Windows
message loop. The message loop retrieves messages Windows sends to the
program and in turn sends these to the message response functions for
processing. The message loop executes until the user exits the program.
Here is the First.cpp file for our first example program:
/****************************************************************************\
* *
* First.cpp *
* *
* This is the first Fastgraph for Windows example program. It demonstrates *
* tasks common to most Fastgraph for Windows programs and serves as a *
* template for building the other examples. *
* *
\****************************************************************************/
// First.cpp : Defines the class behaviors for the application.
//
#include "stdafx.h"
#include "First.h"
#include "MainFrame.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CFirstApp
BEGIN_MESSAGE_MAP(CFirstApp, CWinApp)
//{{AFX_MSG_MAP(CFirstApp)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CFirstApp construction
CFirstApp::CFirstApp()
{
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CFirstApp object
CFirstApp theApp;
/////////////////////////////////////////////////////////////////////////////
// CFirstApp initialization
BOOL CFirstApp::InitInstance()
{
// Standard initialization
// Change the registry key under which our settings are stored.
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
m_pMainWnd = NULL;
CMainFrame* pFrame = new CMainFrame;
if (!pFrame->Create(NULL,"First Fastgraph for Windows Program"))
return FALSE;
m_pMainWnd = pFrame;
pFrame->ShowWindow(m_nCmdShow);
pFrame->UpdateWindow();
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CFirstApp message handlers
The message response functions, where most of the action takes place,
provide our first look at some Fastgraph functions. Note that our
program does not explicitly call the message response functions.
Instead, they are called by CWinApp::Run() in response to events
such as creating or resizing the window. Our program's MainFrame.cpp
file includes message response functions for the WM_CREATE, WM_PAINT,
WM_SETFOCUS, WM_SIZE, and WM_DESTROY messages, among others.
MainFrame.cpp is shown here:
// MainFrame.cpp : implementation of the CMainFrame class
//
#include "stdafx.h"
#include "First.h"
#include "MainFrame.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CMainFrame
IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_SIZE()
ON_WM_SETFOCUS()
ON_WM_QUERYNEWPALETTE()
ON_WM_PALETTECHANGED()
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction
CMainFrame::CMainFrame()
{
m_hDC = NULL;
m_hPal = NULL;
m_hVB = -1;
m_cxClient = 0;
m_cyClient = 0;
}
CMainFrame::~CMainFrame()
{
if (m_hVB >= 0)
{
fg_vbclose();
fg_vbfree(m_hVB);
}
fg_vbfin();
if (m_hPal)
{
DeleteObject(m_hPal);
m_hPal = NULL;
}
if (m_hDC)
{
::ReleaseDC(m_hWnd, m_hDC);
m_hDC = NULL;
}
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CFrameWnd::PreCreateWindow(cs))
return FALSE;
cs.style = WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE;
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
cs.lpszClass = AfxRegisterWndClass(CS_OWNDC|CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
LoadCursor(NULL,IDC_ARROW), NULL, NULL);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const
{
CFrameWnd::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
BOOL bRet = CFrameWnd::OnCreateClient(lpcs, pContext);
if (bRet)
{
m_hDC = ::GetDC(m_hWnd);
fg_setdc(m_hDC);
m_hPal = fg_defpal();
fg_realize(m_hPal);
fg_vbinit();
m_hVB = fg_vballoc(640,480);
fg_vbopen(m_hVB);
fg_vbcolors();
fg_setcolor(19);
fg_fillpage();
}
return bRet;
}
void CMainFrame::OnPaint()
{
CPaintDC dc(this); // device context for painting
fg_vbscale(0,fg_getmaxx(),0,fg_getmaxy(),0,m_cxClient-1,0,m_cyClient-1);
}
void CMainFrame::OnSize(UINT, int cx, int cy)
{
m_cxClient = cx;
m_cyClient = cy;
}
void CMainFrame::OnSetFocus(CWnd* pOldWnd)
{
OnQueryNewPalette();
}
BOOL CMainFrame::OnQueryNewPalette()
{
fg_realize(m_hPal);
Invalidate();
return TRUE;
}
void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd)
{
if ((pFocusWnd != this) && (!IsChild(pFocusWnd)))
OnQueryNewPalette();
}
Windows generates a WM_CREATE message when it first creates the
program's window. Only one WM_CREATE message typically occurs per
program instance, so it is a good place for any application-specific
initialization code. In an MFC program, the CMainFrame::OnCreateClient() message response function serves as the WM_CREATE handler. Our OnCreateClient() function begins by calling the base class OnCreateClient() function:
BOOL bRet = CFrameWnd::OnCreateClient(lpcs, pContext);
If successful, it calls the Windows API function GetDC() to obtain a device context to the window's client area, and then fg_setdc() to make the device context available to other Fastgraph functions:
m_hDC = ::GetDC(m_hWnd);
fg_setdc(m_hDC);
Note how we use the :: global scope resolution operator to guarantee that we call the Windows API version of GetDC(). We'll use this technique whenever we call a Windows API function in an MFC program. Next, CMainFrame::OnCreateClient() creates and realizes the default logical palette:
m_hPal = fg_defpal();
fg_realize(m_hPal);
CMyWindow::OnCreate() then initializes Fastgraph's virtual buffer
environment, creates a 640x480 virtual buffer and makes it the active
virtual buffer, and assigns the logical palette colors to the virtual
buffer:
fg_vbinit();
m_hVB = fg_vballoc(640,480);
fg_vbopen(m_hVB);
fg_vbcolors();
Finally, we fill the virtual buffer with blue pixels (color 19 is blue
when using Fastgraph's default 256-color virtual buffers with the
default logical palette):
fg_setcolor(19);
fg_fillpage();
Windows generates a WM_PAINT message when the window's client area must be repainted. In an MFC program, the CMainFrame::OnPaint() message response function serves as the WM_PAINT handler. Our CMainFrame::OnPaint() function does little more than call fg_vbscale() to display the contents of the 640x480 virtual buffer scaled to the size of the client area.
Windows generates a WM_SETFOCUS message when the window gains the input
focus. This most often happens when the window becomes the active or
top-level window. In an MFC program, the CMainFrame::OnSetFocus() message response function serves as the WM_SETFOCUS handler. Our CMainFrame::OnSetFocus() function merely calls CMainFrame::OnQueryNewPalette(), which first calls fg_realize() to activate the program's logical palette (in case another program has changed the logical palette colors), then calls the Invalidate() member function to force a WM_PAINT message to redraw the client area.
Windows generates a WM_SIZE message whenever the size of the window
changes, and also upon creation of a window. In an MFC program, the CMainFrame::OnSize() message response function serves as the WM_SIZE handler. Our CMainFrame::OnSize()
function simply saves the new width and height of the client area (in
pixels) in the member variables m_cxClient and m_cyClient. These
quantities are passed to fg_vbscale() in CMainFrame::OnPaint().
Windows generates a WM_DESTROY message after removing a window to signal
a program exit. In an MFC program, the CMainFrame destructor serves as
the WM_DESTROY handler. The destructor first closes the virtual buffer,
releases its memory, and terminates virtual buffer processing:
if (m_hVB >= 0)
{
fg_vbclose();
fg_vbfree(m_hVB);
}
fg_vbfin();
It then calls the DeleteObject() member function to delete the logical palette created with fg_defpal(), and the Windows API function ReleaseDC() to release the device context created with GetDC():
if (m_hPal)
{
DeleteObject(m_hPal);
m_hPal = NULL;
}
if (m_hDC)
{
::ReleaseDC(m_hWnd, m_hDC);
m_hDC = NULL;
}