devxlogo

A Preview of Visual C++ .NET 2003

A Preview of Visual C++ .NET 2003

y the time you are reading this Microsoft will be very close to shipping Visual Studio .NET 2003 and its associated language products?including Visual C++. February 2003 marks an exciting anniversary for this mainstay developer tool: it is now 10 years since the product’s original launch as Visual C++ 1.0 in 1993. A lot has changed in those 10 years (including the name, which since the last release is officially ‘Visual C++ .NET’), and I’m happy to take this opportunity to show you some of the cool updated features in the latest and greatest version.

Everett
Perhaps you know the pending release by its Microsoft codename, Everett. Everett involves a lot of things for a lot of different people; indeed, Visual Studio meets the demands of an awfully diverse set of developers. For the Visual C++ Product Team, Everett represents a major update to our favorite code base, one in which we’ve enabled several significant features representing literally years of development effort. From the outset our primary goal has been to make you, a developer and user of the tool, as well as the larger C++ community, be pleasantly awed by the significance of this release.

Lay on the Proof
There are four primary categories of features I’m going to discuss in this article: ISO C++ Conformance, Optimizations, Code Security, and Windows Forms. Each has huge implications in the code you’ll write, but that said, it is difficult to rank the categories in order of importance or significance for all C++ developers combined. That C++ enables you to undertake so many different types of development means that some of you will have specialized in areas that aren’t serviced by a particular feature.

One feature we all share in common is the language syntax itself. One of the loudest complaints I’ve heard over the years about Microsoft’s C++ compiler is that it is absolutely lousy at compiling C++ code that uses advanced template features. (Some well-known C++ gurus have even goaded me with comments such as, “Visual C++ isn’t even a C++ compiler!”) Not only does this deter developers at large from writing somewhat portable C++ code, it precludes those developers from using some of the more sophisticated features the language has to offer. Additionally, the MS compiler prevents developers from compiling and using some of the more interesting and powerful C++ libraries that have emerged in the last couple of years.

ISO C++ Conformance
Visual C++ 6.0 conforms to the ISO C++ Standard at a roughly 80% level. It lacks support for advanced template features, and incorrectly implements certain core features such as For-Loop scoping. Visual C++ .NET 2002 (the release of one year ago) elevates the conformance number to roughly 90%, which is great but clearly still short of a world-class C++ compiler.

Visual C++ .NET 2003 passes several popular compiler test suites with a mark of 98% or better.

Visual C++ .NET 2003 passes several popular compiler test suites with a mark of 98% or better. It implements the entire ISO C++ Standard save for a few relatively controversial features including export and exception specifications. Dependent name lookup also remains absent, (Microsoft has plans to implement this in a later release). However, a 98% score means that a Visual C++ developer can now build popular modern C++ libraries like Boost, Blitz, and Loki, sans code changes?a feat few compilers can assert (pun intended).Additionally, Visual C++ developers can now structure their own code to benefit from partial template specialization, partial ordering of function templates, user-defined conversion templates, and more. (See Sidebar: C++ and the .NET Framework)

Compiler Optimizations
Most C++ developers will tell you that one reason they choose C++ is its ability to deliver raw performance. One reason for this is that C++ compilers are very good at turning C++ syntax into highly optimized binary code. One of Visual C++’s strengths has always been that it has a very good optimizer.

Note that the core optimizations are only available in the Visual C++ packaged with Visual Studio .NET Professional and better products. Visual C++ .NET Standard, and the .NET Framework SDK continue to ship with an optimizer-crippled compiler.

Visual C++ .NET delivered a core new optimization called Whole Program Optimization (WPL). Invoked with a compiler switch (/GL), WPL enables the compiler a second chance to optimize after the linker has done an initial pass on all the object files in the project. Normally, compilers are only able to optimize within the module that they are presently compiling. WPL technology enables the compiler to see the entire program at once and apply optimizations on a global scale, resulting in real performance gains (up to 10% in real world code). Be warned?WPL eats memory, and in some cases may slow the compilation/link process.

Microsoft enhanced some of the existing optimization heuristics for Everett, including Whole Program Optimization. For example, during WPL the compiler can remove dead parameters from function calls. If a parameter isn’t used by a given function call, it is optimized away entirely.

But the real excitement in Everett for optimizations comes from two new switches: /G7 and /arch:SSE(2).

The compiler switch /G7 is the long awaited “optimize for Pentium 4 / AMD Athlon” switch. Throwing this switch won’t prevent programs from executing on lesser processors, but it will make them run noticeably faster on the newer silicon. In practice, and depending on the amount of floating-point-centric code, a /G7 compiled program will run between 5 and 10% faster.

Note: If you are targeting your program at a customer base that is still largely Pentium III or older, that the (/G7) code will run slightly slower on those machines, than if you had used the /G6 switch, which produces optimizations blended for PII and PIII.

If you write heavy-duty floating point code, take note of the /arch:SSE and /arch:SSE2 switches. Throwing these will prevent your code from executing on chips lacking the specified Streaming SIMD Extension (SSE) family of instructions, but will enable the compiler to generate code that takes advantage of the additional capabilities. The gain seen in run-of-the-mill C++ code is negligible, but in certain cases floating-point code will see a 4-5% improvement.

Code Security and Robustness
C++ has made the evening news several times in the past several years. And not because it is rescuing kittens from trees, keeping children from villains, or for somehow being instrumental in the quest for world peace. Because a majority of real-world code is written in C++, that code is often the target of malicious attacks. That’s right; software viruses often attack programs written in C++.

One of the key reasons attacks are possible is the all too ubiquitous buffer overrun. C++ largely places the onus of ensuring program correctness on the programmer, and a common error is writing code that miscalculates the amount of data being copied into a buffer. Managed runtimes generally prevent this type of behavior at the expense of maximum speed (these environments must validate at runtime the length of the data and the size of its destination).

A lot of C++ developers are unaware as to why buffer overruns offer marauders such an attractive means of attack. I know I didn’t until I read Writing Secure Code by Michael Howard and David Leblanc (Microsoft Press, ISBN: 0735615888). What an eye opener! I highly recommend reading this book. To be honest it made me shiver when I considered all of the dramatically non-secure code I’ve written in the last 10 years.

In its simplest form, a buffer-overrun attack works like this: the attacker sends a malicious data packet to the program in the guise of normally expected data input (perhaps the program processes incoming e-mail headers, so the data is a ‘bad’ e-mail header). Because the program doesn’t properly bounds-check the incoming data (a bug), when it performs an unchecked copy of the input string into a stack-allocated buffer, it corrupts the values of variables allocated on the stack around the buffer. But the corruption is far from random?attackers carefully analyze the target program (victim) and craft input to overwrite the program’s data with known values. For example, viruses may attempt to overwrite a value on the stack defining where the program should jump should an exception occur, with the address of the attack code (also delivered in the malicious input). Causing the program to trigger an exception is often trivial (more decisively placed bad data), and once that occurs the malicious code executes and takes over.

So, why am I digressing into this explanation? Well, to scare you. Be afraid?be very afraid.

Fortunately, protecting your code isn’t an impossible prospect.

Fortunately, protecting your code isn’t an impossible prospect. It does require work on your part, and perhaps an adjustment to your coding style. Visual C++ can help too, with a set of features introduced in Visual C++ .NET and improved upon in Everett.

Runtime Error Checks (/RTCn) and Compiler Buffer Security Checks (/GS) are features in the compiler that help improve the robustness of your code. Runtime Checks are intended to aide during the debug/test process in identifying common programming errors, including buffer overruns and stack corruption. Buffer Security Checks are intended for you to use in release builds, and offer a level of protection against attacks like the one I described above. But take note: this technology is intended only to make a severe situation less severe. /GS doesn’t prevent attacks, nor does it detect all types of attacks. What it will do is shut down a program when it detects a known type of attack. That is to say, /GS won’t detect or protect against all possible attacks.

Microsoft introduced /GS in Visual C++ .NET, and Microsoft improved /GS in Everett with an expanded knowledge of attack types. When you specify /GS, the compiler injects code into functions it decides are at risk. This code checks the value of a random ‘cookie’ to see if it has been subverted. Developers should always use /GS?the overhead produced when this technology is used is slight?roughly 9 instructions per function (and not all functions require this instrumentation). Programs compiled with Everett, (whether they specify /GS or not, actually) also cooperate with the underlying operating system (Windows .NET Server and better) to further protect against certain types of attacks. (See Sidebar: Enhanced Debugging)

Windows Forms
Visual Studio .NET introduced the developer community to a new thick-client technology called Windows Forms. This technology is rooted in the .NET Framework as a set of classes that enable developers to write robust GUI code more easily. Because they are .NET managed-runtime classes, any language capable of targeting the .NET CLR can, in theory, be used to implement Windows Forms.

In practice, however, no one writes Windows Forms code by hand. At least, not the code that describes the all important user interface layout. Rather, Visual Studio includes a designer that enables you to drag-and-drop controls onto a form, and then ‘spits’ out the tedious code that implements that form. In Visual Studio .NET, the Windows Forms designer didn’t know how to ‘spit C++’. In Everett it does, thanks in part to remedial programming lessons. This is a great feature for C++ developers, enabling them to continue writing GUI code in the syntax they’re familiar with.

Windows Forms programming is different than programming with MFC, or even WTL.

Windows Forms programming is different than programming with MFC, or even WTL. Gone are message-maps, Windows-based resource files, command handlers, and more. In many ways the classes that comprise Windows Forms (found in the System::Windows::Forms namespace of the .NET Framework) offer both more and less capability than MFC.

The .NET Framework as a whole offers much more capability than MFC, but certain technologies such as command routing or MDI support are not yet as refined in the .NET Framework as their MFC counterparts. You may find yourself implementing code that wouldn’t have been necessary in MFC, but the converse is also true. Keep in mind also that you may be able to mix-and match technologies; in Visual C++ you can use both managed and unmanaged code in the same application.

Let’s take a moment to build and examine some C++ Windows Forms code using Visual Studio .NET 2003.

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 directives.

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 and Form1.cpp. The only code that lives in Form1.cpp by default is the entry point to the application, and it looks something like this:

   #include "stdafx.h"   #include "Form1.h"   #include    using namespace CoDe;   int APIENTRY _tWinMain(HINSTANCE hInstance,   HINSTANCE hPrevInstance,   LPTSTR    lpCmdLine,   int       nCmdShow)   {   System::Threading::Thread::CurrentThread->ApartmentState =   System::Threading::ApartmentState::STA;   Application::Run(new Form1());   return 0;   }

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 this?for 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 types?even 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   {   public:   ...   };

__gc 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().

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.

Using Resource Files
There are no .rc files for Windows Forms. This correctly implies that resources for Windows Forms are not data-based, but rather, are largely code-based. The InitializeComponent() method contains all of the Designer-generated code that instantiates, initializes, and positions all of the controls on the form?manually.

In more advanced scenarios, such as those involving localization or data-centric objects on the form itself (like a bitmap), a resource model is used. .ResX files are XML files that exist for each form type and are compiled into .resource files that are incorporated into the assembly (.dll or .exe) by the compiler. The InitializeComponent() method will instantiate a ResourceManager object to retrieve the values stored in the XML, and is used to initialize the form’s properties and sub-objects (rather than hard-coding these values in code).

It is a simple matter to witness this. The default code spit out by the designer for the empty form looks something like this:

   void InitializeComponent(void)   {   //   // Form1   //   this->AutoScaleBaseSize = System::Drawing::Size(5, 13);   this->ClientSize = System::Drawing::Size(292, 266);   this->Name = S"Form1";   this->Text = S"Form1";   }

If, in the Property Window for the form, you set the Localizable property to ‘True’ (default is ‘False’), the Designer emits quite a different picture:

   void InitializeComponent(void)   {   System::Resources::ResourceManager *  resources = new   System::Resources::ResourceManager(__typeof(CoDe::Form1));   //   // Form1   //   ... (lots of generated code, ommitted here) ...   this->ClientSize =   (*__try_cast<__box system::drawing::size>   (resources->GetObject(S"$this.ClientSize")));   this->Text = resources->GetString(S"$this.Text");   }

You can see that the code is using the ResourceManager to retrieve values defined elsewhere (in the XML resource). In this case, the rationale is to use values that are Localized. The single .ResX file could define values in multiple languages, for example. The ResourceManager object accesses by default the resource values that correspond to the Locale set for the executing thread.

It is generally good practice to NOT edit the code inside IntitializeComponent(). Basic changes you might make to property values WILL be reflected back inside the designer (the designer parses the code it generates for changes), but additional code you place there will in most cases be rejected with an error.

Since this form example doesn’t yet do anything, let’s add some controls.

Switch back to the Windows Forms Designer (click the View Designer button in the Solution Explorer window), then drag and drop a button from the Toolbox window onto the form. Understand that the designer just generated code “behind the form” in the .h file to implement that button. As you adjust properties on the button it will update the code.

The Properties window serves a number of purposes, and largely supplants the MFC CodeWizard from the 6.0 product. (This is true even for unmanaged C++ code you might write with Visual C++ .NET, although I’m not covering that here.) The primary purpose is to enable the developer to review and set property values on the object, but it also provides an interface to review and implement event handlers for objects.

Set the button’s Text property to something like “Click Me”. Then click the Events button in the Properties window (it looks like a lightning bolt; I suppose because most would consider a lightning-strike an ‘event’). The first event listed is the Click event, and as of yet there is no implemented handler. Double-click the word Click in the list and the designer will do two things: First, implement the event handler. Second, take you to the event handler method so you can write the code that’s executed when the event fires.

(You can also simply double-click the button itself on the form. This will also prompt the designer to create the event hander and take you to the code.)

The event handler code is simply a method called when the event occurs. The magic that binds the method to the event source (the button) is called a delegate. Think of delegates as object-oriented and type-safe function pointers. A delegate is a kind of user-definable type that you instantiate and pass to the source of the event. When the source decides to fire, it iterates through the collection of delegates it has amassed and calls the registered event handlers through function pointers. The code generated by the Designer to hook up a delegate event handler looks like this:

   this->button1->Click +=   new System::EventHandler(this, button1_Click);

In this example, Click is the event on the Button object. In C++, the += operator is overloaded to append the delegate to the event’s delegate list. You’re responsible (or rather, the designer is responsible, in this case) for allocating a new delegate (Called EventHandler) and initializing it with a reference to your handler function on a given object (this).

All Said and Done
The C++ landscape is changing. Compilers are not only becoming more ISO C++ conforming, they’re also offering dramatic new optimizations and code-robustness features. Visual C++ is now venturing deep into the realm of managed-code generation?whilst simultaneously keeping a foot firmly planted in the unmanaged world of Windows. Everett is the culmination of several years of effort by the Visual C++ team at Microsoft to facelift the product and deliver exciting new features and capabilities that will have definite impact on your coding. With Everett, you’ll be secure in knowing your language and compiler can meet the demands of any software development project.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist