2022年2月14日、Delphiは27歳のハッピーバースディを迎えました。この記念すべきDelphiの27周年アニバーサリーに、エンバカデロのWebinarで発表したMarco CantuのWinUI 3のデモを公開しました。このブログでは、Microsoftからの公式発表と技術情報、そしてGetItから入手できるWinUi 3のデモについて紹介いたします。
Table of Contents
はじめに
MicrosoftのWindows App SDK(以前はProject Reunionと呼ばれていました)には、WinUI 3と呼ばれる新しいネイティブUIフレームワークが含まれています。 WinUI 3はWinRT(Windowsランタイム)アーキテクチャの現在のUIフレームワークで、パッケージアプリケーション(APPXまたはMSIXコンテナ経由)と非パッケージGUIアプリケーション(例えば、DelphiやRAD Studioで作成された通常のWin32/Win64ネイティブアプリケーション)で利用可能です。このパッケージアプリケーションへの言及は、Delphiのパッケージとは何の関係もありません。
Windows App SDKは、以前からRAD Studioのインストール機能として提供している同様の名称の「Windows SDK」とは異なるツールセットであることもご留意ください。
またサンプルプロジェクトSampleWinRTは、2021年11月にリリースされたMicrosoftのWinUI 3(1.0)フレームワークから純粋に単純なDelphi GUIアプリケーションを構築する方法を紹介する目的で提供しています。WinUI3のプレリリースバージョンでは、アプリケーションをWindowsパッケージアプリケーションとしてデプロイする必要がありましたが、WinUI 3(1.0)ではその制限がなくなり、Delphiで通常のアプリとしてWinUI 3アプリを構築できるようになりました。
詳しくは、以下の情報をご参照ください。
- WinUI 3: https://docs.microsoft.com/ja-jp/windows/apps/winui/winui3/
- Windows App SDK: https://docs.microsoft.com/ja-jp/windows/apps/windows-app-sdk/
このデモには、WinUI 3の1.0リリースをサポートするために、WinRT Delphi RTLインポートユニットの最新バージョンが付属しています。 (現在のDelphi 11 Alexandriaバージョンには、同じインポートユニットの古いバージョンが同梱されています。)
前提条件: Windows App ランタイムのインストーラーとWindowsアプリランタイムローダーDLL
非パッケージアプリをビルドしてデプロイするためには、以下の手順が必要です。
https://docs.microsoft.com/ja-jp/windows/apps/windows-app-sdk/deploy-unpackaged-apps
(1)まず、Windows App SDKのインストーラーが必要です。
前述のMicrosoftのページにあるダウンロードリンクから入手するか、https://aka.ms/windowsappsdk/1.0-stable/msix-installerのURLからzipファイルをダウンロードしてください。
このアーカイブはMicrosoft.WindowsAppRuntime.Redist.1.0.0というファイル名で、ファイルを展開すると、arm64/x86/x64プラットフォーム向けのインストーラー(いずれもWindowsAppRuntimeInstaller.exeというファイル名)が含まれています。
インストーラー(UACによる権限昇格)を実行すると、Windows Appランタイムがインストールされます。Windows Appランタイムは、C:¥Program Files¥WindowsAppsの隠しフォルダ内に配置されます。たとえば、x86版は、C:¥Program Files¥WindowsApps¥Microsoft.WindowsAppRuntime.1.0_0.319.455.0_x86__8wekyb3d8bbweフォルダ内に必要なファイルが配置されます。
(2)次に、Windows app ランタイムのローダーDLLである「Microsoft.WindowsAppRuntime.Bootstrap.dll」が必要です。
このファイルは、Windows App SDK NuGetパッケージ(現在Microsoft.WindowsAppSDK.1.0.0.nupkg)から取得できます。このパッケージは、https://www.nuget.org/packages/Microsoft.WindowsAppSDKのURLへアクセスし、下図の”Download package”のリンクをクリックしてmicrosoft.windowsappsdk.1.0.0.nupkgというファイルをダウンロードしてください。
microsoft.windowsappsdk.1.0.0.nupkgファイルのダウンロードが完了したら、拡張子を.nupkgから.zipへ変更し、microsoft.windowsappsdk.1.0.0.zipというファイル名に変更して、アーカイブを展開してください。
Microsoft.WindowsAppRuntime.Bootstrap.dllは、展開したruntimes¥win10-x86¥nativeフォルダ、あるいは runtimes¥win10-x64¥nativeフォルダに配置されています。
Microsoft.WindowsAppRuntime.Bootstrap.dllは、アプリケーション起動時にAppランタイムに関連するAPIを呼び出すために必要なので、実行ファイル(.exe)と同じフォルダに配置するか、このDLLを配置したパスを事前に通してください。ローダーDLL(またはブートストラップDLL)は、WinUI 3を実装するWindows App Runtimeの適切なバージョンを、そのスタートアップAPIに渡されたバージョン値に基づいて検索する役割を担っています。
注意:DelphiのTEdgeBrowerコンポーネントのサポート(Window App SDKの別の機能をラップしたもの)を使用した際、Microsoft WebView2ランタイム(エンドユーザも必要)と再配布可能なWebView2Loader.dllのようなSDKの要件に類似しています。詳しくは、こちらを参照ください。
デモのダウンロード
Delphi WinUI 3デモは、RAD Studio 11 Alexandriaのお客様向けにGetItで公開されています。インストールすることで、デモのソースコードと必要なライブラリファイルが、このブログ記事の内容と同じようなReadmeファイルと一緒にインストールされます。下図は、GetItパッケージマネージャで表示されるDelphi WinUI 3デモのパッケージです。
デモの紹介
このデモアプリケーションは、コンソールアプリとして作成されており、アプリケーションの進行に応じてコンソールウィンドウに情報を書き込み、エラーメッセージ(依存関係の欠如など)を表示する方法も提供されています。
注意:WinUI 3では、昇格した特権を使わずにプロセスを実行する必要があります。そのため、デモはこれをチェックし、昇格された特権が見つかった場合は簡単なメッセージで終了します。 このチェックを行わないと、WinUI3は一見優雅な方法でアプリケーションをクラッシュさせます。
以下のコード例では、特権チェックをパスした後、アプリはローダーDLLの初期化APIであるMddBootstrapInitializeを呼び出し、Windows Appランタイムを見つけて起動すると、アプリはさらに初期化を続行します。そしてアプリを終了する前に、MddBootstrapShutdownを呼び出してクリーンアップしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
uses WinRT; const // From includeWindowsAppSDK-VersionInfo.h in WinAppSDK NuGet package WINDOWSAPPSDK_RELEASE_MAJORMINOR = $00010000; WINDOWSAPPSDK_RUNTIME_VERSION_UINT64 = $0000013F01C70000; WINDOWSAPPSDK_RUNTIME_VERSION_DOTQUADSTRING = '0.319.455.0'; var PackageVersion: PACKAGE_VERSION; ... PackageVersion.Version := WINDOWSAPPSDK_RUNTIME_VERSION_UINT64; var HR := MddBootstrapInitialize(WINDOWSAPPSDK_RELEASE_MAJORMINOR, nil, PackageVersion); if Succeeded(HR) <strong>then try Main; WriteLn('Press Enter (again) to continue'); Readln; finally MddBootstrapShutdown; |
ご覧のようにSDKのリリースとランタイムパッケージ版は、Windows App SDK NuGetパッケージ内のヘッダー(.h)ファイルから借用しています。
Main内で、WinUI 3 Applicationオブジェクトの静的なStartメソッドを呼び出します。
詳しくは、https://docs.microsoft.com/en-us/windows/winui/api/microsoft.ui.xaml.application.startを参照ください。
Startメソッドは、アプリケーションが初期化されるときに実行されるコールバックを取るので、必要に応じて設定することができます。幸いなことに、WinRTのコールバック(WinUI 3のコールバックも含む)はDelphiの匿名メソッドと同じ実装なので、アプリケーションのOnLaunchedイベントハンドラを設定するために匿名メソッドを使用します。
WinUI 3では、OnLaunchedは実際にはイベントハンドラではありません。
詳しくはhttps://docs.microsoft.com/en-us/windows/winui/api/microsoft.ui.xaml.application.onlaunchedを参照ください。その代わりにOnLaunchedは、WinUIアプリケーションクラスから継承するクラスでオーバーライドすることが期待されるメソッドです。 DelphiのWinRTクラスから継承するというこの要件にどのように対処すればよいのでしょうか?
WinUIは、Delphiで利用可能な仕組みを提供することで対応できます。このような場合、WinUI 3クラスからの継承には、以下のような3つのオブジェクトが必要です。
- WinUIによって提供される基本実装を与える内部オブジェクトで、この場合はアプリケーションの基本実装となります。この内部オブジェクトは、この場合、IApplicationOverridesの実装を提供するメソッドを定義するインターフェースを実装します。
- 問題のメソッドのカスタム実装を提供する外部オブジェクト(ベースオブジェクトと呼ばれる)は、Delphiで提供するものであり、IApplicationOverridesも実装します。
- 内部オブジェクトと外部オブジェクトを組み合わせたラッパーオブジェクトで、WinUIによって提供されます。
以下は、Mainのコードの例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
var AppInner: IInspectable; var AppOuter: IApplicationOverrides; var AppWrapper: IApplication; // In this code block we use an anonymous procedure (which Delphi implements as an // interface with an Invoke method) that matches the layout of the callback interface type TCallbackProc = reference to procedure (p: IApplicationInitializationCallbackParams) safecall; procedure Main; begin // Here we define an anonymous method and cast it to the matching WinUI interface type var CallbackProc: TCallbackProc := procedure (p: IApplicationInitializationCallbackParams) safecall begin // First we create our derived class AppOuter := TDerivedApp.Create as IApplicationOverrides; // Then we get the IApplicationFactory... var Factory := TApplication.Factory; // ... and use it to get an IApplication wrapper, AppWrapper, // and an IApplicationOverrides inner as well AppWrapper := Factory.CreateInstance(AppOuter, AppInner); // We store the inner object in the outer one so // we can call the base functionality when needed AppOuter.Inner := AppInner as IApplicationOverrides; end; var AppInitCallback := ApplicationInitializationCallback(CallbackProc); TApplication.Start(AppInitCallback); end; |
以下のコードは、アプリケーションでカスタマイズしたクラスです。
1 2 3 4 5 6 7 8 9 10 |
type TDerivedApp = class(TInspectableObject, IApplicationOverrides) private FInner: IApplicationOverrides; public // IApplicationOverrides methods procedure OnLaunched(args: ILaunchActivatedEventArgs); safecall; ... property Inner: IApplicationOverrides read FInner write FInner; end; |
OnLaunchedメソッドは、必要なアプリのセットアップを自由に行うことができます。
この例では、Window (https://docs.microsoft.com/en-us/windows/winui/api/microsoft.ui.xaml.window)を作成して設定し、いくつかのコントロールを追加して、下図のような UI を作成しています。
アプリケーションの詳しい動作については、Delphi WinUI 3デモの実装コードをご覧ください。以下はデモの要約です。
- StackPanelコントロールを作成してウィンドウに配置し、Clickイベントハンドラでボタンを作成してStackPanelに追加します。
- 次に、いくつかのXAMLを使用して、別のStackPanelとボタンの子コントロールを作成します。ここでもClickイベントハンドラを使用して、StackPanelをウィンドウに追加します。 これにより、同じ目標を達成するための2つのアプローチが得られます。
- 最後に、さらにいくつかのXAMLを使用してグリッドを作成し、その中にTextBlockを作成します。このテキストブロックには、OnPointerEnteredとOnPointerExited向けにいくつかのイベントハンドラが追加されています。
- 最終的には、いくつかのボタンとテキストブロックを表示するウィンドウが表示されます。 ボタンをクリックしてキャプションを変更したり、マウスをボタンの内外に移動したときにテキストブロックの色を変更できます。
動的に作成されたUIコントロール(最初のボタンなど)の場合、イベントハンドラを直接関連付けることができますが、XAML構成を読み込んでコントロールを作成する場合、コードは一致するオブジェクトを見つけてイベントハンドラを関連付ける必要があります。
注意:C#やC++とは異なり、Delphiの言語プロジェクションは、現在、WinRTとWinUI 3オブジェクトが実装するさまざまなインターフェースをすべて統一プロキシクラスに組み込んでいないため、他の場所よりもはるかに多くのインターフェース作業が行われています。
今後の展開
Microsoftは将来的に、XAML Islands for Win UI 3を介して、従来のWindowsフォームハンドル(例えば、VCLまたはFMXアプリ)内でWinUI 3コントロールをホストできるようにサポートすることを発表しました 。 この機能は2022年後半に予定されているので、まだDelphiの機能に統合する計画の検討には時期尚早ですが、今後の展開にどうかご期待ください。
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition