program SampleWinrt; uses System.Win.ComObj, Winapi.Winrt in 'Winapi.Winrt.pas', AllWinRt in 'Generated\AllWinRt.pas', WinRtHelpers in 'WinRtHelpers.pas';There are three new units here:
- Winapi.Winrt contains translations of the new global WinRT APIs. These are essentially the functions and datatypes defined in the Windows Runtime C++ reference: things such as RoInitialize and WindowsCreateString, etc.
- AllWinRt contains all the enumerations, records, and interfaces defined in the Windows Metadata files. This file was generated by a tool that turns the metadata into delphi style interfaces. I'll go into the details of more of this process at a later time.
- WinRtHelpers contains some helper classes that reduce the amount of code one needs to write in order to use the native APIs. This is the starting of a Delphi wrapper around the API to make the code more "Delphi". Currently, it contains a TInspectableObject-- which similar to TInterfacedObject but also implements the IInspectable interface -- and TWindowsString -- a helper class to convert strings to/from HSTRINGs.
begin OleCheck(RoInitialize(RO_INIT_MULTITHREADED)); try Main; finally RoUninitialize; end; end.This code is just to initialize the Windows Runtime, and make sure it gets uninitialized on termination. RoInitialize is similar to COM's CoInitialize -- all threads that interact with WinRT objects must call RoInitialize prior to using it. The application can be initialized as single threaded or multithreaded. Ian Griffiths had difficulty getting the RO_INIT_SINGLETHREADED and likewise my app seems to have issues starting up as single threaded. I suspect this problem may just be a bug in the Metro API. There are a couple of Application Runtime classes in the WinRT framework: Windows.ApplicationModel.Core.CoreApplication, and Windows.UI.Xaml.Application. The Xaml Application is easier to set up, so we'll start with that one. It is interesting to be able to create Xaml applications in Delphi, because immediately we can take advantage of all the Xaml controls provided by Microsoft as well as consume third party controls (regardless of implementation language) and even provide libraries of additional Xaml components that could be consumed by Delphi or other language projections.
procedure Main; var Factory: IApplicationFactory; App: IApplication; insp, outer, inner: IInspectable; begin outer := TDerivedApp.Create; OleCheck(RoGetActivationFactory(TWindowsString('Windows.UI.Xaml.Application'), IApplicationFactory, insp)); Factory := insp as IApplicationFactory; app := Factory.CreateInstance(outer as IApplicationOverrides, inner); TDerivedApp(outer).inner := inner as IApplicationOverrides; app.Run; app := nil; // App needs to be _Released first, otherwise it AVs end;Here we create a TDerivedApp (which I'll expand on below), request a Xaml ApplicationFactory, and construct a IApplication specifying the TDerivedApp is the outer implementation of IApplicationOverrides. The ApplicationFactory provides an Inner implementation, which we store off so that we can make inherited calls. Finally we Run the app. This is sort of a bizarre inheritance model provided by WinRT that I'll gloss over here. Ian Griffiths has an in depth article about how to create inherited WinRT classes, give that a read for some of the more technical details of how it's implemented in C++. Everything here is being stored in Interfaces, so it's reference counted. Even our classes, such as TDerivedApp are reference counted. TDerivedApp derives from TInspectableObject, which derives from TInterfacedObject from System. The Delphi compiler automatically generates calls to _Release, making this code look a lot cleaner than the raw C++ version. Note however that App is being initialized to nil manually before exiting. If Inner is released first, WinRT crashes. I haven't investigated closely, but my suspicion is that Inner may actually be a weak reference to the same object as App, and releasing both is unnecessary/incorrect. So what is our TDerived app? It's an object that implements Windows.UI.Xaml.IApplicationOverrides so we can actually define the behavior of our application.
type TDerivedApp = class(TInspectableObject, IApplicationOverrides) private FInner: IApplicationOverrides; public // IApplicationOverrides interface procedure OnInitialize; safecall; procedure OnActivated(args: IActivatedEventArgs); safecall; procedure OnLaunched(args: ILaunchActivatedEventArgs); safecall; procedure OnFileActivated(args: IFileActivatedEventArgs); safecall; procedure OnSearchActivated(args: ISearchActivatedEventArgs); safecall; procedure OnSharingTargetActivated(args: IShareTargetActivatedEventArgs); safecall; procedure OnFilePickerActivated(args: IFilePickerActivatedEventArgs); safecall; property inner: IApplicationOverrides read FInner write FInner; end;Note, like in C++, we have to implement all the entries in the interface, even if we don't want to handle those events. Implementation of those methods will just be a pass through to the Inner IApplicationOverrides provided by the Activation Factory. For example:
procedure TDerivedApp.OnActivated(args: IActivatedEventArgs); begin FInner.OnActivated(args); end;Eventually there should be class wrappers that handle all the inheritance for you if you don't want to override methods. When the Application Runs, we get an OnLaunched event. That's the one event we wanted to hook up, so let's do that:
procedure TDerivedApp.OnLaunched(args: ILaunchActivatedEventArgs); const content = '<TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ' + ' Name="Text" TextAlignment="Center" HorizontalAlignment="Center" ' + ' VerticalAlignment="Center" FontSize="56">' + ' <Run Text="Hello World"/>' + ' <LineBreak/>' + ' <Run Text="Delphi"/>' + ' <LineBreak/>' + ' <Run Text="Metro Native App (Xaml)"/>' + '</TextBlock>'; var WinStatic: IWindowStatics; ReaderStatic: IXamlReaderStatics; begin // Get the IWindowStatics RoGetActivationFactory(TWindowsString(SWindow), IWindowStatics, WinStatic); // Get an IXamlReaderStatics RoGetActivationFactory(TWindowsString(SXamlReader), IXamlReaderStatics, ReaderStatic); // Populate Xaml WinStatic.Current.Content := ReaderStatic.Load(TWindowsString(content)) as IUIElement; // Activate the current view WinStatic.Current.Activate; FInner.OnLaunched(args); // inherited call; end;Here we just load some constant XML data into the current frame using an XamlReader. It would be easy to design your Xaml in a separate file using Rad Studio or your favorite XML editor and storing on disk it along side your Application, but for simplicity's sake I've just included it inline.