C++/WinRT での Windows ランタイム API の作成と使用に関する質問への回答です。
重要
C++/WinRT に関するリリース ノートについては、「C++/WinRT 2.0 の新機能と変更点」を参照してください。
注意
質問の内容が、表示されたエラー メッセージに関するものである場合は、「C++/WinRT に関する問題のトラブルシューティング」のトピックも参照してください。
C++/WinRT サンプル アプリはどこにありますか?
C++/WinRT サンプル アプリに関するページを参照してください。
C++/WinRT プロジェクトのターゲットを Windows SDK の後のバージョンに変更するにはどうすればよいですか?
C++/WinRT 2.0 に移行した後、新しいプロジェクトがコンパイルされないのはなぜですか?
詳細な変更点 (破壊的変更を含む) については、「News, and changes, in C++/WinRT 2.0 (C++/WinRT 2.0 の新機能と変更点)」を参照してください。 たとえば、Windows ランタイム コレクションで範囲ベースの for
を使用している場合は、#include <winrt/Windows.Foundation.Collections.h>
が必要になります。
新しいプロジェクトがコンパイルされないのはなぜですか? 私は Visual Studio 2017 (Version 15.8.0 以降) と SDK Version 17134 を使用しています
Visual Studio 2017 (Version 15.8.0 以降) を使用し、Windows SDK Version 10.0.17134.0 (Windows 10 Version 1803) をターゲットにしている場合は、C++/WinRT プロジェクトを新しく作成すると、エラー "'エラー C3861: 'from_abi': 識別子が見つかりませんでした" と、base.h から発生する他のエラーでコンパイルに失敗することがあります。 解決策として、Windows SDK のより新しい (より準拠度の高い) バージョンを対象とするか、プロジェクト プロパティ [C/C++]>[言語]>[準拠モード: いいえ] に設定します (また、/permissive- がプロジェクト プロパティ [C/C++]>[コマンド ライン] ([追加オプション] の下) に表示される場合は、削除してください)。
ビルド エラー "The C++/WinRT VSIX no longer provides project build support. Please add a project reference to the Microsoft.Windows.CppWinRT Nuget package" (C++/WinRT VSIX はプロジェクトのビルドをサポートしなくなりました。Microsoft.Windows.CppWinRT Nuget パッケージへのプロジェクト参照を追加してください) を解決するにはどうすればよいですか?
Microsoft.Windows.CppWinRT NuGet パッケージを自分のプロジェクトにインストールします。 詳細については、「VSIX 拡張機能の以前のバージョン」を参照してください。
NuGet パッケージでビルド サポートをカスタマイズするにはどうすればよいですか?
C++/WinRT ビルド サポート (プロパティ/ターゲット) については、Microsoft.Windows.CppWinRT NuGet パッケージの readme に記載されています。
C++/WinRT Visual Studio Extension (VSIX) の要件は何ですか?
VSIX 拡張機能の Version 1.0.190128.4 以降については、C++/WinRT の Visual Studio のサポートに関する記事を参照してください。 その他のバージョンについては、「VSIX 拡張機能の以前のバージョン」を参照してください。
ランタイム クラスとは
ランタイム クラスは、通常実行可能な境界を越えて、モダン COM インターフェイスを介してアクティブ化でき、使用できる型です。 ただし、ランタイム クラスは、それを実装するコンパイル ユニット内でも使用できます。 インターフェイス定義言語 (IDL) でランタイム クラスを宣言し、C++/WinRT を使用した標準の C++ で実装することができます。
プロジェクションされる型と実装型とは
Windows ランタイム クラス (ランタイム クラス) を使用する場合のみ、プロジェクションされる型を排他的に処理します。 C++/WinRT は言語のプロジェクションであるため、プロジェクションされる型は、C++/WinRT を使用した C++ にプロジェクションされる Windows ランタイムの画面に表示されます。 詳細については、「C++/WinRT での API の使用」を参照してください。
実装型にはランタイム クラスの実装が含まれるため、ランタイム クラスを実装するプロジェクトでのみ利用可能です。 ランタイム クラス (Windows ランタイム コンポーネント プロジェクト、つまり XAML UI を使用するプロジェクト) を実装するプロジェクトで作業している場合、ランタイム クラスの実装型と C++/WinRT にプロジェクションされたランタイム クラスを表すプロジェクションされる型との違いを習熟することが重要です。 詳細については、「C++/WinRT での API の作成」を参照してください。
使用するランタイム クラスの IDL でコンストラクターを宣言する必要性
ランタイム クラスが実装するコンパイル ユニット (Windows ランタイム クライアント アプリで一般的に使用するための Windows ランタイム コンポーネント) 以外で使用されるように設計されている場合のみ IDL のコンストラクターを宣言する際の目的と影響の詳細については、「ランタイム クラス コンストラクター」を参照してください。
コンパイラーに "C3779: consume_Something: 'auto' を返す関数を、定義より前に使用することはできません" というエラーが発生するのはなぜですか。
対応する名前空間ヘッダー ファイルを最初に含めずに、Windows ランタイム オブジェクトを使用しています。 API の名前空間で付けられた名前のヘッダーを含めてから、リビルドしてください。 詳細については、「C++/WinRT プロジェクション ヘッダー」を参照してください。
リンカーで "LNK2019:Unresolved external symbol" (外部シンボルは未解決です) エラーが発生するのはなぜですか?
未解決のシンボルが RoInitialize などの Windows ランタイムの自由関数である場合、WindowsApp.lib の包括的なライブラリを明示的にプロジェクトにリンクする必要があります。 C++/WinRT プロジェクションは、これらの一部の自由 (非メンバー) 関数とエントリ ポイントに依存します。 アプリケーションでいずれかの C++/WinRT Visual Studio Extension (VSIX) プロジェクト テンプレートを使用する場合は、WindowsApp.lib
が自動的にリンクされます。 それ以外の場合、プロジェクトのリンク設定を使用して含めるか、またはソース コードでそれを行うことができます。
#pragma comment(lib, "windowsapp")
代替の静的リンク ライブラリではなく WindowsApp.lib をリンクしてリンカー エラーを解決することが重要です。そうしないと、アプリケーションは、Visual Studio および Microsoft Store で提出の検証に使用される Windows アプリ認定キット テストに合格しません (つまり、結果として、アプリケーションは Microsoft Store に正常に取り込まれません)。
未解決のシンボルがコンストラクターの場合は、構築するクラスの名前空間ヘッダー ファイルを含め忘れた可能性があります。 クラスの名前空間の名前が付いたヘッダーを含めて再構築してください。 詳細については、「C++/WinRT プロジェクション ヘッダー」を参照してください。
"クラスが登録されていません" という例外が発生するのはなぜですか?
この場合の現象としては、ランタイム クラスを構築するか、または静的メンバーにアクセスすると、実行時に HRESULT の値が REGDB_E_CLASSNOTREGISTERED である例外がスローされます。
原因の 1 つは、Windows ランタイム コンポーネントを読み込むことができないことです。 コンポーネントの Windows ランタイム メタデータ ファイル (.winmd
) の名前がコンポーネント バイナリ (.dll
) の名前と同じであることを確認してください。この名前は、プロジェクトの名前、およびルート名前空間の名前でもあります。 また、Windows ランタイム メタデータとバイナリが、ビルド プロセスによって使用中のアプリの Appx
フォルダに正しくコピーされていることを確認してください。 さらに、使用中のアプリの AppxManifest.xml
(および Appx
フォルダ内) に、アクティブ化可能なクラスとバイナリ名を正しく宣言している <InProcessServer> 要素が含まれていることを確認してください。
均一の構築 このエラーは、投影された型のコンストラクターのいずれか (std::nullptr_t コンストラクター以外) を使ってローカルに実装されているランタイム クラスをインスタンス化しようとした場合にも、発生する可能性があります。 それを行うには、均一の構築と呼ばれることがよくある C++/WinRT 2.0 の機能が必要です。 その機能にオプトインする場合は、詳細とコード例について、「均一コンストラクション、実装への直接アクセス」を参照してください。
均一の構築を必要と "しない" ローカルに実装されたランタイム クラスをインスタンス化する方法については、「XAML コントロール: C++/WinRT プロパティへのバインド」をご覧ください。
Windows::Foundation::IClosable を実装するかどうかとその方法
自身のデストラクターのリソースを解放するランタイム クラスを使用していて、そのランタイム クラスが、実装するコンパイル ユニット (これは、Windows ランタイム クライアント アプリで一般的に使用するための Windows ランタイム コンポーネントです) の外部で使われるように設計されている場合は、IClosable も実装して、確定的な終了処理がない言語でランタイム クラスの使用をサポートすることをお勧めします。 デストラクター、IClosable::Close、または両方が呼び出されたときにリソースが解放されるようにしてください。 IClosable::Close は必要に応じて呼び出すことができます。
使用するランタイム クラスで IClosable::Close を読み出す必要性
IClosable は、確定的な終了処理がない言語をサポートするために用意されています。 そのため、一般に、C++/WinRT から IClosable::Close を呼び出す必要はありません。 ただし、その一般的な規則に対して以下の例外を考慮してください。
- IClosable::Close の呼び出しが必要になるような、シャットダウンの競合や破壊的に近い支配が関係する場合はほとんどありません。 たとえば、Windows.UI.Composition を使用していて、設定順序でオブジェクトを破棄する場合、その代わりとして、C++/WinRT ラッパーを破棄することができます。
- オブジェクトに対する残りの最後の参照を自分が持っていることが確実でない場合 (オブジェクトを他の API に渡し、そこで参照が保持されている可能性があるため)、IClosable::Close を呼び出すのはよいアイデアです。
- 不明な場合は、破棄時にラッパーによって呼び出されるのを待つのではなく、手動で IClosable::Close を呼び出すのが安全です。
それで、自分が最後の参照を持っていることが分かっている場合は、ラッパー デストラクターに処理させることができます。 最後の参照が消える前にクローズする必要がある場合は、Close を呼び出す必要があります。 例外を適切に処理するには、resource-acquisition-is-initialization (RAII) 型で Close を実行する必要があります (アンワインド時にクローズされるようにする)。 C++/WinRT には unique_close ラッパーはありませんが、自分で作成することができます。
LLVM/Clang を使用して C++/WinRT でコンパイルすることはできますか。
C++/WinRT の LLVM および Clang ツール チェーンはサポートしていませんが、C++/WinRT の標準への準拠を検証するためにそれを内部で使用します。 たとえば、マイクロソフトが内部で行っている内容をエミュレートする場合は、次に説明するような実験を試してみることができます。
LLVM ダウンロード ページに移動し、[LLVM 6.0.0 のダウンロード]>[事前ビルドされたバイナリ] を探し、[Windows の Clang (64 ビット)] をダウンロードします。 インストール中に、コマンド プロンプトから起動できるように、PATH システム変数に LLVM を追加することを選択します。 この実験の目的上、"Failed to find MSBuild toolsets directory (MSBuild ツールセット ディレクトリの検索に失敗しました)" または "MSVC integration install failed (MSVC 統合インストールに失敗しました)" というエラーが表示された場合には無視できます。 LLVM/Clang を起動するさまざまな方法があります。次の例は、1 つの方法のみを示しています。
C:\ExperimentWithLLVMClang>type main.cpp
// main.cpp
#pragma comment(lib, "windowsapp")
#pragma comment(lib, "ole32")
#include <winrt/Windows.Foundation.h>
#include <stdio.h>
#include <iostream>
using namespace winrt;
int main()
{
winrt::init_apartment();
Windows::Foundation::Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
std::wcout << rssFeedUri.Domain().c_str() << std::endl;
}
C:\ExperimentWithLLVMClang>clang-cl main.cpp /EHsc /I ..\.. -Xclang -std=c++17 -Xclang -Wno-delete-non-virtual-dtor -o app.exe
C:\ExperimentWithLLVMClang>app
windows.com
C++/WinRT では C++17 標準の機能を使用するため、そのサポートを受けるために必要なコンパイラ フラグを使用する必要があります。そのようなフラグはコンパイラによって異なります。
Visual Studio は、マイクロソフトがサポートし、C++/WinRT 用に推奨する開発ツールです。 C++/WinRT の Visual Studio のサポートに関する記事を参照してください。
読み取り専用プロパティ用に生成された実装関数に const 修飾子がないのはなぜですか?
MIDL 3.0 で読み取り専用プロパティを宣言すると、cppwinrt.exe
ツールによって const
で修飾された実装関数 (this ポインターを定数として扱う const 関数) が自動的に生成されることが期待されるかもしれません。
確かに、可能な限り const を使用することをお勧めしますが、cppwinrt.exe
ツール自体では、どの実装関数がおそらく const で、どれがそうでないかについて推論が試行されません。 この例のように、任意の実装関数を const にすることを選択できます。
struct MyStringable : winrt::implements<MyStringable, winrt::Windows::Foundation::IStringable>
{
winrt::hstring ToString() const
{
return L"MyStringable";
}
};
実装で何らかのオブジェクトの状態を変更する必要があると判断した場合は、ToString の const
修飾子を削除できます。 ただし、各メンバー関数を const または non-const (両方ではなく) にします。 つまり、const
の実装関数をオーバーロードしないでください。
実装関数とは別に、const が関与するもう 1 つの場所として Windows ランタイム関数のプロジェクションがあります。 このコードを見てみましょう。
int main()
{
winrt::Windows::Foundation::IStringable s{ winrt::make<MyStringable>() };
auto result{ s.ToString() };
}
前述の ToString の呼び出しの場合、Visual Studio の [宣言へ移動] コマンドを実行すると、Windows ランタイム IStringable::ToString から C++/WinRT へのプロジェクションはこのように表示されます。
winrt::hstring ToString() const;
プロジェクションに対する関数は、それらの実装をどのように修飾するかにかかわらず、const です。 バックグラウンドでは、プロジェクションによってアプリケーション バイナリ インターフェイス (ABI) が呼び出されます。これは COM インターフェイス ポインターを介した呼び出しに相当します。 プロジェクションが実行された ToString が対話する唯一の状態は、その COM インターフェイス ポインターです。また、確かにそのポインターを修正する必要がないので、関数は const です。 そのため、呼び出している IStringable の参照については何も変わらないことが保証されます。また、IStringable への const 参照があっても確実に ToString を呼び出すことができます。
これらの const
の例は、C++/WinRT プロジェクションの実装の詳細と実装です。これらによって、有益なコードの検疫が構成されます。 COM と Windows ランタイム ABI (メンバー関数用) のどちらにも const
のようなものはありません。
C++/WinRT バイナリのコード サイズを減らすための推奨事項はありますか?
Windows ランタイム オブジェクトを使用する場合は、必要以上に多くのバイナリ コードが生成され、アプリケーションに悪影響が及ぶ可能性があるため、次のようなコーディング パターンは避けてください。
anobject.b().c().d();
anobject.b().c().e();
anobject.b().c().f();
Windows ランタイム環境では、c()
の値、または間接指定 ('.') を介して呼び出される各メソッドのインターフェイスをコンパイラでキャッシュすることはできません。 介入しないと、結果として仮想呼び出しと参照カウントのオーバーヘッドが増えます。 上記のパターンによって、厳密に必要なコードの 2 倍が容易に生成される可能性があります。 代わりに、できる限り次のパターンをお勧めします。 生成されるコードがはるかに少なくなり、実行時のパフォーマンスも大幅に向上します。
auto a{ anobject.b().c() };
a.d();
a.e();
a.f();
上記の推奨パターンは、C++/WinRT だけでなく、すべての Windows ランタイム言語のプロジェクションにも当てはまります。
文字列を型に変換するにはどうすればよいですか (たとえばナビゲーション用に)?
ナビゲーション ビューのコード例 (主に C#) の末尾には、この実行方法を示す C++/WinRT コード スニペットがあります。
GetCurrentTime および TRY でのあいまいさを解決するにはどうすればよいですか?
ヘッダー ファイル winrt/Windows.UI.Xaml.Media.Animation.h
では GetCurrentTime という名前のメソッドが宣言されているのに対し、windows.h
では (winbase.h
を介して) GetCurrentTime という名前のマクロが定義されています。 その 2 つが競合すると、C++ コンパイラで次のエラーが発生します。"エラー C4002: 関数に似たマクロ呼び出し 'GetCurrentTime' の引数が多すぎます"。
同様に、winrt/Windows.Globalization.h
では TRY という名前のメソッドが宣言されているのに対し、afx.h
では TRY という名前のマクロが定義されています。 これらが競合すると、C++ コンパイラで次のエラーが発生します。"エラー C2334: '{' の前に予期しないトークンがありました。関数の本体は無視されます"。
一方または両方の問題を解決するには、次のようにすることができます。
#pragma push_macro("GetCurrentTime")
#pragma push_macro("TRY")
#undef GetCurrentTime
#undef TRY
#include <winrt/include_your_cppwinrt_headers_here.h>
#include <winrt/include_your_cppwinrt_headers_here.h>
#pragma pop_macro("TRY")
#pragma pop_macro("GetCurrentTime")
シンボルの読み込みを高速化するにはどうすればよいですか?
Visual Studio で、[ツール]>[オプション]>[デバッグ]>[シンボル]> に移動し、"[指定したモジュールのみ]" のチェック ボックスをオンにします。 その後スタックの一覧で DLL を右クリックして、個々のモジュールを読み込むことができます。
注意
このトピックで質問の答えが見つからなかった場合は、Visual Studio C++ 開発者コミュニティにアクセスするか、c++-winrt
タグを Stack Overflow で使用することでヘルプが得られる場合があります。