Home > C#, WPF > C++/CLI/WPF

C++/CLI/WPF

The executive summary of this post is as follows:

Application().Run(%Window());

That line of code is all you need to make a blank window open in today’s Visual C++. And if you’re a veteran of the dark days of Petzold and Win32, that has to be viewed as a major improvement. (Even if that % operator does look like gibberish.)

In fact from here on in, I’ll assume you are such a veteran but are only vaguely aware of .NET.

Win32 API has a native language – C, and by extension, C++. Other languages lack the equivalent of all the .h and .lib files that are the doorway to Win32. Even C# only provides the syntax for declaring entry points, and the user has to then use that syntax to manually declare them (although community efforts like http://pinvoke.net/ have naturally sprung up to fill this gap).

And some APIs require direct manipulation of native memory just to call them. This is notoriously difficult to get right in C and C++. Try the security or Spooler or Lan Manager APIs for example. Of course C# can’t really do anything to make it easier, as every case is slightly different, but at least in C++ there is sample code to guide you, and it can be tricky to translate it into the equivalent unsafe C#.

So Visual C++ still has a place in 2008. And then there’s legacy code that needs new features. The question is, how easy is it to start from an old-style C++ app and then seamlessly mix today’s CLR libraries into it?

To test this in Visual Studio 2008, create a C++ Win32 Console Application. This serves as our example starting point, all old and crummy, and the IDE has written the crummy old main function for us like this:

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
    return 0;
}

Obviously we can include <windows.h> and do anything we like with Win32. But then we look at Petzold and see how many pages of code are needed just to get a blank window to open on the screen, and we think to ourselves: there must be an easier way.

Go to the project’s Properties, and under Configuration Properties change the Common Language Runtime support setting to have the value: Common Language Runtime Support (/clr).

Then right-click the project and choose References. Click Add Reference and choose the following magical ingredients:

  • PresentationCore
  • PresentationFramework
  • WindowsBase

Note that just by adding the references, our C++ language namespace already contains all the types we need. There’s no need to include any extra headers. But we can still use a using namespace declaration to help us to abbreviate our code, same as in standard C++. So put this between the #include statement and the _tmain function:

using namespace System::Windows;

(It has to be after the stdafx.h header, because of the weird behaviour of Visual C++ precompiled headers, sigh.)

Directly above the _tmain function, paste this magical incantation:

[System::STAThread]

This is an attribute, and it has to be there for WPF to work. Finally, we’re ready to enter a new world! The excitement is electric here at the Stroustrup Stadium.

Put this inside the _tmain function.

Application().Run(%Window());

Now hit F5. A blank window opens! When you close it, the program terminates (in fact, control returns from the Run function, so the Window behaves like a modal dialog box).

As a C++ veteran, you’d assume that Application is either a function that returns an object (or a reference to an object) or else its the constructor of a type, being used here to make a temporary instance. Well, I can tell you it’s the latter: constructor of a temporary. Same for the Window type. We construct an Application, construct a Window, and then call Application::Run, passing it… something windowy. If we tweaked the code a little, you’d understand it immediately:

Application().Run(&Window());

The % operator is very much the CLR equivalent of &. As always in C++, we distinguish between an object and a pointer-to-object. When declaring a pointer-like variable, we use ^ instead of * (and we call the variable a handle instead of a pointer, and we use gcnew instead of new to construct a free store instance). It’s almost as if a whole separate peer language has been dropped into C++, but the new language still has the characteristic flavour of the old.

So both of these have the same outcome as the original snippet:

Window w;
Application().Run(%w);

or…

Window ^w = gcnew Window();
Application().Run(w);

The analogy even extends to C++ references, which are like pointers that can only be assigned to once and then are syntactically used like objects instead of pointers. For example,

Window ^w = gcnew Window();

Application ^a1 = gcnew Application();
a1->Run(w);

Because a1 is a handle, we have to use the arrow (->) operator to call functions on it, instead of the dot, just like we do with pointers. But just as in standard C++, we can avoid that:

Application ^a1 = gcnew Application();
    
Application %a2 = *a1;
a2.Run(w);

Again, note how in the declarations, ^ is like * and % is like &. But in the usage, the syntax is just like Standard C++: asterisk to dereference, and arrow to access members – this extra bit of uniformity was suggested by Stroustrup to allow templates to be written such that they would work on pointers or handles.

The part I really like is the way destructors work, and I wish C# had the same thing (it’s currently only part of the way there, with the using statement). But anyway, to finish off the simple example, add this extra namespace declaration:

using namespace System::Windows::Controls;

Then try this code:

TextBlock b;
b.Text = "Hello, world.";

Window w;
w.Content = %b;
Application().Run(%w);

Here’s the complete source:

#include "stdafx.h"

using namespace System::Windows;
using namespace System::Windows::Controls;

[System::STAThread]
int _tmain(int argc, _TCHAR* argv[])
{
    TextBlock b;
    b.Text = "Hello, world.";

    Window w;
    w.Content = %b;
    Application().Run(%w);

    return 0;
}
Advertisements
Categories: C#, WPF Tags: ,
  1. Suparna
    September 24, 2013 at 12:06 pm

    The System::Window is not visible in the C++ application. How to resolve this?

    • earwicker
      September 29, 2013 at 8:47 pm

      Easy – there’s no such class! It’s System::Windows::Window – the using namespace directive allows all the class names to be resolved.

  1. December 2, 2011 at 11:19 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: