Hour 3: Writing a Simple C++
.NET Program
Inevitably every programming book and programming course has you create as
your first applicationa simple "Hello World" program. Therefore,
this hour's lesson is dedicated to writing your first simple application
with Visual C++ .NET.
The difference is, this lesson already assumes you know how to create a
simple application and are at least somewhat experienced with the previous
version of Visual C++. With that assumption, the lesson will take you through
creating two applications: one with MFC and one using the .NET Framework. This
allows you to compare the two methods of programming Windows applications with
Visual C++ .NET.
In this hour you will learn:
How to build a simple application with MFC
How to build a managed application with the .NET Framework
How to study and understand the differences between the two types of
applications
Building an MFC Application
Building an MFC application with Visual Studio .NET is very similar to the
way it was done with previous versions of Visual Studio. The Application Wizard
is available to allow you to customize your settings, although it has a
different look.
As usual with Visual Studio, first select the New, Project menu option to display
the New Project dialog, as shown in Figure 3.1.
Select the MFC application type and name the application HelloMFC.
Figure 3.1 New Project
Window in Visual Studio .NET for an MFC application.
Pressing the OK button in the New Project dialog brings up the MFC Application
Wizard, shown in Figure 3.2. This wizard
is no longer a linear process; you can now select the different sections on
the left side of the wizard page and directly access the settings you want to
change. The differences between this and the Application Wizard for MFC applications
in the previous version of Visual Studio are mainly visual and navigational.
Select the Application Type section in the wizard and change the settings to
match those shown in Figure 3.3. These selections
will result in creating a dialog-based application when you click the Finish
button.
The first thing you will see after dismissing the settings dialog is the
dialog editor. If your dialog requires any controls, you can easily select them
from the toolbox displayed on the left. For this project, however, we are just
going to change the controls that are already present.
Figure 3.2 The MFC
Application Wizard.
Figure 3.3 Dialog-based
application settings for the HelloMFC application.
First of all, select the button labeled OK. On the
right side of the IDE, you should see a window titled Properties. This is a
departure from the old way of doing things in Visual C++, which required editing
properties through small tool windows. Properties are now categorized and always
displayed without having to select a menu item or click a toolbar button. Change
the property value labeled Caption to Message by typing the new value in
and pressing Enter. You should see the text on the button change as you do
this.
Now you need to change the control ID of the static text control. Select the
static text control located in the middle of the dialog in the dialog editor and
change the ID property to the value IDC_MESSAGE.
Figure 3.4 HelloMFC
dialog template.
Assign a member variable to the static text control by right-clicking the control
and selecting Add Variable from the context menu. The Add Member Variable Wizard,
shown in Figure 3.5, is displayed. Edit the
member variable properties, as shown, and finish the wizard.
Figure 3.5 The Add
Member Variable Wizard for the MFC application.
One final step in the dialog editor is to double-click the
Message button to add a handler for when the button is clicked. This is a nice
new feature added to Visual Studio .NET. When you double-click objects within
the dialog editor and other form editors, the most appropriate message map entry
and method definition is added to your application. In this case, the
ON_BN_CLICKED message map entry and the
CHelloMFCDlg::OnBnClickedOK() method are added. Because this button is
the OK button, you could have overridden the OnOK() method to handle
the selection; however, this method works for all buttons and other control
types.
Change the OnBnClickedOK() method to what is shown in the following
code to set the message text to a message:
void CHelloMFCDlg::OnBnClickedOk()
{
m_MessageST.SetWindowText("Hello from the world of MFC.");
}
Compile and run the HelloMFC application by selecting the Debug, Start menu
item from the menu bar or by pressing the F5 key. Clicking the Message button
within the application displays your message, as shown in Figure
3.6.
Figure 3.6 Executing
the HelloMFC application.
If
you are already familiar with building MFC applications in previous versions of
Visual Studio, the HelloMFC application should have been simple for you to
create within Visual Studio .NET. What you've created, however, is an
unmanaged C++ project. This application has nothing to do with .NET and, as
such, does not run within the .NET runtime. Any memory allocation, versioning
issues, and other things that benefit .NET applications will have to be done
manually using this type of project. To run in the .NET runtime, you need a
managed C++ application.
Building a Managed .NET Framework Application
Switching gears to the .NET Framework and building the same type of
application as a managed C++ application is a bit different, as you will see.
The first step is the same: Select to build a new project from the main menu,
only this time select Managed C++ Application as the type and name the
application HelloNET.
Notice that you don't receive any wizard to ask what settings you would
like to have in your application, as you did with the MFC application. The C++
.NET applications are generated with a script that builds a basic application
framework that is a console "Hello World" application. One difference
between Visual C++ .NET and the other .NET languages is that there is no form
editor for Visual C++ .NET to edit Windows Forms, the basis of building user
interfaces in .NET. Although it may seem daunting at first having to hand-code
the user interface, you'll find that working with the .NET Framework
Forms classes is rather intuitive.
Tip - If you want to learn C# also, you
can actually use a dummy application in C# that does provide the Windows Form
editor to build your forms and then port the code into VC++ .NET. You have to
know how to do the porting, but it isn't that difficult with a little
practiceand it saves time on designing your forms.
The next step is to declare a class that represents the form for the
application. Select Project, Add Class from the main menu. In the list of
available templates in the dialog that is displayed, select the Generic C++
Class template and click the Open button. Name the class CHelloNETForm
and specify the base class Form, the .NET Framework's class found
in the System::Windows::Forms namespace, which provides the Windows
Form functionality. You may get an error message explaining that Visual Studio
.NET cannot find the Form base class. Click Yes to continue adding the
class. Performing the previous steps sets up a new file and skeleton code for
your class. The next steps will transform your generic class into a managed C++
.NET class.
Click the Class View tab next to the Solution Explorer tab on the right side
of the IDE window. Expand the project tree and locate the Classes item and
expand that also. You should now see your CHelloNETForm class.
Right-click the class and select Add, Add Variable from the context menu. You
will now be presented with the Add Variable dialog. Set the access to protected,
the variable type to Button*, and the variable name to
m_pbtnMessage. Then click Finish. Add another Button* variable
named m_pbtnDone and a Label* variable named
m_pstMessage.
The variables you just added are .NET Framework classes within the
Forms namespace. Controls, however, need to have a container object to
hold them. Add another variable of type
System::ComponentModel::Container* with the name m_pComponents
and the same protected access level.
Now that you've added the member variables, its time to add some member
functions. Right-click the CHelloNETForm class again, but this time
select Add, Add Function. Enter void as the return type,
InitForm as the function name, and an access level of protected. Click
Finish to close the dialog.
The last step to finish the design of the class is to add the necessary
elements that are not supported by wizards within the IDE. Using Listing 3.1 as
a guide, add the appropriate using statements and add the __gc
keyword immediately preceding the class keyword. This indicates to the
compiler that the class is managed by the .NET Framework and its memory
manager.
When you are finished, you're HelloNETForm.h file should look
similar to Listing 3.1.
Listing 3.1 Transforming a Generic C++ class into Managed Code
1: #pragma once
2:
3: #using <mscorlib.dll>
4:
5: #using <System.DLL>
6: #using <System.Drawing.DLL>
7: #using <System.Windows.Forms.DLL>
8:
9: using namespace System;
10: using namespace System::Windows::Forms;
11:
12: __gc class CHelloNETForm : public Form
13: {
14: public:
15: CHelloNETForm(void);
16: ~CHelloNETForm(void);
17: protected:
18: Button* m_pbtnMessage;
19: Button* m_pbtnDone;
20: Label* m_pstMessage;
21: System::ComponentModel::Container* m_pComponents;
22: void InitForm();
23: };
Now it's time to fill in the functions you just created. Refer to
Listing 3.2 as you read through this section. To begin with, try and compile
your project. One thing you'll notice is that the compiler complains that
NULL is undefined. One great addition to Visual C++ .NET is that when
you're adding member variables to a class like we did earlier, the
generated code also includes class initializers for these variables. In this
case, because we declared pointers, the generated code set these variables to
NULL. However, it didn't add the appropriate #include statement.
To fix this problem, include the header file stdlib.h at the top of the
HelloNETForm.cpp file. Your program should now compile with zero errors
and zero warnings.
Begin by filling in the constructor code. Because the member variables you
added are simply pointers, you need to create the objects before you can use
them. Therefore, create the form control container member variable
(m_pComponents) by using the C++ keyword new. The remaining
line in the constructor calls the InitForm function.
In the InitForm() function, create the three controls, one by one,
and set the properties appropriately. After all the controls are created, they
are added to the form with the Form::Controls->Add() method. The
order in which the controls are added has an effect on the tab order if the tab
order isn't specified for the controls. In this case, it is specified and
therefore doesn't matter.
Listing 3.2 Creating the Form
1: #include "stdafx.h"
2: #include "hellonetform.h"
3: #include <stdlib.h>
4:
5: #using <mscorlib.dll>
6: CHelloNETForm::CHelloNETForm(void)
7: : m_pbtnMessage(NULL)
8: , m_pbtnDone(NULL)
9: , m_pstMessage(NULL)
10: , m_pComponents(NULL)
11: {
12: m_pComponents = new System::ComponentModel::Container();
13:
14: // Initialize the Form
15: InitForm();
16: }
17:
18: CHelloNETForm::~CHelloNETForm()
19: {
20: }
21:
22: void CHelloNETForm::InitForm()
23: {
24:
25: // Allocate the controls
26: m_pbtnMessage = new Button();
27: m_pbtnDone = new Button();
28: m_pstMessage = new Label();
29:
30: SuspendLayout();
31:
32: // Initialize all control properties
33: //
34: // Message Button
35: //
36: m_pbtnMessage->Location = System::Drawing::Point(240, 8);
37: m_pbtnMessage->Name = "Message";
38: m_pbtnMessage->TabIndex = 0;
39: m_pbtnMessage->Text = "Message";
40: m_pbtnMessage->add_Click(
41: new System::EventHandler( this, &CHelloNETForm::OnMessageClick ) );
42:
43: //
44: // Done Button
45: //
46: m_pbtnDone->Location = System::Drawing::Point(240, 40);
47: m_pbtnDone->Name = "Done";
48: m_pbtnDone->TabIndex = 1;
49: m_pbtnDone->Text = "Done";
50: m_pbtnDone->add_Click(
51 new System::EventHandler( this, &CHelloNETForm::OnDoneClick ) );
52: //
53: // Message Label
54: //
55: m_pstMessage->Location = System::Drawing::Point(8, 8);
56: m_pstMessage->Name = "Label";
57: m_pstMessage->Size = System::Drawing::Size(192, 40);
58: m_pstMessage->TabIndex = 2;
59: m_pstMessage->Text = "";
60:
61: // Set form properties and add controls to form
62: //
63: // Form1
64: //
65: AutoScaleBaseSize = System::Drawing::Size(5, 13);
66: ClientSize = System::Drawing::Size(322, 87);
67: Controls->Add( m_pstMessage );
68: Controls->Add( m_pbtnDone );
69: Controls->Add( m_pbtnMessage );
70:
71: FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog;
72: Name = "HelloNET";
73: Text = "HelloNET";
74:
75: ResumeLayout(false);
76: }
In order for your form to respond to user events, you'll need to capture
the button click events. Events that were previously handled with the MFC
message map are handled quite differently in the .NET Framework. Events are
handled by delegates within your class. A delegate is quite similar to a C/C++
function pointer. You assign delegates to handle object events with the
following statement:
m_pbtnMessage->add_Click(
new System::EventHandler( this, &CHelloNETForm::OnMessageClick ) );
Every time the Message button is clicked, the
CHelloNETForm::OnMessageClick() method is called. In your project, you
will add two event handlers to the two buttons.
Event handlers must have a specific set of parameters, much in the same way
they did for MFC message map handlers. For most events, pointers to a generic
Object and an EventArgs object are passed as parameters. The
generic Object pointer is a pointer to the object that caused the
event, whereas the EventArgs object contains information specific to
the event. Following the instructions given earlier, add two member functions.
The first function is named OnMessageClick, and the second function is
named OnDoneClick. Both functions have a void return type, protected
access level, and two parameters: Object* source and EventArgs*
e. Listing 3.3 shows the final version of the HelloNETForm.h
file.
Listing 3.3 Adding Event Handler Declarations
1: #pragma once
2:
3: #using <mscorlib.dll>
4:
5: #using <System.DLL>
6: #using <System.Drawing.DLL>
7: #using <System.Windows.Forms.DLL>
8:
9: using namespace System;
10: using namespace System::Windows::Forms;
11:
12: __gc class CHelloNETForm : public Form
13: {
14: public:
15: CHelloNETForm(void);
16: ~CHelloNETForm(void);
17: protected:
18: Button* m_pbtnMessage;
19: Button* m_pbtnDone;
20: Label* m_pstMessage;
21: System::ComponentModel::Container* m_pComponents;
22: void InitForm();
23: void OnMessageClick( Object* source, EventArgs* e);
24: void OnDoneClick( Object* source, EventArgs* e);
25: };
Now open the HelloNETForm.cpp file. The two functions you just added
will be at the bottom of the file. In OnMessageClick, set the
m_pstMessage Text property to "Hello from the .NET World."
For the OnDoneClick function, call the function Close, which
shuts down the application. The two functions should appear similar to the
following:
1: void CHelloNETForm::OnMessageClick( Object* source, EventArgs* e )
2: {
3: m_pstMessage->Text = "Hello from the .NET World.";
4: }
5:
6: void CHelloNETForm::OnDoneClick( Object* source, EventArgs* e )
7: {
8: Close();
9: }
The final change to the application is to have the main() function
create and run the new Windows Form class. Make the changes shown in the
following code segment:
#include "helloNETform.h"
int _tmain(void)
{
Application::Run(new CHelloNETForm());
return 0;
}
With those final additions, the application is ready to compile and run. This
is the same as with the MFC application. Simply press the F5 key or select the
Debug, Start menu command. The resulting Visual C++ .NET application should have
the same appearance as the MFC application created earlier.
Comparing the Differences
Although the process of building the MFC application was more automated with
wizards and form designers, the resulting code of the two applications shows
that the MFC application is much more complex. If you take into consideration
that the .NET application has no resource file to describe the Windows Form and
remove the form definition from the comparison, the .NET application is
significantly smaller and even easier to read.
Looking at a few of the major differences between the applications shows a
fundamental distinction in the way a .NET application is developed versus how an
MFC/Win32 application is developed.
The first main difference is that an MFC Windows application always starts
with a CWinApp derivative. The InitInstance() method is
overridden and provides the startup initialization for the application. By
contrast, the .NET application doesn't require an application class. The
.NET application's entry point is the main() function, whereas an
MFC/Win32 application's entry point is a WinMain() function
encapsulated within the MFC library.
Another major difference you should notice while looking at both applications
is that the .NET application does not delete anything it allocates with
new. This is because the .NET Framework frees all objects once they are
no longer referenced. This is done by the garbage collector automatically. This
eliminates the problems of memory leaks in your applications.
Finally, the way that events are handled is quite different between the two
applications. With MFC, a message map entry is added to the class's message
map, which maps an event or Windows message to a class function. In the .NET
Framework, there is no message map; therefore, each object has events associated
with it to which you can attach an event handler that is called when the events
occur.
For a further comparison of the two applications, look at the code for the
applications on the accompanying CD and compare them in more detail. In the end,
you should find the .NET application cleaner and easier to work with than the
MFC application.
Summary
In this hour you created two applications that perform the same task. One
application was created with MFC and the other with the .NET Framework. Both
application display a hello message when a push button is clicked. You also
learned about the differences between the two application implementations.
Being familiar with how things are implemented in .NET as compared to MFC
will help you in making better decisions when the time comes to convert legacy
applications to the .NET platform.
Q&A
Q Is using Windows Forms the only way to produce a user interface
within .NET?
A Windows Forms represent windows of all types, not just dialogs.
They can be MDI frame windows, pop-up windows, tool windows, and so on. They are
the only means within the .NET Framework to represent a Windows-based user
interface.
Q Is it possible to create a dialog in MFC and use it from a .NET
application?
A Yes, it is possible to do so. In fact,
in Hour 5, "Understanding Managed Versus Unmanaged Code," there is a
lesson on mixing managed (.NET Framework) code with unmanaged (MFC/Win32) code
that will help you understand how this is done.
Workshop
The Workshop provides quiz questions to help solidify your understanding of
what was covered in this chapter. Answers are provided in Appendix A, "Quiz
Answers."
Quiz
What is the base class for a Windows Form?
How is a delegate related to an event? How are events
different in MFC versus the .NET Framework?
© Copyright Pearson Education. All rights reserved.
|