デスクトップアプリの高DPI対応 #2 – アプリ側の高DPI対応の概要

前回の投稿でWindows側の高DPI対応状況を説明しました。では、アプリ側が高DPI対応をするにはどうすればよいのでしょうか?今回の投稿では、アプリ側の高DPI対応の概要を説明します。

アプリの高DPIへの対応

アプリを高DPIに対応するには以下の二つの項目に対応する必要があります。

  • アプリ内での描画処理をディスプレイスケール率に応じて拡大・縮小する
  • 高DPIの対応状況を宣言する

前者は、アプリ内の描画を高DPI対応にします。後者は、OS側にアプリの高DPIへの対応状況を伝えます。OS側はこの情報をもとに、アプリに対してDPI仮想化が必要であるかなどを判断します。

アプリの描画処理の高DPI対応

OS側は初期のころからディスプレイスケール率に対応しており、アプリが高DPIに対応するための最低限必要なAPIは提供されていました。ただし、API的にはディスプレイスケール率が直接取得できるのではなく、DPI値として取得できました。取得したDPI値を96DPIで除算することにより、ディスプレイスケール率を求めることができます。

System Monitorへの対応の場合は、アプリが起動している間はディスプレイスケール率が変更されることはありません。そのため、アプリの起動時にディスプレイスケール率を取得して、その値に応じた大きさで描画・表示します。

Per Monitor / Per Monitor V2への対応の場合は、上記のSystem Monitorとは異なり、アプリが起動した後にディスプレイスケール率が変更されることがあります。たとえば、以下の場合です。

  • アプリのウィンドウがディスプレイスケール率が異なるディスプレイ間をまたいで移動したとき
  • ユーザーが「設定→システム→ディスプレイ」でディスプレイスケール率を変更したとき

このため、アプリが起動された後も、アプリはディスプレイスケール率の変更を検出して、それに応じた表示サイズに変える必要があります。

アプリ内の描画処理は、アプリが独自に描画している部分はアプリ自身で処理する必要があります。しかし、UIフレームワーク(WPFやWinFormsなど)を使って描画している場合は、UIフレームワーク側で高DPI対応の処理してくれることも多くあります。そのような場合は、UIフレームワークの設定をするのみで、高DPI対応が完了することもあります。

UIフレームワーク毎の対応方法は別の機会に投稿したいと思います。

アプリの高DPI対応状況の宣言

つぎに、対応状況の宣言についてです。OS側の対応レベルには、3種類あることを説明しました。

  • System Monitor
  • Per Monitor
  • Per Monitor V2

上記の三つです。これに対して、アプリ側ではアプリがどの方式に対応しているかの宣言をする必要があります。アプリケーションの宣言内容としては、

  • 高DPI非対応アプリ
  • System Monitor対応アプリ
  • Per Monitor対応アプリ
  • Per Monitor V2対応アプリ

があります。宣言しない場合は、高DPI非対応アプリの扱いとなります。

宣言の方法には、以下の二つがあります。

  • アプリケーションマニフェストによる方法
  • API呼び出しによる方法

通常は、アプリケーションマニフェストで宣言します。APIで宣言する場合は、UIを表示する前にAPI呼び出しを行います。

以下にアプリケーションマニフェストで宣言する方法を示します。

高DPI非対応アプリの宣言

アプリケーションマニフェストに以下のような<dipAware>の宣言をします。

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
<application xmlns="urn:schemas-microsoft-com:asm.v3">
     <windowsSettings>
       <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">False</dpiAware>
     </windowsSettings>
   </application>
 </assembly>

後述する<dpiAwareness>タグでも<dpiAwareness>unaware</dpiAwareness>と記述できます。この宣言(Unaware: 高DPI非対応)の場合は、<dpiAware><dpiAwareness>は同等の意味を持つので省略しています。<dpiAware>のタグは、アプリがWindows 10 1511以前のバージョンをサポートするために省略することはできません。

<dpiAware>および<dpiAwareness>のどちらも記載しない場合も、高DPI非対応アプリの宣言に相当します。そのため、アプリケーションマニフェストに高DPI対応状況の宣言がない過去のアプリは、自動的に高DPI非対応アプリとして扱われます。

System Monitor対応アプリの宣言

アプリケーションマニフェストに以下のような<dipAware>の宣言をします。

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
<application xmlns="urn:schemas-microsoft-com:asm.v3">
     <windowsSettings>
       <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware>
     </windowsSettings>
   </application>
 </assembly> 

後述する<dpiAwareness>タグでも<dpiAwareness>system</dpiAwareness>と記述できます。この宣言(System: System Monitor)の場合は、<dpiAware><dpiAwareness>は同等の意味を持つので省略しています。<dpiAware>のタグは、アプリがWindows 10 1511以前のバージョンをサポートするために省略することはできません。

Per Monitor対応アプリの宣言

アプリケーションマニフェストに以下のような<dipAware>の宣言をします。

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
<application xmlns="urn:schemas-microsoft-com:asm.v3">
     <windowsSettings>
       <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
     </windowsSettings>
   </application>
 </assembly> 

True/PMPMは、PerMonitorの短縮形ですね。後述する<dpiAwareness>タグでも <dpiAwareness>PerMonitor</dpiAwareness>と記述できます。この宣言(PerMonitor: Per Monitor)の場合は、<dpiAware><dpiAwareness>は同等の意味を持つので省略しています。<dpiAware>のタグは、アプリがWindows 10 1511以前のバージョンをサポートするために省略することはできません。

Per Monitor V2対応アプリの宣言

アプリケーションマニフェストに以下のような<dipAware>の宣言をします。

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
<application xmlns="urn:schemas-microsoft-com:asm.v3">
     <windowsSettings>
       <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
       <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
     </windowsSettings>
   </application>
 </assembly> 

<dpiAwareness>タグは、Windows 10 1607から導入されたタグです。そのため、<dpiAware>および<dpiAwareness>の両方が設定されていた場合、Windows 10 1607以降では、<dpiAwareness>の値が優先されます。Windows 10 1511以前では、<dpiAwareness>は無視され<dpiAware>の値が利用されます。そのため<dpiAware>のタグは、アプリがWindows 10 1511以前のバージョンをサポートするために省略することはできません。

さらに<dpiAwareness>タグの値のPerMonitorV2はWindows 10 1703から導入されたタグです。そのため、<dpiAwareness>タグをサポートしているWindows 10 1607で、 非サポートのPerMonitorV2の値が使われていた場合は、その値は無視され、サポートされているPerMonitorが宣言されたものとして扱われます。このフォールバック処理のために、PerMonitorV2PerMonitorの両方の値を宣言する必要があります。

Windowsの対応レベルとアプリの対応レベルの関係

Windows側の高DPIの対応レベルは、

  • System Monitor
  • Per Monitor
  • Per Monitor V2

と三種類あります。それに対して、アプリ側の高DPIの対応状況は、

  • Unaware (非対応)
  • System Monitor
  • Per Monitor
  • Per Monitor V2

と4種類あります。Windowsのバージョンとアプリの対状況の関係はどうなるのでしょうか?

以下の表にまとめてみました。

アプリを実行するWindowsのバージョンWindows側の対応レベル非対応アプリSystem Monitor
対応アプリ
Per Monitor
対応アプリ
Per Monitor V2 対応アプリ
Windows XPSystem Monitor①: ×
②: ×
①: ◎
②: ×
①: ◎
②: ×
①: ◎
②: ×
Windows Vista①: △
②: ×
Windows 7
Windows 8.0
Windows 8.1Per Monitor①: △
②: △
①: ◎
②: △
①: ◎
②: ◎
①:◎
②:◎
Windows 10 1607以前
Windows 10 1703以降Per Monitor V2①:◎
②:◎
(タイトルバーもOS側で対応)

表内の各記号の意味は以下の通りとなります。

PCに接続されたディスプレイは、二つのグループに分けました。

  • ①(プライマリー): PCに接続されている一つ目のディスプレイ上での状態
  • ②(セカンダリー): PCに接続されている二つ目以降のディスプレイ上での状態

また、それぞれのディスプレイ上での表示の状態を以下のグループに分けました。

  • ◎(適切表示):表示サイズおよび表示内容ともに適切に表示されること
  • △(DPI仮想化):DPI仮想化により、表示サイズは正しい。ただし、表示内容はボケが発生する
  • ×(小さい表示):表示サイズが小さく適切ではない。ボケは発生しないが、小さく表示されること

表から分かるようにアプリ側がPer Monitor V2方式に対応していると、どのWindowsでも一番適切に表示できることがわかります。

デスクトップアプリの高DPI対応の動作確認

デスクトップアプリの高DPI対応の動作確認をするときには一つ注意点があります。アプリ側の高DPI対応が正しくできていても、動作確認のときに想定の動作をしないことがあります。アプリをVisual Studioから起動したときなど、デバッガでアタッチしているときに起きます。

この場合は、デバッガでアタッチせずに起動する(Visual Studioのメニューにある「デバッグ」→「デバッグなしで開始」)か、アプリを単体で起動することにより、本来の動作を確認することができます。Visual Studio上から作成したアプリを起動するときは、注意しましょう。


次回は.NET Frameworkアプリの高DPI対応について投稿したいと思います。


2020年07月11日 投稿内容の更新 (XML名前空間の修正)

Visual Studioでアプリケーションマニフェストファイル(app.manifest)を追加したときに作成されるファイルがXML名前空間名(接頭辞:asmv3)を使わず、デフォルトXML名前空間のみを使った形式でした。

この投稿では、当初、以下のようなasmv3というXML名前空間名(接頭辞)を使用した記述でした。

xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"

しかし、Visual Studioで作成されるアプリケーションマニフェストファイルとの整合性を取るため投稿内でのManifestファイルの記述例をXML名前空間名(asmv3)を使用せず、デフォルト名前空間のみを使った形式に修正しました。

なお、XMLファイルの形式としては、どちらも間違っていないので、以前の記述でも問題なく動作します。

“デスクトップアプリの高DPI対応 #2 – アプリ側の高DPI対応の概要” への2件の返信

コメントを残す