Windows.UI.Composition で DirectComposition やスワップチェーンのサンプルを追いかけていると、突然 ICompositor という見慣れないインターフェイスが登場し、「公式ドキュメントはどこ?」「Compositor と何が違うの?」と戸惑うことがよくあります。この記事では、その正体と実務での付き合い方を整理します。
ICompositor のドキュメントが見つからない理由
結論:ドキュメントは「Compositor クラス」に集約されている
まず最初に押さえておきたいのは、一般的なアプリ開発者が読むべき公式ドキュメントは Windows.UI.Composition.Compositor クラス に集約されている、という点です。Compositor は「アプリとシステムコンポジタのセッションを管理し、Windows.UI.Composition 名前空間に属する各種オブジェクトを生成するファクトリ」として定義されています。
一方で Windows.UI.Composition.ICompositor というインターフェイスそのものを直接説明したページは公開されていません。実際に Microsoft Q&A でも「ICompositor のドキュメントはどこ?」という質問に対して、「Compositor クラスのドキュメントが、ICompositor を含むすべてのインターフェイスのドキュメントだと思ってほしい」 と回答されています。
つまり、
ICompositor自体は存在しているが、あくまで裏側(ABI)のインターフェイス- アプリからは Compositor クラスだけを意識すればよい
というスタンスになっています。
Windows.UI.Composition 名前空間と Compositor の役割
Windows.UI.Composition 名前空間は、その名のとおり「UI のコンポジション(合成)レイヤー」を扱う API 群です。Visual や Brush、Effect、Animation など、描画やアニメーションに関する軽量なオブジェクトをまとめて提供しており、XAML や WinUI の一段下にあるビジュアルレイヤーを直接制御できます。
この中核にいるのが Compositor で、ざっくり言うと次のような役割を持っています。
- Visual や Brush、Animation、Effect などのインスタンスを生成するファクトリ
- アプリとシステムコンポジタ(DWM の一部)のセッションを管理
- 自分が生成したオブジェクトのライフタイム管理
「Composition を使いたい」と思ったら、まずは Compositor を 1 つ作って、そこから Visual ツリーを生やしていく、というのが基本パターンです。
runtimeclass と既定インターフェイスとしての ICompositor
WinRT のメタデータでは、runtimeclass Compositor が定義され、その default interface(既定インターフェイス) として Windows.UI.Composition.ICompositor が紐付いています。Wine などの IDL を見ると、runtimeclass Windows::UI::Composition::Compositor { [default] interface Windows.UI.Composition.ICompositor; } のような形で定義されていることが確認できます。
この default interface が、言語プロジェクション(C++/WinRT や C# の Windows ランタイムラッパー)から見た「Compositor の中身」です。開発者はクラス(Compositor)しか見ていなくても、裏ではこの ICompositor を通じて OS と会話している……という構造になっています。
| 名前 | 種類 | 役割 | ドキュメントの場所 |
|---|---|---|---|
Windows.UI.Composition.Compositor | WinRT クラス | Visual / Brush / Animation などの生成、セッション管理 | Compositor クラスの API リファレンス |
Windows.UI.Composition.ICompositor | WinRT インターフェイス(ABI) | Compositor の既定インターフェイス。言語プロジェクション内部で利用 | 単体ページは公開されておらず、Compositor クラスに統合されていると考える |
ICompositorInterop | ネイティブ Interop インターフェイス | スワップチェーンやグラフィックスデバイスから Composition サーフェスを生成 | windows.ui.composition.interop.h |
なぜ ICompositor をやめて Compositor に変えたら動いたのか
事例:Composition Swapchain サンプルの Example 3
問題になっているのは、Win32 用の「Composition swapchain code examples」にある Example 3「Binding a presentation surface to a Windows.UI.Composition surface brush」です。このサンプルでは引数として ICompositor* を受け取り、そこから ICompositorInterop を QueryInterface し、さらに CreateCompositionSurfaceForHandle などを呼び出しています。
Q&A では、このサンプルを真似して ICompositor を使っていたところ初期化エラーになり、型を Compositor に変えたら動作した、という報告がありました。その回答では次のような趣旨が説明されています。
- サンプル中の
ICompositorはwinrt::Windows::UI::Composition::ICompositorではなく、ABI::Windows::UI::Composition::ICompositor(低レベル ABI) を指している - WinRT API はクラスベースだが、複数言語から使えるように内部では COM/ABI インターフェイスの集合体として実装されている
- 言語プロジェクション(C++/WinRT など)は、この ABI インターフェイスを「クラス」に見えるようラッピングしている
つまり、サンプルは「言語プロジェクションを素通りして ABI を直接触っている」コードであり、C++/WinRT から同じ記述をすると世界線がずれる、というのが原因です。
WinRT のクラス APIと ABI インターフェイスの関係
WinRT の世界はざっくり次の 3 層に分けて考えると整理しやすくなります。
| 層 | 例 | 誰が使うか | アプリ開発者が意識するか |
|---|---|---|---|
| 言語プロジェクション | winrt::Windows::UI::Composition::CompositorWindows.UI.Composition.Compositor (C#) | 通常のアプリ開発者 | ここだけを意識すればよい |
| WinRT クラス / 既定インターフェイス | runtimeclass Compositorinterface ICompositor | ツール、言語ランタイム、プロジェクション | 通常は意識不要 |
| COM/ABI インターフェイス | ABI::Windows::UI::Composition::ICompositorICompositorInterop | 低レベル Interop や OS 実装 | ごく一部の Interop コードのみ |
C++/WinRT や C# で普段触っているのは一番上の「言語プロジェクション」の層です。ここで Compositor を生成すると、内部では
- COM/ABI レベルの
ICompositorインスタンスを生成 - それを C++/WinRT がラッピングして
winrt::Windows::UI::Composition::Compositorとして公開
といった処理が自動的に行われます。開発者は Compositor クラスを使うだけで、裏の ICompositor を意識する必要はありません。
なぜ Compositor に書き換えると正常動作するのか
では、なぜ ICompositor を Compositor に変えたらエラーが消えたのでしょうか。これは次のように理解できます。
- サンプルは「すでに初期化済みの
ICompositor*が渡ってくる」前提のコード - しかし C++/WinRT で
ICompositorをそのまま型として使うと、適切なインスタンスを生成する手段がそもそも用意されていない - 結果として「中身のないポインタ」「正しく初期化されていないオブジェクト」を握ったまま Interop を呼び出し、エラーになる
Compositorクラスに書き換えると、C++/WinRT が COM/ABI レベルまで含めて正しく初期化してくれるので、Interop 側から見ても妥当なICompositorが渡ってくる
Microsoft Q&A でも、「Windows.UI.Composition.ICompositor は存在するが隠されており、Windows.UI.Composition.Compositor を使えばプロジェクションが内部で正しいインターフェイスを解決する」と説明されています。
ここから言える実務的な指針はシンプルです。
- アプリコードでは
Compositorクラスを使う - Interop が必要な箇所だけ
compositor.as<ICompositorInterop>()のようにして ABI インターフェイスを取り出す ICompositorを直接メンバー変数や引数の型に使う必要はほぼない
Interop が必要なときにだけ登場する ICompositor* 系インターフェイス
ICompositorInterop:スワップチェーンと Composition の橋渡し
DirectX や DXGI スワップチェーンと Composition をつなぐ際に主役になるのが ICompositorInterop です。ドキュメントでは「スワップチェーン サーフェスやグラフィックス デバイスを作成するネイティブ相互運用インターフェイス」と説明されています。
代表的なメソッドは次のとおりです。
CreateCompositionSurfaceForSwapChain:DXGI スワップチェーンからICompositionSurfaceを生成CreateCompositionSurfaceForHandle:DirectComposition のDCompositionCreateSurfaceHandleで作った HANDLE からサーフェスを生成CreateGraphicsDevice:D3D デバイスからCompositionGraphicsDeviceを生成
Windows.UI.Composition と DirectX/Direct2D のネイティブ Interop の全体像については「Composition native interoperation with DirectX and Direct2D」のドキュメントが詳しいので、一度目を通しておくと理解がスムーズです。
ICompositorDesktopInterop:HWND に DesktopWindowTarget を作る
デスクトップアプリ(Win32 / WPF / WinForms)で Windows.UI.Composition を使う場合、HWND に対して描画ターゲットを作る必要があります。そのためのインターフェイスが ICompositorDesktopInterop です。
メソッドの例:
CreateDesktopWindowTarget(HWND, BOOL, DesktopWindowTarget**):指定した HWND に結び付いたDesktopWindowTargetを生成
このインターフェイスも windows.ui.composition.interop.h に含まれています。
IDesktopWindowTargetInterop:DesktopWindowTarget と HWND の相互参照
すでに作成済みの DesktopWindowTarget から HWND を取り出したり、別の Interop パターンで関連付けを行いたいときには IDesktopWindowTargetInterop を使います。こちらも通常のアプリではそう頻繁に触るものではなく、フレームワーク的なコードを書くときに登場する程度です。
まとめると、Interop 系インターフェイスは次のように使い分けるイメージです。
| インターフェイス | 主な役割 | よくある用途 |
|---|---|---|
ICompositorInterop | DXGI スワップチェーン / HANDLE から Composition サーフェスを作る | 動画プレーヤー、キャプチャ、ゲームなどの高速描画 |
ICompositorDesktopInterop | HWND に DesktopWindowTarget を作成 | Win32 / WPF / WinForms + Composition のホスティング |
IDesktopWindowTargetInterop | DesktopWindowTarget と HWND の相互参照 | 既存ターゲットの再利用、複雑なホスティング |
いずれも「まずは Compositor を作り、そこから as<…>() で必要な Interop インターフェイスを取得する」という使い方が基本になります。
実装ステップ:DXGI スワップチェーンを Windows.UI.Composition に接続する
DirectComposition / Composition swapchain のシナリオで、DXGI スワップチェーンを Windows.UI.Composition の Visual ツリーに乗せる一般的な流れを、実装ステップとして整理してみます。
Compositor を生成する(デスクトップなら DispatcherQueue に注意)
UWP アプリであれば、単に new Compositor() するだけでほぼ問題ありませんが、デスクトップアプリ(特に WPF や Win32)では DispatcherQueue が無い状態で Compositor を作ると例外や HRESULT エラーになる ことがあります。WPF での事例として、Dispatcher 設定を忘れて new Windows.UI.Composition.Compositor() が失敗するケースが Stack Overflow でも報告されています。
実務上は次のような順番を守ると安定します。
- STA スレッドでアパートメントを初期化(
CoInitializeExまたはwinrt::init_apartment) - そのスレッド上で
DispatcherQueueControllerを生成 - 同じスレッドで
Compositorを生成
Compositor から ICompositorInterop を取得する
C++/WinRT の場合、Compositor クラスからは as<T>() で Interop インターフェイスを取り出せます。
// C++/WinRT 例
#include <winrt/Windows.UI.Composition.h>
#include <windows.ui.composition.interop.h>
using namespace winrt;
using namespace Windows::UI::Composition;
void SetupSwapChainInterop(Compositor const& compositor, IDXGISwapChain1* swapChain)
{
// 1. Interop インターフェイスを取得
winrt::com_ptr<ICompositorInterop> interop = compositor.as<ICompositorInterop>();
// 2. スワップチェーンから ICompositionSurface を生成
winrt::com_ptr<ICompositionSurface> surface;
winrt::check_hresult(
interop->CreateCompositionSurfaceForSwapChain(
swapChain,
surface.put())
);
// 3. SurfaceBrush を作成して Visual にバインド
auto surfaceBrush = compositor.CreateSurfaceBrush();
surfaceBrush.Surface(surface.as<ICompositionSurface>());
auto sprite = compositor.CreateSpriteVisual();
sprite.Brush(surfaceBrush);
// あとはこの SpriteVisual をルートの Visual ツリーにぶら下げる
}
ポイントは、あくまでも Compositor を起点にしていることです。ICompositor を直接メンバーとして持つのではなく、Compositor から必要なときに ICompositorInterop を引き出す、というスタイルにしておくと、ライフタイム管理や将来の API 変更にも耐性が高くなります。
HWND をターゲットにする(DesktopWindowTarget)
デスクトップアプリでは、作った Visual ツリーをどのウィンドウに描画するかを指定する必要があります。ここで使うのが ICompositorDesktopInterop です。
#include <winrt/Windows.UI.Composition.Desktop.h>
using namespace Windows::UI::Composition::Desktop;
DesktopWindowTarget CreateTargetForHwnd(Compositor const& compositor, HWND hwnd)
{
winrt::com_ptr<ICompositorDesktopInterop> desktopInterop =
compositor.as<ICompositorDesktopInterop>();
DesktopWindowTarget target{ nullptr };
winrt::check_hresult(
desktopInterop->CreateDesktopWindowTarget(
hwnd,
TRUE, // topmost
reinterpret_cast<IDesktopWindowTarget**>(
winrt::put_abi(target))
)
);
return target;
}
得られた DesktopWindowTarget に対して
target.Root(spriteVisual);
のように Visual ツリーのルートを設定すれば、そのウィンドウに対して Composition の描画が行われるようになります。
DirectComposition との共存:Composition swapchain サンプルの読み解き方
先ほど見た swapchain サンプルの Example 3 では、次のような形で ICompositor* を引数に取っています。
- この
ICompositor*は ABI レベルのインターフェイス を前提としている - DirectComposition 用のコードと対称性を持たせるため、WinRT のプロジェクションを通さず生の COM として扱っている
- C++/WinRT や C# から同じ関数を呼ぶ場合は、引数を
Compositorに変える、あるいは「ラッパー関数」を書いた方が自然
つまり「サンプルのまま ICompositor* を真似する」のではなく、「自分のアプリでは Compositor を起点にラップし直す」くらいの気持ちで読み替えると安全です。
よくあるハマりどころとベストプラクティス
ICompositor を直接使おうとしてしまう
典型的な NG パターンは次のようなものです。
| やりがちな書き方(NG) | 推奨される書き方(OK) |
|---|---|
// ABI の ICompositor をメンバーにしてしまう ABI::Windows::UI::Composition::ICompositor* m_compositor; | // C++/WinRT の Compositor を持つ winrt::Windows::UI::Composition::Compositor m_compositor; |
// ICompositor を直接 new / CoCreate しようとする // CLSID も IID も公開されておらず、そもそも正しく生成できない | // 言語プロジェクションに任せる m_compositor = Compositor{}; |
WinRT のクラスは「new したら中で勝手に COM オブジェクトを紐付けてくれる」存在です。COM レベルの ICompositor を自分で new する必要も、できる手段も提供されていません。ここに逆らおうとすると、初期化されていないポインタや ABI の齟齬に悩まされることになります。
DispatcherQueue / スレッドアフィニティを無視する
デスクトップアプリで Windows.UI.Composition を使う際、DispatcherQueue の有無 と スレッドアフィニティ は非常に重要です。
- UWP ではフレームワークが UI スレッドと Dispatcher を用意してくれるため、あまり意識しなくてよい
- Win32 / WPF では、自分で DispatcherQueue を用意しないと Compositor が作成時に失敗することがある
- Composition オブジェクトは基本的に「作ったスレッド」に紐付くため、別スレッドから触ると例外や HRESULT エラー(
RPC_E_WRONG_THREADなど)になる
対策としては:
- Composition を扱う専用 UI スレッドを 1 本決め、そのスレッドで Compositor を生成する
- 必要であればそのスレッドに DispatcherQueue を紐付ける
- 他スレッドからはメッセージやキューを介して間接的に操作する
名前空間の違いを混同する:Windows.UI.Composition vs Microsoft.UI.Composition
Windows 10 の UWP 世代では Windows.UI.Composition が主流でしたが、現在の新規デスクトップ開発では Windows App SDK / WinUI 3 の Microsoft.UI.Composition を使うのが推奨されるケースも増えています。
両者は概念的にはほぼ同じで、クラス名も Compositor、SpriteVisual などほぼ共通です。ただし:
- 名前空間が異なる(
Windows.UI.CompositionvsMicrosoft.UI.Composition) - Interop 用ヘッダーも
windows.ui.composition.interop.hとmicrosoft.ui.composition.interop.hで分かれている - Windows App SDK のバージョン管理や配布方法が異なる
記事タイトルにある ICompositor の話はどちらにも基本的に当てはまり、どちらも「クラス API(Compositor)を使う。ICompositor は裏方」という考え方で問題ありません。
Microsoft.UI.Composition を使う場合の ICompositor との付き合い方
Windows App SDK / WinUI 3 の世界でも、構造はほぼ同じです。
Microsoft.UI.Composition.Compositorクラスが中心- Interop 用に
Microsoft.UI.Composition.Interop.ICompositorInteropなどが存在 - こちらも「アプリコードでは Compositor を使い、Interop が必要な箇所だけ Interop インターフェイスを as で取り出す」というスタンス
将来的に UWP から WinUI 3 へ移行する場合でも、「Composition の中心に Compositor がいて、裏で ICompositor が動いている」 という頭の中のモデルはそのまま流用できます。
「ICompositor のドキュメントはどこ?」への実務的な回答
最後に、冒頭の質問に対する「実務的な答え」を整理しておきます。
どこを見るべきか
- Compositor クラスの公式ドキュメントを見る
- Windows.UI.Composition.Compositor(UWP)
- Microsoft.UI.Composition.Compositor(Windows App SDK / WinUI 3)
- Interop が必要な場面だけ、
windows.ui.composition.interop.h/microsoft.ui.composition.interop.hのドキュメントを参照する - ICompositor 単体のページは探さなくてよい(Compositor の説明で十分)
なぜ Compositor に置き換えるとエラーが消えるのか
- WinRT は COM/ABI を土台にしたクラスベース API
Compositorクラスを使うと、プロジェクションが裏でICompositorを含む必要なインターフェイスを自動的に解決してくれる- サンプルコードの
ICompositor*はABI::Windows::UI::Composition::ICompositorを前提にしており、C++/WinRT の世界にそのまま持ち込むと初期化パスが欠落する - Compositor クラスに置き換えることで、正しい ABI オブジェクトが生成され、Interop 側から見ても有効な ICompositor として扱えるようになる
いつ ICompositor / Interop を意識するべきか
通常の UWP / WinUI アプリであれば、ICompositor を意識する必要はほぼありません。意識するのは次のようなケースだけです。
- DXGI スワップチェーンや DirectComposition のサーフェスを Composition にブリッジするとき
- HWND ベースのデスクトップアプリに Composition のビジュアルレイヤーをホストするとき
- UWP / WinUI とは別の自前フレームワークで Composition をラップする「フレームワーク側のコード」を書いているとき
それ以外の場面では、「ICompositor という名前を見かけたら、ああ裏側の既定インターフェイスのことね」と軽く流してしまって構いません。
まとめ:ICompositor は裏方、アプリからは Compositor に集中しよう
ここまでの内容を、コンパクトに振り返ってみます。
- ICompositor は Compositor の既定インターフェイスであり、WinRT の ABI レベルの“裏方”
- 公式ドキュメントは Compositor クラスに集約されており、ICompositor 単体の解説ページは存在しない(=探さなくてよい)
- 言語プロジェクション(C++/WinRT / C#)は、Compositor クラスの裏で ICompositor を含むインターフェイスを自動的に解決してくれる
- Swapchain / DirectComposition との Interop では ICompositorInterop / ICompositorDesktopInterop などの ABI インターフェイスを、
compositor.as<T>()で取り出して使う - デスクトップでは DispatcherQueue やスレッドアフィニティに注意しつつ、「Compositor を 1 つ作り、そこからすべての Composition オブジェクトを生やす」という設計にするとトラブルが少ない
- 新規開発なら Microsoft.UI.Composition(Windows App SDK / WinUI 3) の採用も検討しつつ、考え方は Windows.UI.Composition と共通でよい
ひとことで言えば、
「ICompositor は“裏方のインターフェイス”であり、アプリ開発者は Compositor クラスのドキュメントを読み、Compositor を中心にコードを書くのが正解」
となります。Interop が必要になったときだけ落ち着いて .as<ICompositorInterop>() を取り出し、必要最小限の箇所にだけ ABI レベルの知識を閉じ込めておく――これが実務での一番扱いやすいスタイルです。

コメント