Windowsの高DPIのAPI(DPI_AWARENESS_CONTEXT型)

Windows 10 1607で、高DPI関係のAPIや型が追加されました。 その中にDPI_AWARENESS_CONTEXT型があるのですが、この扱いで少しはまったので、覚書として残しておきます。

Windows 10 1607で追加された高DPI関係のAPI

Windows 10 1607で、高DPI関係のAPIが追加されました。そのAPI群の中に、

  • SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT dpiContext)
  • DPI_AWARENESS_CONTEXT GetDpiAwarenessContextForProcess(…)
  • SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT dpiContext)
  • DPI_AWARENESS_CONTEXT GetThreadDpiAwarenessContext()
  • DPI_AWARENESS_CONTEXT GetWindowDpiAwarenessContext(…)

など DPI_AWARENESS_CONTEXT型を扱うAPIがあります。

DPI_AWARENESS_CONTEXT型

Windows SDKのヘッダーファイルによると、 DPI_AWARENESS_CONTEXT型の定義は、以下のようになっています。

DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
#define DPI_AWARENESS_CONTEXT_UNAWARE               ((DPI_AWARENESS_CONTEXT)-1)
#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE          ((DPI_AWARENESS_CONTEXT)-2)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE     ((DPI_AWARENESS_CONTEXT)-3)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2  ((DPI_AWARENESS_CONTEXT)-4)
#define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED     ((DPI_AWARENESS_CONTEXT)-5)

このように、Contextの種類ごとに値が定義されています。

DPI_AWARENESS_CONTEXTの値の設定と取得

DPI_AWARENESS_CONTEXT型は、設定にも取得にも使われます。

  • SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT dpiContext)
  • DPI_AWARENESS_CONTEXT GetThreadDpiAwarenessContext()

のAPIのように、設定と取得のペアのAPIがあります。このAPIで設定して、その値を取得したどんな値が取得できるでしょうか?

例えば、以下のようなコードを実行したら、「Same」と「Different」のどちらのダイアログが表示されるでしょうか?

DPI_AWARENESS_CONTEXT hDpiContextSet = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE;
SetThreadDpiAwarenessContext(hDpiContextSet);
DPI_AWARENESS_CONTEXT hDpiContextGet = GetThreadDpiAwarenessContext();
if (hDpiContextSet == hDpiContextGet)
{
    MessageBox(NULL, _T("Same"), _T("DPI_AWARENESS_CONTEXT"), MB_OK);
}
else
{
    MessageBox(NULL, _T("Different"), _T("DPI_AWARENESS_CONTEXT"), MB_OK);
}

答えは、「Different」のダイアログが表示されます。

DPI_AWARENESS_CONTEXTの値

前掲したようにDPI_AWARENESS_CONTEXT型の定義は、DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);となっています。この定義から分かるようにDPI_AWARENESS_CONTEXT型は、Enum値ではなく、ハンドルです。そのため、値の直接比較では、内容の比較をしたことにならないのです。

実際、すべての値を設定して取得してみました。取得値は、Windowsの環境やバージョンによって異なることがあるかもしれません。

定義名定義値取得値
DPI_AWARENESS_CONTEXT_UNAWARE-10x00006010
DPI_AWARENESS_CONTEXT_SYSTEM_AWARE-20x00009011
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE-30x00000012
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2-40x00000022
DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED-50x40006010

このように、 DPI_AWARENESS_CONTEXT型の値は、同じ内容を示すときも別の値となります。そのため列挙値(Enum値)を想定して、値を直接比較したときは、期待の動作になりません。

DPI_AWARENESS_CONTEXTの値の比較

では、DPI_AWARENESS_CONTEXT型のハンドル値が、どのDpiContextの種類を示しているか比較したいときはどうすればよいのでしょうか?

ちゃんとAPIが用意されています。 DPI_AWARENESS_CONTEXT型が導入されたWindows 10 1607のときに、以下のAPIも追加されていました。

BOOL AreDpiAwarenessContextsEqual(
     In DPI_AWARENESS_CONTEXT dpiContextA,
     In DPI_AWARENESS_CONTEXT dpiContextB);

このAPIを使うと DPI_AWARENESS_CONTEXT型のハンドルの内容の比較ができます。

そのため、前掲したサンプルコードは以下のように書き換えると、正しく動作します。

DPI_AWARENESS_CONTEXT hDpiContextSet = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE;
SetThreadDpiAwarenessContext(hDpiContextSet);
DPI_AWARENESS_CONTEXT hDpiContextGet = GetThreadDpiAwarenessContext();
if (AreDpiAwarenessContextsEqual(hDpiContextSet, hDpiContextGet))
{
    MessageBox(NULL, _T("Same"), _T("DPI_AWARENESS_CONTEXT"), MB_OK);
}
else
{
    MessageBox(NULL, _T("Different"), _T("DPI_AWARENESS_CONTEXT"), MB_OK);
}

if文の比較部分をAreDpiAwarenessContextsEqual()を使って書き換えてました。


以上、DPI_AWARENESS_CONTEXT型を使うときの注意点でした。

コメントを残す