Mixed-Mode DPI スケーリングと DPI 対応 API

Sub-Process DPI 対応サポート

SetThreadDpiAwarenessContext を使用すると、1 つのプロセス内でさまざまな DPI スケーリング モードを使用できます。 Windows 10 Anniversary Update より前は、ウィンドウの DPI 認識がプロセス全体の DPI 認識モード (DPI 認識なし、システム DPI 対応、または DPI 対応Per-Monitor) にバインドされていました。 ただし、 SetThreadDpiAwarenessContext では、最上位のウィンドウに、プロセス全体の DPI 認識モードとは異なる DPI 認識モードを使用できるようになりました。 子ウィンドウは親ウィンドウと常に同じ DPI 認識モードを持つので、これは子ウィンドウにも影響します。

SetThreadDpiAwarenessContext を使用すると、開発者はデスクトップ アプリケーションの DPI 固有の動作を定義するときに、開発作業に集中する場所を決定できます。 たとえば、アプリケーションのプライマリ トップレベル ウィンドウはモニターごとにスケーリングできますが、セカンダリ 最上位ウィンドウはオペレーティング システムによるビットマップ スケーリングを使用してスケーリングできます。

DPI 認識コンテキスト

SetThreadDpiAwarenessContext が利用可能になる前は、プロセスの DPI 認識は、アプリケーション バイナリのマニフェストで定義されたか、プロセスの初期化中に SetProcessDpiAwareness を呼び出して定義されていました。 SetThreadDpiAwarenessContext を使用すると、各スレッドは、プロセス全体の DPI 認識モードとは異なる可能性がある個々の DPI 認識コンテキストを持つことができます。 スレッドの DPI 認識コンテキストは 、DPI_AWARENESS_CONTEXT 型で表され、次の方法で動作します。

  • スレッドでは、DPI 認識コンテキストをいつでも変更できます。
  • コンテキストが変更された後に行われる API 呼び出しは、対応する DPI コンテキストで実行されます (仮想化される場合もあります)。
  • ウィンドウが作成されると、その DPI 認識は、その時点での呼び出し元スレッドの DPI 認識として定義されます。
  • ウィンドウのウィンドウ プロシージャが呼び出されると、スレッドは、ウィンドウの作成時に使用されていた DPI 認識コンテキストに自動的に切り替わります。

SetThreadDpiAwarenessContext を使用する一般的なシナリオは次のとおりです。1 つのコンテキスト (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE など) で実行されているスレッドから一時的に別のコンテキスト (DPI_AWARENESS_CONTEXT_UNAWARE) に切り替え、ウィンドウを作成し、すぐにスレッド コンテキストを以前の状態に切り替えます。 作成されたウィンドウにはDPI_AWARENESS_CONTEXT_UNAWAREの DPI コンテキストが含まれますが、呼び出し元のスレッドのコンテキストは、SetThreadDpiAwarenessContext の後続の呼び出しでDPI_AWARENESS_CONTEXT_PER_MONITOR_AWAREに復元されます。 このシナリオでは、呼び出し元のスレッドに関連付けられたウィンドウはモニターごとのコンテキストで実行されます (したがって、オペレーティング システムによってビットマップストレッチされません)。新しく作成されたウィンドウは DPI に対応しないため (したがって、100% のスケーリングに >設定されたディスプレイで自動的にビットマップストレッチされます)。

図 1 は、メイン プロセス スレッドがDPI_AWARENESS_CONTEXT_PER_MONITORを使用して実行され、そのコンテキストをDPI_AWARENESS_CONTEXT_UNAWAREに切り替えて、新しいウィンドウを作成する方法を示しています。 新しく作成されたウィンドウは、メッセージがディスパッチされるか、API 呼び出しが行われるたびに 、DPI_AWARENESS_CONTEXT_UNAWARE の DPI 認識コンテキストで実行されます。 新しいウィンドウを作成した直後に、メイン スレッドは以前のコンテキストの DPI_AWARENESS_CONTEXT_PER_MONITOR に復元されます。

モニターごとの dpi 認識の動作を示す図

SetThreadDpiAwarenessContext が提供する 1 つのプロセス内のさまざまな DPI 認識モードのサポートに加えて、デスクトップ アプリケーション用に次の DPI 固有の機能が追加されました。

EnableNonClientDpiScaling

Note

Per Monitor V2 DPI 認識モードでは、この機能が自動的に有効になります。そのため、EnableNonClientDpiScaling を呼び出す必要はありません。

ウィンドウの WM_NCCREATE ハンドラー内から EnableNonClientDpiScaling を呼び出すと、トップレベル ウィンドウの非クライアント領域で DPI が自動的にスケーリングされます。 最上位のウィンドウがモニターごとの DPI 対応の場合 (プロセス自体がモニターごとの DPI 対応であるか、モニターごとの DPI 対応スレッド内にウィンドウが作成されたかに関係なく)、これらのウィンドウのキャプション バー、スクロール バー、メニュー バー、およびメニュー バーは、ウィンドウの DPI が変更されるたびに DPI スケールされます。

子編集コントロールのクライアント以外のスクロール バーなど、子ウィンドウのクライアント以外の領域では、この API を使用すると DPI が自動的にスケーリングされないことに注意してください。

Note

EnableNonClientDpiScaling、WM_NCCREATE ハンドラーから呼び出す必要があります。

*ForDpi API
  • GetSystemMetrics などの一部のよく使用される API には HWND のコンテキストがないため、戻り値に対する適切な DPI 認識を誘導する方法はありません。 別の DPI 認識モードまたはコンテキストで実行されているスレッドからこれらの API を呼び出すと、呼び出し元スレッドのコンテキストに合わせてスケーリングされない値が返される場合があります。 GetSystemMetricForDpiSystemParametersInfoForDpiAdjustWindowRectExForDpi は、対応する DPI と同じ機能を実行しますが、DPI を引数として受け取り、現在のスレッドのコンテキストから dpi 認識を推論します。

  • GetSystemMetricForDpiSystemParametersInfoForDpi は、次の式に従って、DPI スケールのシステム メトリック値とシステム パラメーター値を返します。

    GetSystemMetrics(...) @ dpi == GetSystemMetricsForDpi(..., dpi)

    そのため、特定のシステム DPI 値を持つデバイスで実行している間に GetSystemMetrics (または SystemParametersInfoForDpi) を呼び出すと、DPI 対応バリアント (GetSystemMetricsForDpi および SystemParametersInfoForDpi) が入力と同じ DPI 値を指定した場合と同じ値が返されます。

  • AdjustWindowRectExForDpi は HWND を受け取り、DPI に依存する方法でウィンドウ四角形の必要なサイズを計算します。

GetDpiForWindow
GetDpiForWindow は、指定された HWND に関連付けられている DPI を返します。 答えは、HWND の DPI 認識モードによって異なります。
HWND の DPI 認識モード 戻り値
非対応 96
システム システム DPI
Per-Monitor 関連付けられている最上位ウィンドウが主に配置されているディスプレイの DPI
(子ウィンドウが指定されている場合は、対応する最上位の親ウィンドウの DPI が返されます)
GetDpiForSystem

GetDpiForSystem の呼び出しは、GetDCGetDeviceCaps を呼び出してシステム DPI を取得するよりも効率的です。

サブプロセス DPI 認識を使用するアプリケーションで実行できるコンポーネントは、プロセスのライフサイクル中にシステム DPI が静的であると想定しないでください。 たとえば、 DPI_AWARENESS_CONTEXT_UNAWARE 認識コンテキストで実行されているスレッドがシステム DPI に対してクエリを実行する場合、その答えは 96 になります。 ただし、同じスレッドが 認識コンテキストDPI_AWARENESS_CONTEXT_SYSTEM に切り替え、システム DPI に再度クエリを実行した場合、その答えは異なる可能性があります。 キャッシュされた (古い可能性のある) システム DPI 値が使用されないようにするには、 GetDpiForSystem を 使用して、呼び出し元スレッドの DPI 認識モードに対するシステム DPI を取得します。