ICompositorとCompositor徹底解説|Windows.UI.Compositionで迷わないための実践ガイド

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.CompositorWinRT クラスVisual / Brush / Animation などの生成、セッション管理Compositor クラスの API リファレンス
Windows.UI.Composition.ICompositorWinRT インターフェイス(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::Compositor
Windows.UI.Composition.Compositor (C#)
通常のアプリ開発者ここだけを意識すればよい
WinRT クラス / 既定インターフェイスruntimeclass Compositor
interface ICompositor
ツール、言語ランタイム、プロジェクション通常は意識不要
COM/ABI インターフェイスABI::Windows::UI::Composition::ICompositor
ICompositorInterop
低レベル Interop や OS 実装ごく一部の Interop コードのみ

C++/WinRT や C# で普段触っているのは一番上の「言語プロジェクション」の層です。ここで Compositor を生成すると、内部では

  1. COM/ABI レベルの ICompositor インスタンスを生成
  2. それを 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 系インターフェイスは次のように使い分けるイメージです。

インターフェイス主な役割よくある用途
ICompositorInteropDXGI スワップチェーン / HANDLE から Composition サーフェスを作る動画プレーヤー、キャプチャ、ゲームなどの高速描画
ICompositorDesktopInteropHWND に DesktopWindowTarget を作成Win32 / WPF / WinForms + Composition のホスティング
IDesktopWindowTargetInteropDesktopWindowTarget と 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 でも報告されています。

実務上は次のような順番を守ると安定します。

  1. STA スレッドでアパートメントを初期化(CoInitializeEx または winrt::init_apartment)
  2. そのスレッド上で DispatcherQueueController を生成
  3. 同じスレッドで Compositor を生成

Compositor から ICompositorInterop を取得する

C++/WinRT の場合、Compositor クラスからは as<T>() で Interop インターフェイスを取り出せます。

// C++/WinRT 例
#include &lt;winrt/Windows.UI.Composition.h&gt;
#include &lt;windows.ui.composition.interop.h&gt;

using namespace winrt;
using namespace Windows::UI::Composition;

void SetupSwapChainInterop(Compositor const&amp; compositor, IDXGISwapChain1* swapChain)
{
    // 1. Interop インターフェイスを取得
    winrt::com_ptr&lt;ICompositorInterop&gt; interop = compositor.as&lt;ICompositorInterop&gt;();

    // 2. スワップチェーンから ICompositionSurface を生成
    winrt::com_ptr&lt;ICompositionSurface&gt; surface;
    winrt::check_hresult(
        interop-&gt;CreateCompositionSurfaceForSwapChain(
            swapChain,
            surface.put())
    );

    // 3. SurfaceBrush を作成して Visual にバインド
    auto surfaceBrush = compositor.CreateSurfaceBrush();
    surfaceBrush.Surface(surface.as&lt;ICompositionSurface&gt;());

    auto sprite = compositor.CreateSpriteVisual();
    sprite.Brush(surfaceBrush);

    // あとはこの SpriteVisual をルートの Visual ツリーにぶら下げる
}

ポイントは、あくまでも Compositor を起点にしていることです。ICompositor を直接メンバーとして持つのではなく、Compositor から必要なときに ICompositorInterop を引き出す、というスタイルにしておくと、ライフタイム管理や将来の API 変更にも耐性が高くなります。

HWND をターゲットにする(DesktopWindowTarget)

デスクトップアプリでは、作った Visual ツリーをどのウィンドウに描画するかを指定する必要があります。ここで使うのが ICompositorDesktopInterop です。

#include &lt;winrt/Windows.UI.Composition.Desktop.h&gt;

using namespace Windows::UI::Composition::Desktop;

DesktopWindowTarget CreateTargetForHwnd(Compositor const&amp; compositor, HWND hwnd)
{
    winrt::com_ptr&lt;ICompositorDesktopInterop&gt; desktopInterop =
        compositor.as&lt;ICompositorDesktopInterop&gt;();

    DesktopWindowTarget target{ nullptr };
    winrt::check_hresult(
        desktopInterop-&gt;CreateDesktopWindowTarget(
            hwnd,
            TRUE, // topmost
            reinterpret_cast&lt;IDesktopWindowTarget**&gt;(
                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.Composition vs Microsoft.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 クラスの公式ドキュメントを見る
  • 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 レベルの知識を閉じ込めておく――これが実務での一番扱いやすいスタイルです。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次