Connecting C++ and XAML

Connecting C++ and XAML

Rate This
  • Comments 22

Hi, I’m Andy Rich, a tester on the C++ frontend compiler and one of the primary testers of the C++/CX language extensions.  If you’re like me, making use of a technology without understanding how it works can be confusing and frustrating.  This blog post will help explain how XAML and C++ work together in the build system to make a Windows Store application that still respects the C++ language build model and syntax.  (Note: this blog post is targeted towards Windows Store app developers.)

The Build Process

From a user-facing standpoint, Pages and other custom controls are really a trio of user-editable files.  For example, the definition of the class MainPage is comprised of three files: MainPage.xaml, MainPage.xaml.h, and MainPage.xaml.cpp.  Both mainpage.xaml and mainpage.xaml.h contribute to the actual definition of the MainPage class, while MainPage.xaml.cpp provides the method implementations for those methods defined in MainPage.xaml.h.  However, how this actually works in practice is far more complex.


 

This drawing is very complex, so please bear with me while I break it down into its constituent pieces.

Every box in the diagram represents a file.  The light-blue files on the left side of the diagram are the files which the user edits.  These are the only files that typically show up in the Solution Explorer.  I’ll speak specifically about MainPage.xaml and its associated files, but this same process occurs for all xaml/h/cpp trios in the project.

The first step in the build is XAML compilation, which will actually occur in several steps.  First, the user-edited MainPage.xaml file is processed to generate MainPage.g.h.  This file is special in that it is processed at design-time (that is, you do not need to invoke a build in order to have this file be updated).  The reason for this is that edits you make to MainPage.xaml can change the contents of the MainPage class, and you want those changes to be reflected in your Intellisense without requiring a rebuild.  Except for this step, all of the other steps only occur when a user invokes a Build.

Partial Classes

You may note that the build process introduces a problem: the class MainPage actually has two definitions, one that comes from MainPage.g.h:

 partial ref class MainPage : public ::Windows::UI::Xaml::Controls::Page,
      public ::Windows::UI::Xaml::Markup::IComponentConnector
{
public:
  void InitializeComponent();
  virtual void Connect(int connectionId, ::Platform::Object^ target);

private:
  bool _contentLoaded;

};

And one that comes from MainPage.xaml.h:

public ref class MainPage sealed
{
public:
  MainPage();

protected:
  virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
};

This issue is reconciled via a new language extension: Partial Classes.

The compiler parsing of partial classes is actually fairly straightforward.  First, all partial definitions for a class must be within one translation unit.  Second, all class definitions must be marked with the keyword partial except for the very last definition (sometimes referred to as the ‘final’ definition).  During parsing, the partial definitions are deferred by the compiler until the final definition is seen, at which point all of the partial definitions (along with the final definition) are combined together and parsed as one definition.  This feature is what enables both the XAML-compiler-generated file MainPage.g.h and the user-editable file MainPage.xaml.h to contribute to the definition of the MainPage class.

Compilation

For compilation, MainPage.g.h is included in MainPage.xaml.h, which is further included in MainPage.xaml.cpp.  These files are compiled by the C++ compiler to produce MainPage.obj.  (This compilation is represented by the red lines in the above diagram.)  MainPage.obj, along with the other obj files that are available at this stage are passed through the linker with the switch /WINMD:ONLY to generate the Windows Metadata (WinMD) file for the project. This process is denoted in the diagram by the orange line.  At this stage we are not linking the final executable, only producing the WinMD file, because MainPage.obj still contains some unresolved externals for the MainPage class, namely any functions which are defined in MainPage.g.h (typically the InitializeComponent and Connect functions).  These definitions were generated by the XAML compiler and placed into MainPage.g.hpp, which will be compiled at a later stage.

MainPage.g.hpp, along with the *.g.hpp files for the other XAML files in the project, will be included in a file called XamlTypeInfo.g.cpp.  This is for build performance optimization: these various .hpp files do not need to be compiled separately but can be built as one translation unit along with XamlTypeInfo.g.cpp, reducing the number of compiler invocations required to build the project.

Data Binding and XamlTypeInfo

Data binding is a key feature of XAML architecture, and enables advanced design patterns such as MVVM.  C++ fully supports data binding; however, in order for the XAML architecture to perform data binding, it needs to be able to take the string representation of a field (such as “FullName”) and turn that into a property getter call against an object.  In the managed world, this can be accomplished with reflection, but native C++ does not have a built-in reflection model.

Instead, the XAML compiler (which is itself a .NET application) loads the WinMD file for the project, reflects upon it, and generates C++ source that ends up in the XamlTypeInfo.g.cpp file.  It will generate the necessary data binding source for any public class marked with the Bindable attribute.

It may be instructive to look at the definition of a data-bindable class and see what source is generated that enables the data binding to succeed.  Here is a simple bindable class definition:

[Windows::UI::Xaml::Data::Bindable]
public ref class SampleBindableClass sealed {
public:
  property Platform::String^ FullName;
};

When this is compiled, as the class definition is public, it will end up in the WinMD file as seen here:

This WinMD is processed by the XAML compiler and adds source to two important functions within XamlTypeInfo.g.cpp: CreateXamlType and CreateXamlMember.

The source added to CreateXamlType generates basic type information for the SampleBindableClass type, provides an Activator (a function that can create an instance of the class) and enumerates the members of the class:

if (typeName == L"BlogDemoApp.SampleBindableClass")
{
 XamlUserType^ userType = ref new XamlUserType(this, typeName, GetXamlTypeByName(L"Object"));
  userType->KindOfType = ::Windows::UI::Xaml::Interop::TypeKind::Custom;
  userType->Activator = 
   []() -> Platform::Object^
   {
     return ref new ::BlogDemoApp::SampleBindableClass();
   };
  userType->AddMemberName(L"FullName");
  userType->SetIsBindable();
  return userType;
}

Note how a lambda is used to adapt the call to ref new (which will return a SampleBindableClass^) into the Activator function (which always returns an Object^).

From String to Function Call

As I mentioned previously, the fundamental issue with data binding is transforming the text name of a property (in our example, “FullName”) into the getter and setter function calls for this property.  This translation magic is implemented by the XamlMember class.

XamlMember stores two function pointers: Getter and Setter.  These function pointers are defined against the base type Object^ (which all WinRT and fundamental types can convert to/from).  A XamlUserType stores a map<String^, XamlUserType^>; when data binding requires a getter or setter to be called, the appropriate XamlUserType can be found in the map and its associated Getter or Setter function pointer can be invoked.

The source added to CreateXamlMember initializes these Getter and Setter function pointers for each property.  These function pointers always have a parameter of type Object^ (the instance of the class to get from or set to) and either a return parameter of type Object^ (in the case of a getter) or have a second parameter of type Object^ (for setters).

if (longMemberName == L"BlogDemoApp.SampleBindableClass.FullName")
{
  XamlMember^ xamlMember = ref new XamlMember(this, L"FullName", L"String");
  xamlMember->Getter =
  [](Object^ instance) -> Object^
  {
    auto that = (::BlogDemoApp::SampleBindableClass^)instance;
    return that->FullName;
  };

  xamlMember->Setter =
 [](Object^ instance, Object^ value) -> void
  {
    auto that = (::BlogDemoApp::SampleBindableClass^)instance;
    that->FullName = (::Platform::String^)value;
  };
  return xamlMember;
}

The two lambdas defined use the lambda ‘decay to pointer’ functionality to bind to Getter and Setter methods.  These function pointers can then be called by the data binding infrastructure, passing in an object instance, in order to set or get a property based on only its name.  Within the lambdas, the generated code adds the proper type casts in order to marshal to/from the actual types.

Final Linking and Final Thoughts

After compiling the xamltypeinfo.g.cpp file into xamltypeinfo.g.obj, we can then link this object file along with the other object files to generate the final executable for the program.  This executable, along with the winmd file previously generated, and your xaml files, are packaged up into the app package that makes up your Windows Store Application.

A note: the Bindable attribute described in this post is one way to enable data binding in WinRT, but it is not the only way.  Data binding can also be enabled on a class by implementing either the ICustomPropertyProvider interface or IMap<String^,Object^>.  These other implementations would be useful if the Bindable attribute cannot be used, particularly if you want a non-public class to be data-bindable.

For additional info, I recommend looking at this walkthrough, which will guide you through building a fully-featured Windows Store Application in C++/XAML from the ground up.  The Microsoft Patterns and Practices team has also developed a large application which demonstrates some best practices when developing Windows Store Applications in C++: project Hilo.  The sources and documentation for this project can be found at http://hilo.codeplex.com/.  (Note that this project may not yet be updated for the RTM release of Visual Studio.)

I hope this post has given you some insight into how user-edited files and generated files are compiled together to produce a functional (and valid) C++ program, and given you some insight into how the XAML data binding infrastructure actually works behind the scenes.

  • Couple things, Andy.

    First, you can't call these apps "Windows Store Apps", because I just reviewed Sinofsky's //build/ keynote, and Win32 apps are going to be available in the Windows Store right alongside Metro-style apps. Please work on a more accurate name.

    Second, when will desktop application developers get the ability to use XAML and native code together? That ability would go a long way toward fixing the brokenness of WPF, along with immediate-mode Direct2D graphics.

    Thanks,

    Eric

  • @smallmountain0705 I'm not sure if you're aware but they also cannot call them Metro style apps anymore due to the legal loss with german company.

  • Yes, I am well aware of what Microsoft has said is the reason they cannot use "Metro" anymore - that Metro AG complained.  You use the word "loss" - I don't think they lost a trademark dispute, they just gave up.  I don't think Microsoft really wanted to continue to use "Metro" as the name, but they should have come up with something.  It is critically important that Microsoft call different things different names, and good naming choices matter.  Naming your products is an important part of branding and marketing in general.  The fact that Microsoft is coming to product release without coming up with a good, maybe even clever, name for applications that run under Windows RT (or perhaps for Windows RT itself) that distinguishes them in a non-prejudicial way from applications that run under the desktop is a massive marketing blunder on the part of Microsoft.  This is exactly what they did *right* with Surface.  Surface is a fantastic name for Microsoft's tablets.  Apple has iPhone apps and iPad apps (and maybe iOS apps for apps that run on both).  There is no confusion with applications that run on a Mac, Google has Android apps for apps that run on Android phones and Android tablets and a cute little robot symbol that goes along with it.  *That* is marketing.  Microsoft has "modern" apps (oh, desktop apps are automatically not modern?  If so, it's because Microsoft hasn't given ISV's a decent alternative to 20-year-old MFC for native development) or "Windows 8" apps (well, desktop apps also run on Windows 8) or "Windows Store" apps (well, Win32 apps can appear in the Windows Store).  "Windows RT" apps is an option, but not a great one - too geeky, not catchy.  Different people at Microsoft using different terms to talk about the same thing.  It's just a massive marketing failure.

  • @smallmountain

    >>I don't think they lost a trademark dispute, they just gave up

    If you follow for some years MS you see that giving up is not a word in their dictionary. As a company they are non-compromizing, ferocious player. They only ever stop if they are told to by law verdicts.

  • The ars technica story on it says "threatened with legal action" (http://bit.ly/OsRt0M).  Again though, that's beside the point.  Whether "metro" or something else, it needs a good name, and MSFT has not come up with one.

  • Build already came and went? swooosh!

  • Sinofsky's comment about Win32 apps appearing in the store was from *last* year's //build/ conference.  This year's //build/ conference is late October.

  • and as usual when MS is concerned one (last one) '/' unnecessary

  • Thinking one missed BUILD, whether true or not, shows how irrelevent MSFT (insert new logo here _____ ) has become.

  • You won't see ISV Win32 programs in MSFT's store.  You either misunderstood the Siniofsky or he mispoke, purposely or not.  WinRT is all MSFT can control and that is all you will ever find there, in the store.  Certainly no Win32 program you or I do will ever be there.  Believe whatever you will, but that is the way it is/will be.

  • Well, go here: channel9.msdn.com/.../KEY-0001

    and fast forward the video to the 51 minute mark or so and watch a demo where there is a Quicken tile in the Windows Store, and the dude clicks on it, and it takes him to Intuit's web site where you can buy Quicken.  There is no ambiguity.  @JD, what evidence do you have that Microsoft has put the kibosh on that scenario?

  • I think this blog entry is mistitled; I don't see any native C++ here.

  • Embrace the "^"!  ;-)  It may not be standard, portable C++, but it compiles to native code.  I love C++/CLI (and I think all 5 people who have ever used it also love it), and I will love C++/CX, if I can ever use it on the desktop.

  • Microsoft is welcome to create its own language extensions, but using "C++" to refer to anything but ISO-standard C++ is inaccurate, and dilutes the value of the C++ name, not to mention disappointing those of us who eagerly start to read the material, only to realize it's useless to us if we want to maintain cross-platform code.  I won't go so far as to accuse MS of deliberately trying to "embrace and extend" here, but since they've used such tactics in the past, it would be prudent for them not to invite comparisons to those days, and to use the "C++" and "C++/CX" labels appropriately.

  • I don't understand why MS prefers language extensions (partial) at every possible occasion. I mean look at Qt - they had basically the same idea years ago: .ui files compiled by their tool(uic) to .h files, but instead of generating new keywords(not talking about moc compiler, this is another story) they simply make it another class and include it as a member or use inheritance (optional).

    This is crippling any other compilers from ever being able to cope with WinRT stuff.

Page 1 of 2 (22 items) 12