WPF アプリケーションで SetWindowDisplayAffinity を使用して画面キャプチャ時に特定ウィンドウを除外する

画面共有やスクリーンキャプチャ機能を実装する際、自アプリケーションのウィンドウをキャプチャ対象から除外したいケースがあります。例えば、会議アプリケーションではホストのメインウィンドウや録画インジケータが他端末に表示されないよう、透明化または非表示にする必要があります。この要件を満たすために、Windows が提供する Win32 API の SetWindowDisplayAffinity 関数を利用できます。

SetWindowDisplayAffinity API の概要

この関数は、指定したウィンドウの表示対象を制御します。特に、WDA_EXCLUDEFROMCAPTURE フラグを設定することで、そのウィンドウの内容をスクリーンキャプチャ API や DXGI デスクトップレプリカーションなどの手段で取得した映像から除外できます。

関数宣言とパラメータ

BOOL SetWindowDisplayAffinity(
  HWND  hWnd,
  DWORD dwAffinity
);
  • hWnd:対象となる最上位ウィンドウのハンドル。実行プロセス内にある必要があります。
  • dwAffinity:表示属性を指定するフラグ。
    以下の値が定義されています:
    • WDA_NONE (0x00000000):通常の表示。除外や制限はありえません。
    • WDA_MONITOR (0x00000001):ディスプレイ上でのみ表示。スクリーンキャプチャ対象にはなりませんが、画面には表示されます。
    • WDA_EXCLUDEFROMCAPTURE (0x00000011): ディスプレイ上には表示されますが、キャプチャでは完全に除外されます。Windows 10 version 2004 以降で利用可能です。

注意点と制約

  • DWM(Desktop Window Manager)が有効な環境でのみ動作します。
  • 完全なセキュリティ保護(例:DRM)ではなく、スクリーンショットやカメラ撮影などによる物理的なキャプチャは防げません。
  • Windows 10 v2004 以前では WDA_EXCLUDEFROMCAPTURE を設定しても、内部的に WDA_MONITOR と同等の動作になる可能性があります。
  • 非最上位ウィンドウやビューワーウィンドウなどには適用できません。

WPF での実装例

以下は、WPF の MainWindow でアプリ起動時に自ウィンドウをキャプチャ対象から除外する方法です。

using System;
using System.Runtime.InteropServices;
using System.Windows;

namespace ScreenCaptureExample
{
    public partial class MainWindow : Window
    {
        // API定数
        private const uint WDA_EXCLUDEFROMCAPTURE = 0x00000011;

        // P/Invoke宣言
        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool SetWindowDisplayAffinity(IntPtr hWnd, uint dwAffinity);

        public MainWindow()
        {
            InitializeComponent();
            SourceInitialized += OnSourceInitialized;
        }

        private void OnSourceInitialized(object sender, EventArgs e)
        {
            var hwnd = new WindowInteropHelper(this).Handle;
            var success = SetWindowDisplayAffinity(hwnd, WDA_EXCLUDEFROMCAPTURE);
            if (!success)
            {
                // エラーハンドリング(必要に応じてLog出力など)
                // var errorCode = Marshal.GetLastWin32Error();
            }
        }
    }
}

上記コードを適用すると、キャプチャ対象から MainWindow が完全に除外され、交流スクリーン共有画面には表示されません。これにより、他社製アプリと同様のUXを実現できます。

タグ: WPF win32 SetWindowDisplayAffinity ScreenCapture DWM

6月15日 20:59 投稿