C++/WinRT での Windows ランタイム API の作成と使用に関して考えられる質問に対する回答。
Important
C++/WinRT に関するリリース ノートについては、 C++/WinRT 2.0 のニュースと変更に関する記事を参照してください。
Note
確認したエラー メッセージについて質問がある場合は、「 C++/WinRT のトラブルシューティング 」トピックも参照してください。
C++/WinRT サンプル アプリはどこにありますか?
C++/WinRT サンプル アプリを参照してください。
C++/WinRT プロジェクトを新しいバージョンの Windows SDK に再ターゲットするにはどうすればよいですか?
C++/WinRT 2.0 に移行したので、新しいプロジェクトがコンパイルされないのはなぜですか?
C++/WinRT 2.0 の変更点 (破壊的変更を含む) の完全なセットについては、「ニュース」および「変更」を参照してください。 たとえば、Windows ランタイム コレクションで範囲ベースのforを使用している場合は、#include <winrt/Windows.Foundation.Collections.h>する必要があります。
新しいプロジェクトがコンパイルされないのはなぜですか? Visual Studio 2017 (バージョン 15.8.0 以降) と SDK バージョン 17134 を使用しています
Visual Studio 2017 (バージョン 15.8.0 以降) を使用していて、Windows SDK バージョン 10.0.17134.0 (Windows 10、 バージョン 1803)、新しく作成された C++/WinRT プロジェクトは、エラー "エラー C3861: 'from_abi': 識別子が見つかりません"、および base.h で発生したその他のエラーでコンパイルに失敗する可能性があります。 解決策は、Windows SDK の新しい (より準拠した) バージョンをターゲットにするか、プロジェクト プロパティ C/C++>Language>Conformance モードを設定することです(また、プロジェクト プロパティ C/C++> の [追加オプション] の下に /permissive- が表示される場合は削除します)。
ビルド エラー「C++/WinRT VSIX では、プロジェクトのビルド サポートが提供されなくなりました。」を解決するにはどうすればよいですか。 Microsoft.Windows.CppWinRT NuGet パッケージへのプロジェクト参照を追加してください"?
Microsoft.Windows.CppWinRT NuGet パッケージをプロジェクトにインストールします。 詳細については、 VSIX 拡張機能の以前のバージョンを参照してください。
NuGet パッケージのビルド サポートをカスタマイズするにはどうすればよいですか?
C++/WinRT のビルド サポート (props/targets) は、Microsoft.Windows.CppWinRT NuGet パッケージの readme に記載されています。
C++/WinRT Visual Studio 拡張機能 (VSIX) の要件は何ですか?
VSIX 拡張機能のバージョン 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: 未解決の外部シンボル" エラーが表示されるのはなぜですか?
未解決のシンボルが 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 を呼び出す必要があります。 例外安全にするには、リソース取得時初期化(RAII)型で Close を行う必要があります(これにより、アンワインド時に Close が実行されます)。 C++/WinRT には unique_close ラッパーはありませんが、独自のラッパーを作成できます。
LLVM/Clang を使用して C++/WinRT でコンパイルすることはできますか?
C++/WinRT 用の LLVM および Clang ツールチェーンはサポートされていませんが、C++/WinRT の標準準拠を検証するために内部的に使用します。 たとえば、内部的に行うことをエミュレートする場合は、以下に示すような実験を試すことができます。
LLVM ダウンロード ページに移動し、LLVM 6.0.0 のダウンロード>ビルド済みバイナリを探し、Windows用の Clang (64 ビット) をダウンロードします。 インストール時に、コマンド プロンプトから LLVM を呼び出すことができるように、PATH システム変数に LLVM を追加することを選択します。 この実験の目的上、"FAILed to find MSBuild toolsets directory" や "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修飾された実装関数を生成することが期待される場合があります (const 関数はこのポインターを const として扱います)。
可能な限り const を使用することをお勧めしますが、 cppwinrt.exe ツール自体は、どの実装関数が const であると考えられるか、そうでない可能性があるかを推論しようとはしません。 この例のように、実装関数を const にすることもできます。
struct MyStringable : winrt::implements<MyStringable, winrt::Windows::Foundation::IStringable>
{
winrt::hstring ToString() const
{
return L"MyStringable";
}
};
実装でオブジェクトの状態を変更する必要があると判断した場合は、const の修飾子を削除できます。 ただし、各メンバー関数を const または non-const のどちらかにし、両方を作成しないようにします。 つまり、 constで実装関数をオーバーロードしないでください。
実装関数とは別に、const が画像に入ってくる別の場所は、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 で、Tools>Options>Debugging>Symbols> の Load only specified modules をオンにします。 その後、スタック一覧で DLL を右クリックし、個々のモジュールを読み込むことができます。
Note
このトピックで質問に答えられなかった場合は、Visual Studio C++ 開発者コミュニティにアクセスするか、Stack Overflow の c++-winrt タグを使用してヘルプを見つけることができます。