(1 item) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(6 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(4 items) |
|
(2 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(5 items) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(8 items) |
|
(2 items) |
|
(7 items) |
|
(2 items) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(3 items) |
|
(2 items) |
|
(2 items) |
|
(8 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(6 items) |
|
(1 item) |
|
(2 items) |
|
(5 items) |
|
(5 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(16 items) |
|
(10 items) |
|
(27 items) |
|
(15 items) |
|
(15 items) |
|
(13 items) |
|
(16 items) |
|
(15 items) |
In my previous blog post on Real Native WinRT development, I wrote some native C++ code that created an instance of the Windows.UI.Xaml.Application class, and called its Application.Run method. Because I was using pure native C++ instead of the compiler’s WinRT language projection that sane developers will use, this very simple task took a short helper function and 15 lines of code. (If you read that article, you may recall that there was an unresolved bug—the Run
method was failing. I’ve since updated the post to fix that. It turned out that I was using the wrong threading model. Surprisingly, you have to initialize the UI from a multithreaded context.)
While the code was verbose compared to the equivalent when using the WinRT language projections—a mere two lines—it’s about to get even more complicated. Although my previous example did technically run, it turns out that to do anything useful in a WinRT Xaml application, we need to derive a type from the built-in Application
class. (That’s because you have to override at least one method to be able to show a non-empty UI.) With the WinRT language projections, inheritance is straightforward:
ref class App : public Windows::UI::Xaml::Application { ... };
But I’m choosing to do this natively because I like to understand what’s going on under the covers. In the raw native world, inheritance looks rather more complex.
Just to be clear I’m doing this to learn how WinRT really works. I would NOT normally write a C++ WinRT application this way.
WinRT uses COM for its underpinnings, and classic COM never supported implementation inheritance. So the first challenge was to work out how WinRT maps the concept of inheritance into a COM-like world. I’ve not yet found any clear documentation for this, but there’s a clue in the Application class documentation: the Attributes section shows that the class has a ComposableAttribute
with a first argument of Windows.UI.Xaml.IApplicationFactory
. I’ve not found any documentation for that interface, but its definition lives in Windows.UI.Xaml-coretypes.h
, and from that we can see that this interface defines a single member:
HRESULT CreateInstance( /* [in] */ IInspectable *outer, /* [out] */ IInspectable **inner, /* [out][retval] */ Windows::UI::Xaml::IApplication **instance)
But where do I get an object that implements this IApplicationFactory
interface? Last time, I called RoActivateInstance
to create an instance of the Application
class. But if I want to get hold of the factory for that class I need a different API: RoGetActivationFactory
. All WinRT classes provide an activation factory, which is what RoActivateInstance
uses to create new objects. (It’s very similar in concept to a classic COM class factory.) But when doing something fancier, such as inheritance, we need to talk directly to that factory, rather than letting RoActivateInstance
do that for us.
Here’s how WinRT makes inheritance work in a COM-like world. It appears to involve three objects:
We provide the ‘outer’ object, while the ‘inner’ and the wrapper objects are provided by the WinRT type from which we are deriving. These three objects correspond to the three arguments to IApplicationFactory.CreateInstance
. (I’ve modified the order, because to me, this order makes more sense: we start with the base type, we override virtual methods, and the result is a new type that is the combination of the base object and the overrides.)
Although this system provides a way to model inheritance, it’s actually quite different from how inheritance looks in ordinary C++, C#, or even in the C++ language projection for WinRT. First of all, it’s based on object instances, rather than types. But also, unlike with C++ (or C#) inheritance, we have to define overrides for all virtual methods. That’s because our ‘outer’ object defines overrides by implementing a special ‘overrides’ interface specific to the base class. If you look at a non-sealed WinRT class, you’ll usually see that it implements two interfaces. For example, the Application
class implements IApplication
and IApplicationOverrides
. That second interface defines the methods which inheriting objects can override. (Remember, in COM everything looks like a method, including properties, so virtual properties use this same mechanism.)
Since COM interface implementation is an all-or-nothing prospect, we have to implement every overridable method. If we don’t really want to override a method, we just call back to the base implementation, using the ‘inner’ object returned by the CreateInstance
method.
If you use the C++ WinRT language projection, you can see the compiler generating exactly this sort of code. If you pass the /d1ZWtokens
switch, the compiler will display the full token stream of the code it’s compiling, including any generated code. (Beware: this will slow down compilation considerably, as it dumps several megabytes of code into Visual Studio’s mysteriously slow Output panel.) Actually it generates a bit more than a plain old call to the base class. Here’s the code it produces in an ordinary WinRT project (with the WinRT projection enabled) for a method that I’m not overriding:
inline long __stdcall ::HighLevelWinRTClient::App:: __cli_Windows_UI_Xaml_IApplicationOverrides____cli_OnInitialize() { struct Windows::UI::Xaml::IApplicationOverrides^ __tempValue; long __hr = __cli_baseclass->__cli_QueryInterface( const_cast<class Platform::Guid%>( reinterpret_cast<const class Platform::Guid%>( __uuidof(struct Windows::UI::Xaml::IApplicationOverrides^))), reinterpret_cast<void**>(&__tempValue)); if (!__hr) { __hr = __tempValue->__cli_OnInitialize(); } return __hr ; }
This uses a class member called __cli_baseclass
, which is where the compiler stores that ‘inner’ IInspectable
that comes back from CreateInstance
. It calls Q
uery
Interface
to ask for the IApplicationOverrides
interface, and uses that to call the base class’s original implementation of the method. This seems pretty inefficient—it’s clearly going to be quicker to ask for that interface just once and store that, rather than doing a QueryInterface
every time the method runs. I’ll take that more efficient approach when I write my derived class by hand—the joy of real native code is that you can control this sort of thing.
And the horror of real native code is that you have to write this sort of thing, whether you want control over it or not. We now have some work to do: we must implement this IApplicationOverrides
interface on an object which we pass into the activation factory’s CreateInstance
method as the ‘outer’ object. Since this is a WinRT interface it derives from IInspectable
. (You can see that the first argument of IApplicationFactory.CreateInstance
takes an IInspectable*
argument.) Remember that IInspectable
is the new interface from which all WinRT interfaces derive, and it derives from the classic COM IUnknown
interface. So we’ll need to supply a working COM object with a viable implementation of IInspectable
. While I could write that by hand, it’s not much fun. Fortunately, it turns out that I don’t have to write it completely from scratch, even in this fully-native world. I can turn to a new library, called WRL.
I’m not entirely sure what WRL stands for because I’ve not found the documentation for it yet, but I’m guessing it might be the Window Runtime Library. Whatever it stands for, it appears to be the successor to ATL, which was a widely used library for writing COM components in C++. If you’re familiar with ATL, you’ll feel at home with WRL, but of course WRL knows about WinRT things like IInspectable
, which is why we’re using it. Here’s the class definition for my Application
-derived class:
namespace LowLevelWinRTClient { using namespace Windows::ApplicationModel::Activation; class DerivedApp : public Microsoft::WRL::RuntimeClass<Windows::UI::Xaml::IApplicationOverrides> { InspectableClass(L"LowLevelWinRTClient.DerivedApp", TrustLevel::BaseTrust) Microsoft::WRL::ComPtr<Windows::UI::Xaml::IApplicationOverrides> _spBaseImplementation; public: DerivedApp(void); ~DerivedApp(void); void SetBase(Windows::UI::Xaml::IApplicationOverrides* pBaseImplementation) { _spBaseImplementation = pBaseImplementation; } HRESULT STDMETHODCALLTYPE OnInitialize(); HRESULT STDMETHODCALLTYPE OnActivated(IActivatedEventArgs *args); HRESULT STDMETHODCALLTYPE OnLaunched(ILaunchActivatedEventArgs *args); HRESULT STDMETHODCALLTYPE OnFileActivated(IFileActivatedEventArgs *args); HRESULT STDMETHODCALLTYPE OnSearchActivated(ISearchActivatedEventArgs *args); HRESULT STDMETHODCALLTYPE OnSharingTargetActivated(IShareTargetActivatedEventArgs *args); HRESULT STDMETHODCALLTYPE OnFilePickerActivated(IFilePickerActivatedEventArgs *args); }; }
There are a few things to notice here. The first is that this derives from WRL’s RuntimeClass
base type. This implements the methods of the IUnknown
and IInspectable
base interfaces that all WinRT interfaces derive from. So all we have to do is implement the methods specific to whichever interfaces we want to offer.
I’ve passed the IApplicationOverrides
interface as a template argument to RuntimeClass
—WRL needs to know which interface(s) we want to implement to implement the IUnknown::QueryInterface
and IInspectable::GetIids
methods correctly. WLR defines several RuntimeClass
types, each taking a different number of interfaces as template arguments, but since I only need to implement one interface on this particular object, I’m using the one-argument version.
The next point of interest is the line beginning InspectableClass
. That’s a macro which provides information necessary to implement IInspectable
. As you may recall, IInspectable
offers methods to discover an object’s type name and its trust level, so as you’d expect, we need to provide these to the macro, so that RuntimeClass
can implement IInspectable
correctly.
I’ve also declared a field to hold the ‘outer’ object—the base implementation that we use for methods that we don’t really want to override. (We can also use it when our overrides need to call the base class as part of their implementation.) I’ve written a public helper method to set that base reference because I’ve chosen to make this IApplicationOverrides
object standalone—it won’t actually create its own base instance. You probably wouldn’t do it this way for real, but for this example, I wanted to keep things that are separate COM objects as separate classes, just to make it easier to see all the moving parts required for WinRT inheritance.
Finally, we implement all the methods defined by IApplicationOverrides
. (Again, I couldn’t find documentation for this class, so I got this method list by looking at the relevant header file.)
Most of my method implementations look something like this:
HRESULT DerivedApp::OnInitialize() { return _spBaseImplementation->OnInitialize(); }
I’m just calling the corresponding method on the base class—this is how we choose not to override a method. If there are arguments, we just pass them straight through. Six of my seven methods defer to the base class like this.
The only method I’m truly overriding is OnLaunched
. In the world of the C++ WinRT language projection, the C++ Metro template contains code which provides the application’s window with its initial content, and then activates it, like this:
void App::OnLaunched(LaunchActivatedEventArgs^ pArgs) { Window::Current->Content = ref new MainPage(); Window::Current->Activate(); }
Here’s raw native code that does roughly the same thing.
HRESULT DerivedApp::OnLaunched(ILaunchActivatedEventArgs *args) { using namespace Windows::UI::Xaml; ComPtr<IWindowStatics> spWindowStatics; HRESULT hr = Windows::Foundation::GetActivationFactory( ComHstring::Make(RuntimeClass_Windows_UI_Xaml_Window), &spWindowStatics); ComPtr<IWindow> spCurrentWindow; if (SUCCEEDED(hr)) { hr = spWindowStatics->get_Current(&spCurrentWindow); } ComPtr<Windows::UI::Xaml::Markup::IXamlReaderStatics> spXamlReaderStatics; if (SUCCEEDED(hr)) { hr = Windows::Foundation::GetActivationFactory( ComHstring::Make(RuntimeClass_Windows_UI_Xaml_Markup_XamlReader), &spXamlReaderStatics); } ComPtr<IUIElement> spContent; if (SUCCEEDED(hr)) { wchar_t const* xamlContent = L"<Grid xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">" L" <TextBlock Text=\"Hello, world\" />" L"</Grid>"; ComPtr<IInspectable> spContentAsInspectable; hr = spXamlReaderStatics->Load( ComHstring::Make(xamlContent), &spContentAsInspectable); if (SUCCEEDED(hr)) { hr = spContentAsInspectable.As(&spContent); } } if (SUCCEEDED(hr)) { hr = spCurrentWindow->put_Content(spContent.Get()); } if (SUCCEEDED(hr)) { hr = spCurrentWindow->Activate(); } return hr; }
Again, roughly 10 times as much code!
OK, that’s not quite true. The original code relies on the project defining a UserControl
-derived class which it just instantiates and uses as content. I didn’t want to tackle writing a UserControl
in raw native code just yet, so instead, I used the WinRT XamlReader
class to load some Xaml from a string constant. About half the code you see here is related to that.
On the other hand, it’s actually slightly more complex than it looks. I got bored of writing code to create and destroy HSTRING
s, so I wrote a little helper, ComHstring
, which I’m using in a few places in that code. It provides implicit conversions to HSTRING
, along with a destructor that automatically deletes the HSTR
ING
once the temporary returned by its Make
method is done with. (As far as I can tell, WRL doesn’t have an HSTRING
wrapper, which is slightly surprising.)
In that last example, you’ll notice a couple of calls to GetActivationFactory
, which is a WRL wrapper around RoGetActivationFactory
. Earlier I mentioned that as a way of getting access to the mechanism for inheritance, but it’s also the way to get access to the static methods defined by a class. Classic COM doesn’t have a concept of static methods—methods are always presented through an interface pointer, so there’s always an instance involved. When WinRT classes define static methods, they appear on the activation factory.
Now that we have an implementation of IApplicationOverrides
, we’re ready to create an instance of our Application
-derived type. Here’s the code:
ComPtr<Windows::UI::Xaml::IApplicationFactory> spApplicationFactory; hr = Windows::Foundation::GetActivationFactory( ComHstring::Make(L"Windows.UI.Xaml.Application"), &spApplicationFactory); CheckHresult(hr, L"GetActivationFactory"); ComPtr<DerivedApp> spDerivedApp = Make<DerivedApp>(); ComPtr<Windows::UI::Xaml::IApplicationOverrides> spDerivedAppOverrides = spDerivedApp; ComPtr<Windows::UI::Xaml::IApplication> spApplication; ComPtr<IInspectable> spInner; hr = spApplicationFactory->CreateInstance( spDerivedApp.Get(), &spInner, &spApplication); CheckHresult(hr, L"Application CreateInstance"); ComPtr<Windows::UI::Xaml::IApplicationOverrides> spBaseImplementation; hr = spInner.As(&spBaseImplementation); CheckHresult(hr, L"QI for base IApplicationOverrides"); spDerivedApp->SetBase(spBaseImplementation.Get());
(ComPtr
is a WRL smart pointer. It automates some aspects of COM that otherwise get tedious fast. It’s similar to ATL’s CComPtr
, although some operations that CComPtr
performed implicitly now require explicit code, largely because those implicit operations were responsible for a lot of bugs in code written by people who didn’t really understand how CComPtr
works. With the new ComPtr
, a failure to understand how it works is more likely to lead to a compiler error than a runtime bug.)
We get the Application
class’s activation factory (using GetActivationFactory
which is a wrapper for RoGetActivationFactory
). We create an instance of our IApplicationOverrides
class—the Make<T>
method I’m using here is a WRL helper that creates and initializes RuntimeClass
-based object. We then call the factory’s CreateInstance
method, passing in our overrides implementation. CreateInstance
passes back two interface pointers. One is the ‘inner’ object, which we then pass into our overrides object—remember, it needs that to be able to call the base class for methods that it doesn’t wish to override. (Notice I’m doing the QueryInterface
for IApplicationOverrides
during this construction phase, instead of doing it in every single method invocation as the C++ compiler seems to in its projection.)
At the end of all this, the spApplication
variable points to our finished object—it is an interface pointer to the wrapper generated by the Application
class which combines our overrides with the base implementation. So we can now run that to start the application:
hr = spApplication->Run();
When I run my application, I see the “Hello, world” TextBox
that I created in my OnLaunched
method appearing, verifying that I have successfully overridden that method in my derived class.
What could be simpler?