概要
C++ にある程度慣れている人向けに C++/WinRT を紹介する。
開発環境を整え、簡単なプログラムを実行する。
C++/WinRT とは
- Windows 10 のネイティブアプリを作るためのフレームワーク
- C++/CX のような独自言語を用いない C++17 のライブラリとして表現される
- WRL という Template Library もあったらしいが、 C++/WinRT に取って代わられた
- 小さく、ランタイムが速いのがウリ
C++ で表現されたネイティブなライブラリってことは MFC みたいなもんね、と言ってしまえばそれまでだが、ユニバーサル Windows プラットフォーム (UWP) に対応しているのが大きく異なる。PPI (DPI) の異なる複数のディスプレイの間でウィンドウを移動してもきちんと追従する。
あとは MFC みたいなマクロによるおまじないが登場しないのはとても良い。人間が書くべきコードは大幅に減り、とても簡単に、そしてコードが見やすくなった。ただ、人間が書いていないというだけであって裏で自動生成されているコードはある。Qt や Unity の IL2CPP みたいなやり口だ。
開発環境の準備
はやる気持ちを抑えてまずは環境を整える。
Visual Studio のサポート、C++/WinRT、XAML では、VSIX 拡張機能と、NuGet パッケージ を読めば全て書いてあるが、タイトルからわかる通り翻訳がめちゃくちゃなので英語で読みたくない人のために最低限をここに記す。
以下を用意する:
- Windows 10 (1803以降)
- Visual Studio 2017 (可能な限り最新)
- C++/WinRT VSIX
Windows 10
- スタートメニューから 設定→更新とセキュリティ→開発者向け から 開発者モード になっておくこと
Visual Studio 2017
- Visual Studio Installer を起動
- から Community, Professional, Enterprise のいずれかをインストールする
-
ワークロード は ユニバーサル Windows プラットフォーム開発 を選択
- 右の インストールの詳細 から C++ ユニバーサル Windows プラットフォーム ツール にチェックを入れる
- インストール
C++/WinRT VSIX
VSIX は新規プロジェクトを作成する際の雛形を提供する。すでに存在するプロジェクトには必要ない。
- Visual Studio 2017 を起動
- メニューの ツール→**機能拡張と更新プログラム...**を開く
- 左からオンライン→Visual Studio Marketplace を開き、右上の検索フィールドから
C++/WinRT
で検索して ダウンロード を実行 - Visual Studio 2017 を一度終了すると VSIX インストーラーが起動する
プロジェクトの新規作成
ここまで出来てようやくプロジェクトの作成だ。
- Visual Studio 2017 を起動
- メニューの ファイル→新規作成→プロジェクト...
-
Visual C++→Windows Universal→Blank App (C++/WinRT) を選択して OK
- 環境によっては何故か Visual C++→テスト→Blank App (C++/WinRT) に出現することもあった
- 最小バージョン は Windows 10, version 1803 (10.0; ビルド 17134) にする
実行してみる
実行
メニューからビルドして実行してみよう。Click Me
と書かれたボタンが出てきて、クリックするとボタンが Clicked
に変化する。
レイアウト
この UI のレイアウトは MainPage.xaml に入っている。開いてみるとすごく縮小されたレイアウトが出てきて面食らうのだが、非常に大きなスクリーンで表示していることになっていることが原因。左上のプルダウンから 4" IoT Device など小さなスクリーンを選ぶとだいぶ見やすくなる。
下には XML で同じ内容が表示されており、こちらを編集しても即座に上の絵に反映される。
コントロールは ツールボックス ウィンドウに並んでおり、ここから新たなコントロールを配置することができる。Visual Basic や MFC 時代を知っていると好き勝手並べたくなるのだが、昨今携帯端末から大型ディスプレイまで様々な画素数、PPI のディスプレイがあるため基本的には自動レイアウトになっている。XML をみると分かるが Button
の親にある StackPanel
というやつがそれ。
イベントは Button
に記述されている Click="ClickHandler"
によって ClickHandler
メソッドが呼ばれることになる。
<Button x:Name="myButton" Click="ClickHandler">Click Me</Button>
実装
MainPage.xaml にぶら下がっている MainPage.cpp に実装がある。MyProperty
は32ビットのプロパティを持たせているだけで何もしていないので、実質 MainPage
コンストラクタと ClickHandler
だけだ。
改造してみる
クリップボードの中身を監視して表示するプログラムに変えてみよう。
手順としては次の通り:
- レイアウトに TextBlock を追加する
- ClickHandler を削除する
- クリップボードのハンドラを追加する
レイアウトに TextBlock を追加する
MainPage.xaml の StackPanel
を次のように書き換える:
<StackPanel>
<TextBlock x:Name="clipboardText" Text="コピーするとここに表示される"/>
</StackPanel>
これによりボタンは消滅する。
ClickHandler を削除する
レイアウトからボタンを削除したため MainPage.h と MainPage.cpp から ClickHandler
を削除する。
クリップボードのハンドラを追加する
クリップボードは winrt::Windows:ApplicationModel::DataTransfer::Clipboard で操作できる。
ヘッダは名前空間ごとに存在するので、 DataTransfer
名前空間を取り込むために pch.h に次の1行を追加する:
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
MainPage.cpp の using namespace
が並ぶ位置に DataTransfer
名前空間を、ついでにこの後使う IAsyncAction
のために Foundation
名前空間も追加する:
using namespace winrt;
using namespace Windows::UI::Xaml;
using namespace Windows::ApplicationModel::DataTransfer; // 追加
using namespace Windows::Foundation; // 追加
MainPage.cpp の MainPage::MainPage()
を次のように書き換える:
MainPage::MainPage()
{
InitializeComponent();
// イベントハンドラの登録
Clipboard::ContentChanged([this](IInspectable const, IInspectable const) -> IAsyncAction
{
DataPackageView content = Clipboard::GetContent();
// クリップボードに入っているのはテキストか
if (content.Contains(StandardDataFormats::Text())
{
// clipboardText に表示
hstring text = co_await content.GetTextAsync();
clipboardText().Text(text);
}
});
}
クリップボードからテキストを取得するためには GetTextAsync
という非同期関数を呼び出すため、非同期待ちの co_await
を使用している。co_await
を呼び出すとこのラムダ関数はコルーチンになるため、戻り値の型が IAsyncAction
になる。ラムダ関数の引数も参照にしたくなるが非同期になるため値渡しにする必要がある。
WinRT を使用すると IAsyncAction
, IAsyncOperation
, co_await
があちこちで登場するようになる。ここはややこしいので C++/WinRT のドキュメントを読んで欲しいが、ボタンを押した後の処理で登場した void MainPage::ClickHandler()
のようなやつも、中で非同期処理を行う場合は戻り値の型が IAsyncAction MainPage::ClickHandler()
のように変化するのがポイント。
全体像
<Page
x:Class="BlankApp1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BlankApp1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel>
<TextBlock x:Name="clipboardText" Text="コピーするとここに表示される"/>
</StackPanel>
</Page>
#pragma once
#include <windows.h>
#include <unknwn.h>
#include <restrictederrorinfo.h>
#include <hstring.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.ApplicationModel.Activation.h>
#include <winrt/Windows.UI.Xaml.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Data.h>
#include <winrt/Windows.UI.Xaml.Interop.h>
#include <winrt/Windows.UI.Xaml.Markup.h>
#include <winrt/Windows.UI.Xaml.Navigation.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
#include "pch.h"
#include "MainPage.h"
using namespace winrt;
using namespace Windows::UI::Xaml;
using namespace Windows::ApplicationModel::DataTransfer;
using namespace Windows::Foundation;
namespace winrt::BlankApp1::implementation
{
MainPage::MainPage()
{
InitializeComponent();
// イベントハンドラの登録
Clipboard::ContentChanged([this](IInspectable const, IInspectable const) -> IAsyncAction
{
DataPackageView content = Clipboard::GetContent();
// クリップボードに入っているのはテキストか
if (content.Contains(StandardDataFormats::Text()))
{
// clipboardText に表示
hstring text = co_await content.GetTextAsync();
clipboardText().Text(text);
}
});
}
int32_t MainPage::MyProperty()
{
throw hresult_not_implemented();
}
void MainPage::MyProperty(int32_t /* value */)
{
throw hresult_not_implemented();
}
}
実行
ビルドして実行してみよう。ウィンドウには コピーするとここに表示される
と表示されているはずだ。
この状態で、メモ帳などを使ってクリップボードにテキストをコピーすると、うまくいけば表示がコピーした文字列に変わる。
感想
Windows の C++ プログラミングに慣れていると、あまりの簡単さに驚く。プログラムも従来のものより非常に短く簡潔である。
しかし箱庭から出るにはどのようにしたらいいのかが分からない。Microsoft.Data.Sqlite
のような Windows
名前空間以外のものはどのように使うのか。地道に COM で操作するのか。
長らく Windows プログラミングの座は C# に奪われていたが、これだけ簡単になれば多少は揺り戻しがあるかもしれない。いうても C++ そのものの分かりづらさ、例えば文法エラーが何を言っているのか分からないという問題があり、主力になることはないと思う、と言い訳した上で、いい時代になったと喜びたい。C++ の簡単なツールにちょっと GUI を付けたいなら C++/WinRT という選択肢ができた。