When I first announced that there would be a 6th edition of Programming Windows focusing on writing Metro style applications for Windows 8, a lively debate ensued on what language I should use, with the majority split pretty equally between C# and C++/CX (which is C++ with the Component Extensions for accessing the Windows Runtime).
I decided to use C# exclusively in the pages of the book, but to translate the sample programs in the book into C++/CX.
If you want to examine these sample programs before you buy the book, you can do so. Just go to the Programming Windows 6th edition page on the O'Reilly web site and click the Companion Content link at the right. It's a ZIP file that has all 81 sample programs from the first 7 chapters of the book in both C# and C++.
But C++ programmers might get more out of the code if they buy the book as well. One reason I chose C# is that I figured C++ programmers can successfully read C# better than C# programmers can read C++!
For the most part, Windows 8 Metro style applications written in C# and C++ are fairly similar. They both involve XAML files and access very many of the same classes and structures that comprise the Windows Runtime (WinRT). These classes are defined in the namespaces that begin with Windows and encompass all the user-interface elements, graphics, media, and so forth.
For lower-level tasks, the C# programmer uses a subset of .NET (namespaces that begin with System) while the C++ programmer uses the familiar C runtime library as well as the new Platform namespaces.
In short, if you want to code some sound or 3D graphics in a Metro style application, you'll need to use C++ for that job. As a C# guy, this does not make me happy. But it also tells me that I really need to maintain a fluency in C++ so that I can use the language when I need to.
Prior to about 5 weeks ago, the last time I spent any substantial time with C++ was the late-1990s when I was working on a book about bitmap graphics programming for Windows. I had decided to structure the book around extensions to MFC for bitmap support, and I was slowly discovering how awkward it was to write extensions to MFC. (That book was never completed.)
C++ has evolved some since that time, and the Component Extensions make it so that you might not even recognize the language! Here are a few things I learned in the process of converting the code in the book. (Some of this might be incomprehensible if you have no experience with .NET or the Windows Runtime.)
Here's how to instantiate and initialize a Windows 8 TextBlock in C#:
TextBlock txtblk = new TextBlock();
txtblk.Text = "Hello, Windows 8!";
txtblk.FontFamily = new FontFamily("Times New Roman");
txtblk.FontSize = 96;
txtblk.FontStyle = FontStyle.Italic;
txtblk.Foreground = new SolidColorBrush(Colors.Yellow);
txtblk.HorizontalAlignment = HorizontalAlignment.Center;
txtblk.VerticalAlignment = VerticalAlignment.Center;
That's right out of the HelloCode sample in Chapter 1. Here's the equivalent code in C++/CX:
TextBlock^ txtblk = ref new TextBlock();
txtblk->Text = "Hello, Windows 8!";
txtblk->FontFamily = ref new Windows::UI::Xaml::Media::FontFamily("Times New Roman");
txtblk->FontSize = 96;
txtblk->FontStyle = Windows::UI::Text::FontStyle::Italic;
txtblk->Foreground = ref new SolidColorBrush(Colors::Yellow);
txtblk->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Center;
txtblk->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Center;
Notice that ref new is applied to TextBlock rather than just new. The customary new keyword allocates the object in the local heap and returns a pointer. The ref new instead returns a handle, which is a reference to the object rather than the pointer itself. These references are counted so that the object can be automatically deleted when there are no longer any references to it.
The ref new operator is not the same as the C++/CLI gcnew operator. The gcnew operator is for .NET managed types. The Windows Runtime types are unmanaged and your C++/CX code is also unmanaged.
Because ref new performs a reference-counted allocation and returns a handle, the returned object cannot be stored in a normal pointer variable, and that's the reason for the hat (^) on TextBlock, which indicates a handle of that type. All WinRT classes are of this nature, and you can create your own reference-counted classes with the ref keyword.
In the HelloCode sample, this code snippet is called in the constructor of a Page derivative. Notice in the C++ snippet that the FontFamily class and some enumerations (FontStyle, HorizontalAlignment, and VerticalAlignment) need to be fully qualified with the namespace name. This is because they are being referenced within an object that derives from a class that includes properties with these same names. In resolving these names, the C# compiler gives higher priority to external classes and enumerations, while the C+ compiler gives higher priority to property names, so the namespaces are required to clarify your intention.
Structures that are part of the Windows Runtime are defined as they are in C, which means they don't have non-data members. However, non-data members of WinRT structures are visible when coding in C#. For example, in C# you can use the static Color.FromArgb method to create a Color value. In C++ you use instead the static ColorHelper::FromArgb method. ColorHelper is a class and FromArgb returns a Color value.
The C# string data type is an alias for the String class in the System namespace. In C++ you use a String class defined in the Platform namespace that encapsulates an immutable Unicode string. It's basically a wrapper around a const wchar_t * object. While the C# String class has plenty of amenities for working with strings, the C++ String class does not. To perform string operations in C++, you must first extract the underlying wchar_t pointer using the Data method. For example,
String^ str = "hello";
const wchar_t* text = str->Data();
You can then use standard C string functions on text, or make a copy for manipulation. To convert back to a Windows Runtime String ^ type, use a constructor:
String^ str2 = ref new Platform::String(text);
If you need to format numbers and other objects for display purposes in C++, you'll use swprintf or swprintf_s and then create a String ^ for passing to a Windows Runtime type such as TextBlock.
In almost all cases for the sample programs in my book, the XAML files in the C# and C++ versions are identical. However, if you need to perform numeric formatting in a data binding, you'll need to write a formatted-string converter (such as the one in Chapter 4 of the book). The C# version will require .NET format specifiers for String.Format while the C++ version requires printf format specifiers. That affects the XAML file.
While I was writing the book I didn't give any consideration to the fact that I'd be translating the programs into C++ at some point. Otherwise, I probably wouldn't have used .NET reflection so much in Chapter 4. The System.Reflection namespace is not available to C++ programs.
In Chapter 4 I used reflection in two ways: to obtain all the static properties of the Colors class for displaying all the named colors, and to construct a class hierarchy starting with the DependencyObject class. In these cases, I didn't feel I was demonstrating .NET reflection so much as using reflection to demonstrate other concepts, so I didn't feel the need to perform reflection in C++. There is apparently a way to do it and there's now a CodePlex project to do it, but instead I wrote a little C# library called ReflectionHelper and then flagged this class library to create a WinMD file, which means that it can be accessed from other Windows Runtime languages. (There are restrictions on libraries of this sort, but that's a topic for another time.)
I suspect that later in the book I might find it necessary to do just the opposite: To create a WinMD library in C++ for use by a C# program.
Generally I use XML serialization to save and load program settings (as in the AppSettings class in the XamlCruncher program in Chapter 7), but the System.Xml.Serialization namespace is not available in C++ so I used ApplicationData::Current->LocalSettings instead. I'm tempted to now use this approach in the C# version of the program.
I couldn't find good examples for defining dependency properties in C++. In C#, DependencyProperty.Register is usually called in a static constructor, but C++/CX doesn't have static constructors. Moreover, in C++ each dependency property requires two TypeName values — one for the type of the class registering the dependency property and one for the type of the property itself — and a private static field for the DependencyProperty object, and a public static property to expose that DependencyProperty object.
I saw one C++ example where DependencyProperty.Register was called the first time the get accessor of the static property was called, but I'm not sure that would work right in all cases. Might not the dependency property need to be registered before the static property was accessed? Answering that question requires knowing Windows Runtime internals. Instead, I called DependencyProperty.Register in the implementation of the static field. The XamlCruncher program has several examples of the format I eventually chose.
I'd like to get some feedback from C++ programmers who buy my book. Is this the next best thing to having a book specifically targetting the C++ programmer? Or is it worthless? Or somewhere in between? (It's a non-trivial amount of work to convert these samples, so if they're worthless, I'd rather spend the time focusing on more C# content in the book.)
Programming Windows, 6th Edition
Special Price through the End of May 2012!
For just $10, you get:
(1) the Consumer Preview ebook right now
(2) the Release Preview ebook in a couple months
(3) the final ebook in November
Programming Windows 6th Edition
Consumer Preview eBook