Visual Studio IDE Refinements
If you are a Visual C++ 6.0 developer and this is your first experience with Visual Studio .NET, be ready for a surprise. The IDE has an all-new look and feel, and for the first couple of uses the IDE will not likely behave in the manner you expect. If you are already familiar with Visual Studio .NET 2002, there are a number of refinements to the IDE that are intended to make your time spent here a bit more comfortable and productive.
Creating a Windows Forms-based application in Visual C++ .NET 2003 is simple: Select File | New | Project | Visual C++ Projects | Windows Forms Application (.NET). Supply a project name (see Figure 1
), and Visual Studio will generate the project files (together known as a Solution, akin to a Workspace.)
|Figure 1: Visual Studio .NET (Everett) showing support for C++ -based Windows Forms.|
For the ".NET" project types, including this Windows Forms application, it is important to understand that the environment will automatically set the /CLR compiler switch. This switch instructs the compiler to emit Intermediate Language instruction code and build an assembly rather than a traditional Win32 binary. You can further control the granularity of IL code generation within the project by using #pragma managed
and #pragma unmanaged
With the project files created, you can begin to drag and drop controls and components from the Toolbox onto the surface of the form. This is eerily like Visual Basic, but not to fear: C++ code is behind the form; trust me. (You'll see this in a moment.) Each control you place on the form has properties that you can set using the Properties window. Certain controls, like tab controls, are much easier to use on Windows Forms than they ever were in MFC. For example, with a tab control you can use the designer to add new tabs and pages, and controls on each individual page, all without having to manually write any code.
The first thing to learn about the C++ support in the Windows Forms Designer is that it will always generate (spit) the code that defines and implements the form into a .h file. In the example so far you can see a default form with source files Form1.h
. The only code that lives in Form1.cpp
by default is the entry point to the application, and it looks something like this:
using namespace CoDe;
int APIENTRY _tWinMain(HINSTANCE hInstance,
This code is actually quite interesting, if only because it will look very familiar to C++ developers and rather foreign to C# developers. You can see that windows.h
is included. Note that the entry-point is a traditional Windows application entry-point (_tWinMain
), and it accepts standard Windows arguments. But this is a managed function, meaning that the compiler is generating MSIL for it, and it can call or consume managed objects. Which is exactly what it does: The first thing it does is set the threading model (sound familiar, COM developers???), and the second thing it does is start the application running. Application::Run()
is a static method on the .NET Framework-provided Application class, and passing an instance of Form1 has the effect of adding an event handler for the form's Closed
event. When Closed
is fired, the event handler calls ExitThread()
to shut down the application.
In C# and VB.NET the threading model is specified as an attribute to the Main()
entry-point to the application. While attributes are fully supported by C++, setting the threading model is a bit of a special case because of the more diverse set of application-start up scenarios.
The form itself is implemented in a .h
file. Some C++ developers are likely to grumble at thisfor certain, C++ is all about separation of type definition and implementation. Suffice it to say that this detail aspect of the Designer may change in the future.
The form itself is a managed C++ class that is singly derived from the .NET Framework class System::Windows::Forms::Form. (Multiple inheritance is not supported for managed typeseven in C++). This is very much akin to how one might derive from CDialog to create a dialog in MFC.
Managed C++ classes are different from normal C++ classes in a number of ways. First and foremost, they have a designating syntax. To denote a class as managed, it must be decorated with __gc
, as well as access specifier (public, private, etc.):
public __gc class Form1 : public System::Windows::Forms::Form
means garbage collected, which is really the key behavioral property of the type. When you instantiate a __gc
class (always with new
, never as a stack-based or inline object), you don't have to call delete to free it. So, looking back at the _tWinMain()
entry point in the example, it is NOT a bug that delete is never called on the Form1 instance.
If you investigate further, the C++ code "behind the form" (this is correct terminology, denoting the code that implements the form), you'll notice a few interesting aspects. (To view the .h
file for the form, click the "View Code" button in the upper-left of the Solution Explorer window, with the Form1.h
file highlighted.) The first thing to note is a method called InitializeComponent()
is called from the constructor of the form. It exists as a method where the Forms Designer (the piece of Visual Studio where you visual design the form) can deposit the majority of code that instantiates and lays-out (initializes) the controls on the form. MFC and Win32 developers may find this a little odd because in those environments there was little to do in the application code itself to initialize a dialog; the base class simply loaded a corresponding Dialog Resource and took care of creating and positioning all of the Windows and ActiveX controls on behalf of the developer.