次の方法で共有


PDC 2008

高 DPI 対応の Win32 アプリケーションを記述する

更新日: 2009 年 4 月 28 日


ダウンロード

PDC08_Writing_DPI_Aware_Applications_JPN.docx (Word 形式、609 KB)


目次:

はじめに Windows の高 DPI 関連機能
  1. コントロール パネルを使用して DPI を設定する
  2. DPI 仮想化
  3. DPI 関連の API
高 DPI 関連の一般的な問題
  1. UI 要素やテキストが切れる
  2. フォント サイズが不適切になる
  3. レイアウトが不適切になる
  4. UI 要素がぼやける
  5. テキストがピクセル化する
  6. ドラッグ アンド ドロップなどの入力に関する問題
  7. 全画面表示のアプリケーションが部分的にしかレンダリングされない
  8. 有効解像度が間違って使用される
DPI 互換性を評価する 高 DPI 関連の問題のチェックリスト DPI 互換性をテストする ステップ バイ ステップ ガイド
  1. 既存の Win32 アプリケーション
  2. 新しい Win32 アプリケーション
高 DPI 関連の問題に対処する DPI 対応であることを宣言する
  1. アプリケーション マニフェストを使用する
  2. SetProcessDPIAware 関数
システム情報を取得する
  1. GetDeviceCaps 関数
  2. GetSystemMetrics 関数
  3. SystemParametersInfo 関数
DPI スケール率を求める テキストのサイズを変更する
  1. CreateFont 関数
  2. GetTextExtent 関数
  3. フォントを選択する
グラフィックのサイズを変更する
  1. 複数解像度のサポート
  2. ふさわしいものに最も近いものを使う
  3. アイコン
レイアウトのサイズを変更する
  1. DPI 仮想化が有効になっている場合のレイアウト
最低有効解像度に対処する まとめ 詳細情報 付録 A: Windows で高 DPI を設定する
  1. Windows XP で高 DPI を設定する
  2. Windows Vista で高 DPI を設定する
付録 B: DPI サンプル コード 付録 C: 最適な DPI 構成の例

Ryan Haveson、Ken Sykes
Microsoft Corporation
2008 年 10 月

対象:

Windows® XP

Windows Vista®

要約:

Win32 アプリケーションを DPI 対応にする方法と、そうすべき理由を説明します。このドキュメントでは、Windows XP と Windows Vista の高 DPI 関連の機能を使用して UI をより一貫性があり、魅力的で、読みやすいものにする方法を紹介します。また、DPI 関連の一般的な問題を特定して修正する方法について説明し、マニフェストを使用して、アプリケーションが DPI 対応であることを宣言する方法についても説明します。

法的通知:

このドキュメントに記載されている情報は、このドキュメントの発行時点におけるマイクロソフトの見解を反映したものです。変化する市場状況に対応する必要があるため、このドキュメントは、記載された内容の実現に関するマイクロソフトの確約とはみなされないものとします。また、発行以降に発表される情報の正確性に関して、マイクロソフトはいかなる保証もいたしません。
このホワイト ペーパーに記載された内容は情報の提供のみを目的としており、明示、黙示または法律の規定にかかわらず、これらの情報についてマイクロソフトはいかなる責任も負わないものとします。
お客様ご自身の責任において、適用されるすべての著作権関連法規に従ったご使用を願います。このドキュメントのいかなる部分も、米国 Microsoft Corporation の書面による許諾を受けることなく、その目的を問わず、どのような形態であっても、複製または譲渡することは禁じられています。ここでいう形態とは、複写や記録など、電子的な、または物理的なすべての手段を含みます。ただしこれは、著作権法上のお客様の権利を制限するものではありません。
マイクロソフトは、このドキュメントに記載されている内容に関し、特許、特許申請、商標、著作権、またはその他の無体財産権を有する場合があります。別途マイクロソフトのライセンス契約上に明示の規定のない限り、このドキュメントはこれらの特許、商標、著作権、またはその他の無体財産権をお客様に許諾するものではありません。
© 2008 Microsoft Corporation. All rights reserved.
Microsoft、MS-DOS、Windows、Windows NT、Windows Server、Windows Vista、Active Directory、ActiveSync、ActiveX、Direct3D、DirectDraw、DirectInput、DirectMusic、DirectPlay、DirectShow、DirectSound、DirectX、Expression、FrontPage、HighMAT、Internet Explorer、JScript、Microsoft Press、MSN、Outlook、PowerPoint、SideShow、Silverlight、Visual Basic、Visual C++、Visual InterDev、Visual J++、Visual Studio、WebTV、Windows Media、Win32、Win32s、および Zune は米国 Microsoft Corporation の米国またはその他の国における登録商標または商標です。
記載されている会社名、製品名には、各社の商標のものもあります。


1. はじめに

このホワイト ペーパーは、既存の Win32® アプリケーションと新しい Win32® アプリケーションを DPI 対応にする方法を示す実用的なガイドです。DPI 対応のアプリケーションを記述することは、さまざまな高 DPI のディスプレイ設定で一貫してユーザー インターフェイス (UI) の見栄えをよくするための鍵です。DPI 対応でないアプリケーションを高 DPI のディスプレイ設定で実行すると、多くの視覚的アーティファクトが発生するおそれがあります (UI 要素のサイズが不適切に変更される、テキストが切れる、イメージがぼやけるなど)。アプリケーションを DPI 対応にすると、アプリケーションの UI の見た目を確実により予測可能なものにすることができます。これにより、アプリケーションの UI はユーザーにとって視覚的により魅力のあるものになります。

Windows Vista® が登場してから、ユーザーは、高 DPI のディスプレイでテキストや UI 要素のサイズを拡大する方法として DPI 設定を変更するという推奨アドバイスに従うことが以前よりも多くなっています。高解像度のディスプレイを販売するメーカーも、このようなディスプレイの販売台数も増えているので、既定の 96 DPI の設定を前提にアプリケーションを記述できなくなっています。最適な DPI 構成の例の一覧については、付録 C を参照してください。最良のユーザー エクスペリエンスの提供を推進するには、開発者はアプリケーションを DPI 対応にする必要があります。

このドキュメントでは、DPI 関連の機能について説明し、Windows® XP と Windows Vista における高 DPI 関連の問題について考察します。また、DPI 対応性を評価するための一連のガイドラインや、アプリケーションの DPI 対応に関する問題に対処するのに役立ついくつかの解決策も紹介します。このドキュメントには、次のセクションがあります。

  • Windows の高 DPI 関連機能

    Windows XP と Windows Vista における高 DPI のサポートの概要について説明します。

  • 高 DPI 関連の一般的な問題

    高 DPI 設定でアプリケーションに影響を与える、主な視覚的アーティファクトやユーザビリティに関する問題について説明します。

  • DPI 互換性を評価する

    DPI 互換性の評価テストに使用する設定の表とテスト戦略に関する推奨事項を紹介します。

  • 高 DPI 関連の問題に対処する

    アプリケーションにおける高 DPI 関連の問題を解決するための、コーディングに関する推奨事項を紹介します。

ページのトップへ


2. Windows の高 DPI 関連機能

このセクションでは、Windows® XP と Windows Vista® でサポートされている高 DPI 関連機能の概要について説明します。次の表に、各プラットフォームでサポートされている高 DPI 関連の機能を示します。

機能 Windows XP Windows Vista
コントロール パネルの DPI 設定
カスタム DPI 設定
DPI 仮想化 ×
DPI 対応にすることを宣言するための API ×
システム メトリックや DPI を取得するための API

a. コントロール パネルを使用して DPI を設定する

Windows XP と Windows Vista のどちらでも、高 DPI のディスプレイ設定を変更することができます。Windows XP と Windows Vista で高 DPI を設定する方法については、「付録 A: Windows で高 DPI を設定する」を参照してください。

DPI 設定を変更すると、システム フォントとシステム UI 要素のサイズが変わります。これが、アプリケーションのレンダリングやレイアウトに関するコードでシステム DPI 設定を考慮する必要がある主な理由です。DPI 対応でないアプリケーションでは、フォント サイズにばらつきがある、テキストが切れる、UI 要素が切れるなど、いくつかの視覚的アーティファクトが発生する可能性があります。

b. DPI 仮想化

Windows Vista には、DPI 仮想化と呼ばれる機能が導入されています。この機能は、DPI 対応でないアプリケーションに、ある程度の自動サイズ変更のサポートを提供します。この機能がなければ、高 DPI 設定では、DPI 対応でないアプリケーションのテキストや UI 要素のサイズがシステムの他の部分よりも小さくなることが多く、その結果、ユーザビリティや読みやすさの問題が生じる可能性があります。

この機能では、アプリケーションが 96 DPI で実行されているかのように、"仮想化された" システム メトリックと UI 要素をアプリケーションに提供することでサポートを提供しています。続いて、アプリケーションは 96 DPI で画面表示領域外にレンダリングされ、デスクトップ ウィンドウ マネージャ (DWM) により、表示されるアプリケーション ウィンドウのサイズが DPI 設定に合わせて変更されます。たとえば、ディスプレイの DPI 設定が 144 の場合は、DWM によってアプリケーション ウィンドウのサイズは 150% (144/96 倍) に拡大されます。DPI 仮想化で使用されるサイズ変更は、ピクセルの引き伸ばしに基づくタイプのものです。その結果、引き伸ばされたピクセルが原因でぼやけが発生します。次のスクリーン ショットは、DPI 仮想化によって引き伸ばされたピクセルが原因で発生するタイプの視覚的アーティファクトの一例です。

視覚的アーティファクトの一例

DPI 仮想化機能の目標は、DPI 対応でないアプリケーションのテキスト サイズを拡大することです。しかし、仮想化インフラストラクチャを使用すると、重大な互換性の問題が発生する場合があります。DPI 仮想化が原因で、アプリケーションを Windows Vista で実行すると問題が発生する場合、ユーザーは、他のアプリケーションに影響を与えることなく、そのアプリケーションでの DPI 仮想化を無効にすることができます。特定のアプリケーションで DPI 仮想化を無効にするには、アプリケーションの実行可能ファイルを右クリックし、[プロパティ] をクリックし、[互換性] タブをクリックし、[高 DPI 設定では画面のスケーリングを無効にする] チェック ボックスをオンにします。

注: ユーザーは、[カスタム DPI 設定] ダイアログ ボックスの [WindowsXP 形式の DPI スケーリングを使用する] チェック ボックスをオンにして、システム規模で DPI 仮想化を無効にすることができます。詳細については、「付録 A: Windows で高 DPI を設定する」を参照してください。

DPI 仮想化の詳細については、Greg Schechter のブログの「High DPI Support in Windows Vista Aero」(英語) という記事を参照してください。 Windows Vista の互換性設定の詳細については、「Windows ヘルプと使い方」の「このバージョンのWindows で古いプログラムを実行する方法」を参照してください。

c. DPI 関連の API

Win32® API には、アプリケーションを DPI 対応にすることができる関数が用意されています。アプリケーションを DPI 対応にすることを宣言する方法については、「DPI 対応であることを宣言する」を参照してください。DPI 対応性に関連するシステム情報やメトリックを取得する方法については、「システム情報を取得する」を参照してください。

ページのトップへ


3. 高 DPI 関連の一般的な問題

システム DPI 設定のチェックが行われず、大きなフォントや UI のサイズに合わせた調整が行われないアプリケーションでは、さまざまな種類の問題が発生する可能性があります。このセクションでは、問題の最も一般的なカテゴリについて説明し、各カテゴリの例を紹介します。高 DPI 関連の問題のカテゴリには、次のようなものがあります。

  • UI 要素やテキストが切れる
  • フォント サイズが不適切になる
  • レイアウトが不適切になる
  • UI 要素がぼやける
  • テキストやビットマップがピクセル化する
  • 座標空間の位置調整が不適切で、入力に影響する
  • 全画面表示のアプリケーションが部分的にしかレンダリングされない
  • 仮想解像度が間違って使用される

「DPI 互換性を評価する」セクションでは、高 DPI 関連の問題の概要、それぞれの問題の考えられる根本原因、およびそれぞれの問題を解決するために使用できる手法が紹介されています。

a. UI 要素やテキストが切れる

DPI 対応でないアプリケーションでは、UI 要素やテキストが切れる場合があります。次のスクリーン ショットでは、ボタン上の "Print this page" というテキストの下部が切れています。

Print this page テキスト例

これは、テキストのサイズが変更されたにもかかわらず、テキストが含まれている他の UI 要素 (リスト ボックス項目やボタンなど) のサイズが変更されないために発生する問題です。この場合、アプリケーションでは、大きなテキストに合わせて UI レイアウトのサイズを変更することができなかったため、テキストが切れています。

UI 要素やテキストが切れるシナリオには、他にも次のようなものがあります。

  • テキストやコントロールのサイズが大きくなることにより、UI 要素がアプリケーション ウィンドウに収まらなくなる。
  • システム メトリックに応じてアプリケーションのサイズが部分的に変更されるが、全面的には変更されない。たとえば、新しいテキスト フォントに応じてボタンのサイズは変更されるが、ボタンが含まれている子ウィンドウのサイズは大きくならない。

b. フォント サイズが不適切になる

DPI 対応でないアプリケーションでは、表示されるテキストのフォント サイズが不適切になることがよくあります。次のスクリーン ショットでは、アプリケーションの UI のフォント サイズに一貫性がありません。

アプリケーションの UI のフォント サイズ 例

この種のアーティファクトは、一般に、アプリケーションが不適切にフォントを作成して使用する場合に発生します。Windows アプリケーションでは、さまざまな方法でフォントが作成および使用されます。フォント API には、DPI 設定の変更に応じてアプリケーションのテキストのサイズを動的に変更できるものもあれば、そうでないものもあります。アプリケーションでフォント サイズの表示に一貫性がなくなるのを防ぐには、DPI に依存しない方法でフォントを作成して使用するようにします。フォントを作成および使用する方法の詳細については、「テキストのサイズを変更する」セクションを参照してください。

c. レイアウトが不適切になる

DPI 対応でないアプリケーションでは、レイアウトが不適切になる場合があります。その結果、いくつもの視覚的アーティファクトが発生したり、アプリケーションとの対話に問題が生じたりする場合があります。次のスクリーン ショットでは、UI レイアウトのサイズが変更されず、それが原因で UI 要素が切れており、一部が画面に収まっていない状態を示しています。また、使用できる画面領域は十分あるにもかかわらず、テキストの一部が右端で折り返されていることにも注目してください。

レイアウト例

不適切なレイアウトは、多くの場合、システム メトリック (画面サイズなど) の計算間違いによって生じます。特定の UI 要素に関してはシステム メトリックが正しく計算されているが、計算が間違っているものもある、という場合もあります。

不適切なレイアウトの原因として他によくあるのは、低い "有効解像度" での実行です。"有効解像度" とは、物理的な解像度とディスプレイの DPI 設定の両方を考慮して計算した解像度のことです。レイアウトを計算する際は、正しい有効画面解像度を使用することが非常に重要です。有効解像度の詳細については、「最低有効解像度に対処する」を参照してください。

d. UI 要素がぼやける

Windows Vista® でアプリケーション全体がぼやけて見える場合、一般に、その原因は DPI 仮想化です。次のスクリーン ショットでは、ピクセルが引き伸ばされされており、その影響によりぼやけが発生しています。

ぼやけの発生例

DPI 仮想化の影響を受けないようにするには、アプリケーションを DPI 対応にします。必要な作業の量を理解するには、「DPI 互換性を評価する」また、最も一般的な DPI 構成でアプリケーションが期待どおりに動作するように、テストに関するガイドラインに従うこともお勧めします。

e. テキストがピクセル化する

アプリケーションで、テキストがピクセル化するがその他の視覚的な不自然さは発生しないという場合があります。テキストのピクセル化は、文字内の斜めの線において最も顕著に現れます。次のスクリーン ショットでは、Name というテキストはピクセル化していますが、Advanced Search というテキストはピクセル化していません。

テキストのピクセル化例

この種の視覚的アーティファクトをもたらす可能性が最も高いのは、ビットマップ フォントを使用しているアプリケーションです。ビットマップ フォントのテキストを拡大すると、テキストは、より大きなポイント サイズのフォントを使用して再描画されるのではなく、引き伸ばされます。テキストが引き伸ばされると、ピクセル化が発生します。テキストのピクセル化を防ぐには、DPI 対応のアプリケーションで TrueType フォントを使用します。TrueType フォントはベクタ ベースであり、ディスプレイの DPI 設定の変更に応じてより大きなフォント サイズとして適切にサイズ変更されます。

フォントの詳細については、MSDN ライブラリの「About Fonts (英語)」を参照してください。テキストやフォントに関する Win32® API については、MSDN ライブラリの「Using the Font and Text-Output Functions (英語)」を参照してください。

f. ドラッグ アンド ドロップなどの入力に関する問題

DPI 仮想化に伴う軽微な悪影響の 1 つとして、入力に関する問題が発生することがあります。たとえば、スケーリング中にアプリケーションの座標空間がシステムの座標空間に再マップされる際にアプリケーションの座標空間の位置調整が不適切に行われたことが原因で、ドラッグ アンド ドロップ操作が機能しなくなる場合があります。

この問題は、デスクトップ ウィンドウ マネージャによってアプリケーションのサイズが拡大されると、それに起因する変換によってアプリケーションの座標空間がシステムの他の部分と異なるものに変わってしまうために発生しています。アプリケーションがオフセットのシステム座標をどのように使用するかを知ることはできないので、ドラッグ アンド ドロップ座標をある座標空間から別の座標空間に変えることはできません。

DPI 仮想化機能は、DPI 対応でないアプリケーションで、許容可能なレベルのユーザー エクスペリエンスを提供するために導入されました。しかし、アプリケーションに関する問題の中には、座標空間の変更など、DPI 仮想化では解決できないものもあります。

g. 全画面表示のアプリケーションが部分的にしかレンダリングされない

DPI 仮想化に伴う悪影響として、全画面表示のアプリケーションが部分的にしかレンダリングされないという現象が発生することがあります。ほとんどの場合、この現象は全画面表示のゲーム アプリケーションで見られます。こうしたアプリケーションでは、一般に、プログラムによって画面解像度のモード変更を行っているからです。この問題の影響を受けるのは、DPI 対応であることが宣言されていない Windows Vista アプリケーションだけです。問題は、一部のシステム メトリックが仮想化されていることをアプリケーションが考慮に入れていない可能性があるということです。 ゲームのような全画面表示のアプリケーションは、本来、解像度に依存しません。ほとんどの場合、これは、こうしたアプリケーションが本来 DPI にも依存しないということを意味します。この種のアプリケーションに対する推奨解決策は、こうしたアプリケーションが DPI 対応であることを宣言することです。 ウィンドウ モードのインストーラやアンインストーラを使用するアプリケーションもあるので、高 DPI でのアプリケーションの動作全体が適切なものになるように、DPI のテストに関するガイドラインに従ってアプリケーション テスト パスを実行することが重要です。

h. 有効解像度が間違って使用される

多くの場合、アプリケーションには、実行するために最低限必要なディスプレイ解像度というものがあります。たとえば、あるアプリケーションの解像度の最低要件が 1024x768 だとします。実行時に、アプリケーションでは、現在の画面解像度を特定するためにシステムへの照会を行います。画面解像度が最低要件に満たない場合、アプリケーションではユーザーに対して警告を表示します。

高 DPI 設定では、システムはより大きなフォントや UI 要素を使用することになります。そのため、アプリケーションで描画を行う際に、より多くのピクセルが必要になります。その結果、同じ UI をレンダリングするために必要となるピクセルの数が増えるため、有効解像度が低下します。有効解像度を計算するための公式は、"有効解像度 = リアル解像度/(DPI/96)" です。

たとえば、画面解像度が 1200x900 で、DPI 設定が 144 のディスプレイの有効解像度は 800x600 です。144/96 = 150% なので、有効画面面積は 50% 減少するからです。つまり、この設定では、UI やテキストのサイズは、画面解像度が 800x600 で、DPI の設定が 96 のディスプレイで実行している場合とほぼ同じだということです。有効解像度の詳細については、「最低有効解像度に対処する」を参照してください。

ページのトップへ


4. DPI 互換性を評価する

DPI 互換性を評価するには、次の 3 つの手順が必要です。

  1. 高 DPI 関連の一般的な問題をよく理解します。
  2. 推奨される高 DPI のディスプレイ設定と解像度でアプリケーションをテストします。
  3. 「高 DPI 関連の問題に対処する」で説明されている手法を使用して、DPI 関連の問題に対処します。

a. 高 DPI 関連の問題のチェックリスト

次の表に、DPI 関連の主な問題とその最も一般的な原因および解決策を示します。

問題 最も一般的な原因 調査すべき解決策
UI 要素やテキストが切れる UI 要素のサイズが、GetTextExtent 関数に基づいて変更されていません。 テキストのサイズ変更
フォント サイズが不適切になる ハードコーディングされたピクセル サイズを使用してフォントが作成されています。 テキストのサイズ変更
レイアウトが不適切になる メイン ウィンドウのサイズが、DPI に基づいて適切に変更されていません。 レイアウトのサイズ変更
UI 要素がぼやける アプリケーションが DPI 対応であることが宣言されておらず、DPI 仮想化が有効になっています。 DPI 仮想化
DPI 対応であることを宣言
テキストがピクセル化する アプリケーションで、ビットマップ ベースのフォントが使用されています。 テキストのサイズ変更
座標空間の位置調整が不適切で、入力に影響する アプリケーションが DPI 対応であることが宣言されておらず、DPI 仮想化が有効になっています。 DPI 仮想化
DPI 対応であることを宣言
全画面表示のアプリケーションが部分的にしかレンダリングされない アプリケーションが DPI 対応であることが宣言されておらず、アプリケーションでは仮想化されたメトリックと仮想化されていないメトリックを併用しています。 DPI 仮想化
DPI 対応であることを宣言
DPI スケール率の特定
仮想解像度が間違って使用される アプリケーションが DPI 対応であることが宣言されておらず、アプリケーションでは仮想化されたシステム メトリックを使用しています。 DPI 仮想化
DPI 対応であることを宣言
DPI スケール率の特定
最低有効解像度への対処

b. DPI 互換性をテストする

アプリケーションの DPI 互換性を評価するには、さまざまな解像度と高 DPI 設定の組み合わせでアプリケーションをテストする必要があります。また、Vista には XP に用意されていない DPI 関連の機能があるので、テストは Vista で行うことをお勧めします。

次の表に、テストで使用することをお勧めする DPI 設定と最低解像度を示します。

DPI 設定 最低解像度 推奨解像度
120 1024x768 1200x1000 以上
144 (既定の設定) 1200x900 1600x1200 以上
144 (DPI 仮想化は無効) 1200x900 1600x1200 以上
*192 (既定の設定) 1600x1200 2500x1600 以上
*192 (DPI 仮想化は無効) 1600x1200 2500x1600 以上

*: 192 DPI というディスプレイ設定でのテストは必須ではありませんが、この設定でテストを行うと、アプリケーションがどの程度 "未来を保証された" ものになっているかを確認することができます。Windows Vista アプリケーションの場合、少なくとも 144 DPI 以下の構成で見つかった問題については、解決することをお勧めします。

こうした問題をバグ追跡システムに記録する際は、以下のデータ項目も記録しておくと、役に立つことがよくあります。

  • 視覚的アーティファクトのスクリーン ショット
  • DPI 構成 (DPI 仮想化が有効になっているかどうかを含む)
  • 問題を再現する際に使用した画面解像度

このような情報を開発者が利用できるようにしておくと、問題を特定し、それに応じた修正をより容易に行うのに役立ちます。

c. ステップ バイ ステップ ガイド

以下の手順は、DPI 互換性に関する問題を修正し、アプリケーションが DPI 対応であることを宣言する方法を示しています。

既存の Win32 アプリケーション

既存のアプリケーションを DPI 対応にするには、以下の手順を実行します。

  1. 高 DPI でアプリケーションをテストし、見つかった問題をすべて書き留めます。
  2. DPI に関する、コーディング上の一般的な問題がないかどうか、ソース コードを調べます。
  3. アプリケーションを完全に DPI 対応にするために必要なコストを分析します。
  4. 必要な高 DPI 資産 (ツール バー、ボタン、アイコンなど) をすべてリストアップします。
  5. 問題に応じた解決策を用いて、手順 1 で見つかった問題を修正します。
  6. アプリケーションが DPI 対応であることを宣言します。
  7. 問題がすべて修正されていることを確認します。修正されていない問題がある場合は、これより前の手順を繰り返します。

新しい Win32 アプリケーション

新しいアプリケーションを DPI 対応にするには、以下の手順を実行します。

  1. アプリケーションが DPI 対応であることを宣言します。
  2. コーディング手法をよく理解します。
  3. 必要な高 DPI 資産 (ツール バー、ボタン、アイコンなど) をすべてリストアップします。
  4. アプリケーションを記述します。
  5. 手順 3 でリストアップした新しい資産を統合します。
  6. DPI 互換性を調べるため、アプリケーションをテストします。
  7. DPI 関連の問題がすべて解決されていることを確認します。修正されていない問題がある場合は、これより前の手順を繰り返します。

ページのトップへ


5. 高 DPI 関連の問題に対処する

アプリケーションにおける高 DPI 関連の問題を解決するために使用できる手法はいくつかあります。このような手法には、次のようなものがあります。

  • DPI 対応であることを宣言する
  • システム メトリックの情報を使用してレイアウトを計算する
  • DPI スケール率を求める
  • テキストのサイズを変更する
  • グラフィックのサイズを変更する
  • レイアウトのサイズを変更する
  • 最低有効解像度に対処する

a. DPI 対応であることを宣言する

アプリケーションが DPI 対応であることを宣言するというのは、アプリケーションが 200 DPI 以下の DPI 設定で適切に動作することを明示することです。Windows® XP では、DPI 対応性はアプリケーションやオペレーティング システムに影響を与えません。DPI 対応性が意味を持つのは、Windows Vista® だけです。Windows Vista では、DPI 仮想化が有効になっていると、DPI 対応でないアプリケーションのサイズが変更され、アプリケーションは仮想化されたデータを GetSystemMetric 関数などのシステム API から受け取ります。

: 既定では、DPI 仮想化機能が有効になるのは、ディスプレイの DPI 設定が 144 以上の場合のみです。

Win32® API には、アプリケーションが DPI 対応であることを宣言する関数が用意されていますが、非常に特殊な状況を除いては、この関数の使用は推奨されていません。詳細については、「SetProcessDPIAware 関数」を参照してください。一般に、アプリケーションが DPI 対応であることを宣言する方法としては、アプリケーション マニフェストの使用をお勧めします。

アプリケーション マニフェストを使用する

アプリケーションが DPI 対応であることを宣言するには、アプリケーション マニフェストに <dpiAware> 要素を追加します。

アプリケーション マニフェストで <dpiAware> 要素を使用する方法の例を以下に示します。

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"

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

<asmv3:application>

&lt;asmv3:windowsSettings xmlns=&quot;https://schemas.microsoft.com/SMI/2005/WindowsSettings&quot;&gt;

  &lt;dpiAware&gt;true&lt;/dpiAware&gt;

&lt;/asmv3:windowsSettings&gt;

</asmv3:application>

</assembly>

: <dpiAware> 要素が DLL コンポーネントのアセンブリ マニフェスト内にある場合、設定は無視されます。アプリケーションのアセンブリ マニフェストでのみ、DPI 対応性を有効にできます。

上記の例で示したマニフェスト情報を DeclareDPIAware.manifest という名前のファイルに保存し、MT.exe ツールを使用してそれをアプリケーションにバインドする必要があります。また、Visual Studio を使用している場合は、プロジェクトのプロパティを表示し、[構成プロパティ] の下の [マニフェスト ツール] の下にある [入力と出力] に移動し、[追加のマニフェスト ファイル] テキスト ボックスで DeclareDPIAware.manifest ファイルを指定するという方法で、これをプロジェクトのマニフェスト ファイルに追加することもできます (以下に示す Microsoft Visual Studio 2008 のスクリーン ショットを参照)。


プロジェクトのプロパティ

マニフェストの使い方の詳細については、MSDN ライブラリの「Visual Studio でのマニフェスト生成」を参照してください。アセンブリ マニフェストの使用の詳細については、MSDN ライブラリの「Manifests (英語)」を参照してください。

: Visual Studio のバージョンによっては、"manifest authoring warning 81010002: Unrecognized Element 'application' in namespace 'urn:schemas-microsoft-com:asm.v3'"(マニフェスト作成警告 81010002: 名前空間 "urn:schemas-microsoft-com:asm.v3" 内に、認識されない要素 "application" があります。) などの警告がマニフェスト ツールによって表示される場合があります。これはマニフェスト コンパイラの既知のバグによるものなので、無視して問題ありません。

アプリケーションにマニフェストを追加したら、アプリケーションをテストできます。テストの際は、DPI 仮想化を有効にして 144 DPI でアプリケーションを実行します。アプリケーションが DPI 対応であることを適切に宣言した場合は、DPI 仮想化によるサイズ変更が原因でアプリケーションの UI がぼやけることはありません。

SetProcessDPIAware 関数

Windows Vista の SetProcessDPIAware 関数 (英語) を使用すると、現在のプロセスを DPI 対応にすることができます。ただし、SetProcessDPIAware 関数の使用は推奨されていません。たとえば、初期化中に DLL によって DPI 設定がキャッシュされる場合、アプリケーションで SetProcessDPIAware 関数を呼び出すと競合状態が発生する可能性があります。したがって、 SetProcessDPIAware 関数を呼び出すのではなく、アプリケーションのアセンブリ マニフェストを使用するという方法でアプリケーションで DPI 対応性を有効にすることをお勧めします。

アプリケーションのアセンブリ マニフェストに <dpiAware> 要素を追加することにより、アプリケーションが DPI 対応であることを宣言できます。user32.dll モジュール (Windows のユーザー インターフェイス機能を提供するモジュール) によって、アプリケーションの DPI 対応性設定がチェックされます。アプリケーションが DPI 対応であることが判明すると、アプリケーションの代わりに user32.dll モジュールが SetProcessDPIAware 関数を呼び出します。

: DLL コンポーネントでは、アプリケーションの DPI 対応設定に従い、SetProcessDPIAware 関数を呼び出さないようにする必要があります。

SetProcessDPIAware 関数が必要となるケースとして最も一般的なのは、DLL を読み込んで指定されたエントリ ポイントからプログラムを実行する汎用ホストです。汎用ホストの例を 3 つ挙げると、コマンド ライン ユーティリティ プログラム Rundll32、DllHost プロセス、および Microsoft 管理コンソール (MMC) です。

DLL が汎用ホストから読み込まれると、それが基本的にアプリケーションのエントリ ポイントとなり、その DLL に暗黙的に関連付けられている DLL はすべて、アプリケーションより前に初期化されます。したがって、DPI の影響を受けるメトリックがこうした DLL によってキャッシュされるのを防ぐため、SetProcessDPIAware 関数の呼び出しは必ずあらゆる初期化より前に行う必要があります。

ただし、新しいコードを記述する際は、汎用ホストの使用を可能な限り避け、適切なマニフェスト エントリが含まれている小さな実行可能ファイルを記述する必要があります。Windows Vista のコントロール パネル アプレットの多くでは、この手法が使用されました。

ページのトップへ

b. システム情報を取得する

次の Win32 API 関数は、現在のディスプレイ設定に関する情報を取得するのに役立ちます。

GetDeviceCaps 関数

GetDeviceCaps 関数 (英語) を使用すると、画面の水平方向および垂直方向での、論理インチごとのピクセル数を取得できます。複数のディスプレイ モニタが接続されているシステムでは、すべてのモニタでこの値が等しくなります。次のコード例は、現在のディスプレイ設定における、水平方向および垂直方向の DPI を取得する方法を示しています。

// CDPI::_Init() より抜粋

HDC hdc = GetDC(NULL);

if (hdc)

{

_dpiX = GetDeviceCaps(hdc, LOGPIXELSX);

_dpiY = GetDeviceCaps(hdc, LOGPIXELSY);

ReleaseDC(NULL, hdc);

}

// サンプル クラス CDPI を使用

CDPI g_metrics;

int dpiX = g_metrics.GetDPIX();

int dpiY = g_metrics.GetDPIY();

GetSystemMetrics 関数

GetSystemMetrics 関数 (英語) を使用すると、指定されたシステム メトリックまたはシステムの構成設定を取得できます。GetSystemMetrics 関数で取得されるサイズはすべてピクセル単位であることに注意してください。次のコード例は、現在のディスプレイ設定における、水平方向および垂直方向の解像度を取得する方法を示しています。

// 現在のディスプレイ設定における、水平方向および垂直方向の解像度を取得します。

int cxScreen = GetSystemMetrics(SM_CXSCREEN);

int cyScreen = GetSystemMetrics(SM_CYSCREEN);

付録 B に全容を記載したサンプル クラス CDPI (次のコード内に出てきます) には、DPI に基づいて変更された画面サイズ (通称、相対ピクセル) を取得するためのメソッドが用意されています。

CDPI g_metrics;

int cxScreen = g_metrics.ScaledScreenWidth();

int cyScreen = g_metrics.ScaledScreenHeight();

SystemParametersInfo 関数

SystemParametersInfo 関数 (英語) を使用すると、システム規模のパラメータでいずれかの値を取得または設定できます。この関数では、パラメータの設定中にユーザー プロファイルを更新することもできます。次のコード例は、マウス ホイールを上下に動かした際のスクロール行数を取得する方法を示しています。

int g_ucScrollLines;

// マウス ホイールを上下に動かした際のスクロール行数を取得します。

SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &g_ucScrollLines, 0);

ページのトップへ

c. DPI スケール率を求める

アプリケーションを DPI 対応のアプリケーションとして定義する場合、高 DPI 設定でアプリケーションのサイズを適切に変更する必要があります。サイズ変更を適切に行うには、相対的な DPI スケール率を求める必要があります。この値を求める際には、基準となる設定として 96 DPI が使用されます。次のコード例は、DPI スケール率を求める方法を示しています。

int ScaleX(int x) { _Init(); return MulDiv(x, _dpiX, 96); }

int ScaleY(int y) { _Init(); return MulDiv(y, _dpiY, 96); }

// CDPI クラスを使用した例

int cxScaledWidth = g_metrics.ScaleX(100);

相対的な DPI スケール率を求めたら、アプリケーションでフォントの選択、イメージの読み込み、および UI 要素のレイアウトを行う際に、そのスケール率を適用する必要があります。

ページのトップへ

d. テキストのサイズを変更する

テキストのサイズ変更は、アプリケーションを DPI 対応にするために不可欠です。テキストのサイズを適切に変更するための推奨事項を以下にいくつか示します。

  • 既定の Windows フォントや DC フォントはビットマップ フォントであり適切にサイズ変更されないので、使用しません。代わりに、TrueType フォントを使用します。
  • ピクセル サイズに基づくカスタム フォントでは、DPI スケール率を利用する必要があります。
  • さまざまな高 DPI 設定で UI レイアウトやテキストが適切にサイズ変更されることを確認します。

: アプリケーションで使用されているのが TrueType フォント (.ttf) かどうか定かでない場合は、コントロール パネルにあるフォント定義ファイルを調べます (ファイルを右クリックし、それが .ttf ファイルかどうか確認します)。

CreateFont 関数

フォントを作成するには、CreateFont 関数 (英語)CreateFontIndirect 関数 (英語)、または CreatFontIndirectEx 関数 (英語) を使用します。フォントの特性を定義する際には DPI スケール率を適用する必要があります。次の例は、CreateFontIndirect 関数 (英語) を使用する際に LOGFONT 構造体の lfHeight メンバーに DPI スケール率を適用する方法を示しています。

// CDPI より抜粋: ポイント サイズ (1/72 インチ) を絶対ピクセルに変換します。

int PointsToPixels(int pt) { return MulDiv(pt, _dpiY, 72); }

LOGFONT lf;

lf.lfHeight = -g_metrics.PointsToPixels(12);

// 構造体の残りのメンバに値を設定します。

HFONT hfont = CreateFontIndirect(&lf);

GetTextExtent 関数

サイズ変更されたテキスト文字列のピクセル サイズを求めるには、GetTextExtent (英語) 系の関数を使用します。フォントの物理ピクセル サイズはディスプレイの DPI 設定によって異なるためです。次のコード例は、文字列の周りに四角形を描画する前に、GetTextExtentPoint32 関数 (英語) を使用して、指定されたテキスト文字列の幅と高さ を求める方法を示しています。

static const TCHAR szString[] = TEXT("TEST");

// 指定された文字列の幅と高さを取得します。

SIZE size;

GetTextExtentPoint32(hdc, szString, lstrlen(szString), &size);

// サイズ変更された四角形を計算します。

RECT rcText;

SetRect(&rcText, 10, 10, 10 + size.cx, 10 + size.cy);

g_metrics.ScaleRect(&rcText);

// テキストを囲む四角形に基づいて枠線を計算します。

RECT rcBorder = rcText;

InflateRect(&rcBorder, g_metrics.ScaleX(4), g_metrics.ScaleY(4));

// (DPI スケール率に応じて調整された) 四角形を文字列の周りに描画します。

Rectangle(hdc, rcBorder.left, rcBorder.top, rcBorder.right, rcBorder.bottom);

// 四角形の中に文字列を描画します。

TextOut(hdc, rcText.left, rcText.top, szString, lstrlen(szString));

フォントを選択する

ChooseFont 関数 (英語) を使用すると、ユーザーがフォントの属性を選択できるように [フォント] ダイアログ ボックスを表示することができます。この関数を使用する場合は、CHOOSEFONT 構造体の Flags メンバの一部として TrueType フォントを指定するようにしてください。次の例は、[フォント] ダイアログ ボックス内の一覧に TrueType フォントのみが表示されるように ChooseFont 関数を初期化する方法を示しています。

#include "commdlg.h"

CHOOSEFONT data = { sizeof(data) };

data.hwndOwner = hWnd;

// [フォント] ダイアログ ボックス内の一覧に TrueType のスクリーン フォントのみを表示します。

data.Flags = CF_TTONLY | CF_SCREENFONTS;

ChooseFont(&data);

「Windows Vista User Experience Guidelines」(英語)では、アプリケーションでは可能な限り標準の視覚スタイルを使用すること、およびアプリケーションでは DC フォントでなくスケーラブルな TrueType フォントを使用することを推奨しています。

次のコード例では、メイン テキストに TEXT_BODYTEXT スタイルを使用しています。視覚スタイルが有効になっていない場合 (つまり、Windows クラシックのテーマを使用している場合) は、別のレンダリング コードを使用する必要があることに注意してください。

HTHEME hTheme = OpenThemeData(hWnd, VSCLASS_TEXTSTYLE);

if (hTheme)

{

DrawThemeText(hTheme, hdc, TEXT_BODYTEXT, 0, szText, -1, DT_SINGLELINE,0,&amp;rcText);

CloseThemeData(hTheme);

}

else

{

// 視覚スタイルが有効になっていない場合

DrawText(hdc, szText, -1, &amp;rcText, DT_SINGLELINE);

}

ページのトップへ

e. グラフィックのサイズを変更する

グラフィックのサイズ変更がうまく行われないアプリケーションは、ユーザーにとっての視覚的魅力に欠けます。高 DPI のディスプレイ設定で UI の見栄えをよりよくするために、イメージ、ビットマップ、アイコン、ツール バーなどの主要な全グラフィック コンテンツに対して独自のサイズ変更機能を提供することをご検討ください。アプリケーションでグラフィックのサイズを変更する手法はたくさんありますが、さまざまな高 DPI のディスプレイ設定でグラフィックのサイズを変更するのに適した主な手法は 2 つあります。この 2 つの手法とは、複数解像度のサポート、およびふさわしいものに最も近いものを使うという手法です。

複数解像度のサポート

複数解像度のサポートという手法を用いるには、対象となる DPI 設定 (96、120、144、192 など) それぞれに対して、適切にレンダリングされるバージョンが存在するように、グラフィックを複数の解像度で用意する必要があります。この場合は、それぞれの値は基準となる DPI 設定 (96) の 100%、125%、150%、および 200% に相当します。実行時に、アプリケーションのロジックでは、まず適切な DPI スケール率を求めます。続いて、そのスケール率を用いて、どの解像度のグラフィックを使用するかを決定します。

ふさわしいものに最も近いものを使う

この手法は、複数解像度のサポートを改良したものです。この手法では、DPI のさまざまな対象となるディスプレイ設定で適切にレンダリングされる、複数バージョンのグラフィックを用意するだけでなく、グラフィックをディスプレイのあらゆるカスタム DPI 設定での表示に対応させることができます。この手法の鍵は、対象となる DPI バージョンのうち "ふさわしいものに最も近い" グラフィック (少し大きめのグラフィック) を読み込んでから、ディスプレイの現在の DPI 設定に合わせてグラフィックを縮小することです。

たとえば、現在のカスタム DPI 設定が 132 で、複数解像度のグラフィックとして用意したのが 96 DPI 用、120 DPI 用、および 144 DPI 用のグラフィックだとすると、まず、現在の DPI 設定よりも少し解像度が高いグラフィックを読み込みます。この場合、まず 144 DPI バージョンのグラフィックを読み込みます。続いて、132 DPI で適切に表示されるように、このグラフィックを縮小します。次のコード例は、ふさわしいものに最も近いものを使うという手法を使用してディスプレイのあらゆるカスタム DPI 設定へのサポートを提供する方法を示しています。

int destRectHeight = 20;

int destRectWidth = 20;

int xStart = 40;

int yStart = 85;

// この例では、96、120、144、192 という 4 つのバージョンのビットマップがあります。

// これらのビットマップのサイズは 20x20、25x25、30x30、40x40 で、

// それぞれ 96 DPI、120 DPI、144 DPI、192 DPI に対応します。

// GetDeviceCaps(hDC, LOGPIXELSX) を通じて既に設定されたグローバルな gDPI があるとします。

// まずは読み込むソース イメージを特定します。

int iSourceImageDPIToUse = 96; // 既定では 96 を想定します。

if (gDPI > 144)

iSourceImageDPIToUse = 192;

else if (gDPI > 120)

iSourceImageDPIToUse = 144;

else if (gDPI > 96)

iSourceImageDPIToUse = 120;

LPCTSTR pBitmapResourceName = NULL;

// 読み込むのに適したリソースを選択します。

switch(iSourceImageDPIToUse)

{

case 120: 

    pBitmapResourceName = MAKEINTRESOURCE(MY_RESOURCE_120DPI);

    break;

case 144: 

    pBitmapResourceName = MAKEINTRESOURCE(MY_RESOURCE_144DPI);

    break;

case 192: 

    pBitmapResourceName = MAKEINTRESOURCE(MY_RESOURCE_192DPI);

    break;                      

default: // 既定値の 96 DPI に設定します。

    pBitmapResourceName = MAKEINTRESOURCE(MY_RESOURCE_96DPI);

    break;

}// 適切なリソースを読み込みます。

HBITMAP hbmImage = (HBITMAP)LoadImage(hinst,

pBitmapResourceName, IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);

// 伸縮モードを設定します。hdcDest は表示先への DC ハンドルとします。

SetStretchBltMode(hdcDest, HALFTONE);

// hdcTargetWindow はビットマップの表示先となるウィンドウとします。

HDC hdcBitmap = CreateCompatibleDC(hDCDest);

HBITMAP hbmOld = (HBITMAP)SelectObject(hdcBitmap, hbmImage);

BITMAP bitmap;

GetObject(hbmImage, sizeof(bitmap), &bitmap);

// 変更された表示先サイズに合わせて拡大または縮小されたイメージを StretchBlt に渡します。

// DPI に基づいてレイアウトを変更する必要がある場合、x と y を調整する必要があるかもしれません。

// destRectWidth と destRectHeight はレイアウトの 96 DPI (既定の) サイズです。

// 注: 質を高めるには、GDI+ または WIC を質の高いスケール フィルタと共に使用します。

StretchBlt(hdcDest, g_metrics.ScaleX(xStart), g_metrics.ScaleY(yStart),

g_metrics.ScaleX(destRectWidth), g_metrics.ScaleY(destRectHeight),

hdcBitmap, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);

// ハンドルは使い終わったら忘れずに削除します。

SelectObject(hdcBitmap, hbmOld);

DeleteDC(hdcBitmap);

DeleteObject(hbmImage);

上記の例では、ビットマップの読み込みに GDI を使用しています。現在、アプリケーションでイメージの読み込みに GDI+ を使用している場合は、interpolationMode パラメータに InterpolationModeHighQualityBicubic を設定して SetInterpolationMode 関数を使用する必要があります。Windows Imaging Component (WIC) オブジェクトを使用している場合は、補間モードに WICBitmapInterpolationModeFant を設定する必要があります。

WIC の便利な特徴の 1 つは、DPI がネイティブにサポートされていることです。イメージに DPI 属性のタグを付けることができ、これは WICBitmapSource::GetResolution メソッドを使用して取得できます。この属性により、設計者はソース グラフィックの複数の DPI バージョンをイメージ ファイルに含めることができるようになります。そのため、リソースの名前付け規則に頼ることなく、DPI 属性を指定してプログラムでイメージ ファイルを取得することができます。

GDI、GDI+、および WIC でのイメージのサイズ変更の詳細については、以下を参照してください。

アイコン

高 DPI 設定で大きなアイコンを使用して一覧表示された場合の見栄えをよくするために、アイコン (.ico) ファイルは解像度が 256x256 のバージョンも用意するようにしてください。.ico ファイルの作成、および高 DPI 設定での推奨されるサイズやスケール率の詳細については、https://msdn.microsoft.com/en-us/library/aa511280.aspx (英語) を参照してください。

ページのトップへ

f. レイアウトのサイズを変更する

多くのアプリケーション ウィンドウやダイアログ ボックスは、リソース ファイルで定義されたレイアウトに基づいて作成されています。リソース ファイルでは、DPI に依存しない論理単位でレイアウトが指定されます。一般に、このようなウィンドウを DPI 対応にするために特別な作業は必要ありません。

しかし、プログラムでカスタムの UI 要素を作成してレイアウトしなければならない場合があります。このような場合は、ウィンドウ全体のサイズを選択する際にも、また、各 UI 要素をウィンドウ内のどの位置にレイアウトするかを決める際にも、DPI を考慮に入れることが重要です。アプリケーションの次のコード例では、メイン ウィンドウと 3 つのボタンが作成されています。WM_PAINT メッセージ ハンドラ内では、画面へのテキストの描画も行われています。

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

{

static TCHAR szButton1Text[] = TEXT("Button 1");

static TCHAR szButton2Text[] = TEXT("Button 2");

static TCHAR szButton3Text[] = TEXT("Button 3");

HWND hwndMainWindow;

hInst = hInstance; // インスタンス ハンドルをグローバル変数に格納します。

hwndMainWindow = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

  CW_USEDEFAULT, 0, 420, 200, NULL, NULL, hInstance, NULL);

if (!hwndMainWindow) return FALSE;

if (!CreateMyButton(10, 55, 60, 26, szButton1Text, hwndMainWindow, hInstance)) return FALSE;

if (!CreateMyButton(85, 55, 60, 26, szButton2Text, hwndMainWindow, hInstance)) return FALSE;

if (!CreateMyButton(160, 55, 60, 26, szButton3Text, hwndMainWindow, hInstance)) return FALSE;

ShowWindow(hwndMainWindow, nCmdShow);

UpdateWindow(hwndMainWindow);

return TRUE;

}

// ボタンを作成するためのヘルパ関数です。

HWND CreateMyButton(int x, int y, int nWidth, int nHeight, LPCTSTR

szButtonText, HWND hWndParent, HINSTANCE hInstance)

{

DWORD dwFlags = WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON;

HWND hwndButton = CreateWindow(L"BUTTON",szButtonText,

dwFlags, x, y, nWidth, nHeight,hWndParent,NULL, hInstance, NULL);

return hwndButton ;

}

// WM_PAINT ハンドラからの抜粋です。

case WM_PAINT:

hdc = BeginPaint(hWnd, &amp;ps);        

TextOut(hdc,8,8,szText,lstrlen(szText));

EndPaint(hWnd, &amp;ps);

break;</code></pre>

次のスクリーン ショットは、上記のコード例のアプリケーションが 96 DPI のディスプレイ設定で表示されているようすを示しています。

96 DPI のディスプレイ設定での表示

この例では、アプリケーションの UI に視覚的アーティファクトは発生していません。96 DPI で適切に表示されるようにレイアウト座標が計算されているからです。すべての UI 要素は、アプリケーションのクライアント領域の左上隅を基準とする座標を使用して描画されます。

では、アプリケーションで、他のコンテンツを基準とするコンテンツをレイアウトする場合はどうなるでしょうか。たとえば、[Button 2] は [Button 1] よりも x ピクセル右にあります。DPI 仮想化を無効にして 144 DPI のディスプレイ設定でこの例を実行すると (これにより、DPI 対応にすることを宣言されているアプリケーションがシミュレートされます)、次のスクリーン ショットに示すように、いくつかの視覚的アーティファクトが発生します。

視覚的アーティファクトの発生例

以下に示すように、このスクリーン ショットでは UI 関連の問題がいくつか見受けられます。

  • 現在の DPI 設定に合わせてアプリケーションのサイズが変更されていないので、ウィンドウ枠のサイズが変更されていません。
  • テキストのサイズは適切に変更されていますが、枠のサイズが変更されていないため、テキストが切れています。
  • ボタンのテキストは適切にサイズ変更されていますが、ボタンのサイズは適切に変更されていないため、テキストが切れています。

こうした問題はどれも、アプリケーション レイアウトのサイズ変更が適切に行われていないことの表れです。この場合、ウィンドウ全体のサイズ、およびコントロール間のスペースを変更する必要があります。

DPI 仮想化が有効になっている場合のレイアウト

次のスクリーン ショットは、再び同じ例を示しています。144 DPI で実行されているという点はこれまでと同じですが、今回は DPI 仮想化が有効になっています。

DPI 仮想化の有効例

ウィンドウ全体のサイズが適切に変更され、テキストが大きくなっていることがわかります。しかし、ウィンドウ内のサイズ変更されたテキスト文字列は切れています。ボタンのサイズは変更されましたが、ボタン内のテキストは適切にサイズ変更されておらず、切れています。

テキスト文字列が切れるのは、DPI 仮想化で TextOut 関数がサポートされていないのでテキストが 2 倍に拡大されるためです。実際、Windows Vista の DPI 仮想化は、Win32® API の一部でしかサポートされていません。ですから、アプリケーションでの高 DPI のサポートを実現する方法として DPI 仮想化機能だけに頼るのは解決策ではなく、その場しのぎの対策にすぎません。

この例を真に DPI 対応にするには、レイアウトに関する以下の追加のサポートが必要となります。

  • ウィンドウの作成時に、ウィンドウ用の変更されたサイズを指定します。
  • ボタンの作成時に、ボタン用の変更されたサイズとサイズ変更されたレイアウトを指定します。
  • 視覚スタイル API を使用して、システムによって指定された視覚スタイルに基づいてテキストと他の UI 要素をレンダリングします。

次のコード例は前のコード例をバージョンアップしたもので、これを使用するとアプリケーション レイアウトをさらにサイズ変更することができます。

// これは前述のサイズ変更ヘルパ関数です。

CDPI g_metrics;

// これは変更が加えられた InitInstance のコードです。変更された箇所は太字になっています。

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

{

static TCHAR szButton1Text[] = TEXT("Button 1");

static TCHAR szButton2Text[] = TEXT("Button 2");

static TCHAR szButton3Text[] = TEXT("Button 3");

HWND hwndMainWindow;

hInst = hInstance; // インスタンス ハンドルをグローバル変数に格納します。

hwndMainWindow = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

  CW_USEDEFAULT, 0, g_metrics.ScaleX(420), g_metrics.ScaleY(200), NULL, NULL, hInstance, NULL);

if (!hwndMainWindow) return FALSE;

if (!CreateMyButton(10, 55, 60, 26, szButton1Text, hwndMainWindow, hInstance)) return FALSE;

if (!CreateMyButton(85, 55, 60, 26, szButton2Text, hwndMainWindow, hInstance)) return FALSE;

if (!CreateMyButton(160, 55, 60, 26, szButton3Text, hwndMainWindow, hInstance)) return FALSE;

ShowWindow(hwndMainWindow, nCmdShow);

UpdateWindow(hwndMainWindow);

return TRUE;

}

HWND CreateMyButton(int x, int y, int nWidth, int nHeight, LPCTSTR szButtonText,

HWND hWndParent, HINSTANCE hInstance)

{

HWND hwndButton = NULL;

DWORD dwFlags = WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON;

hwndButton = CreateWindow(L"BUTTON",szButtonText,dwFlags,

   g_metrics.ScaleX(x), g_metrics.ScaleY(y), 

   g_metrics.ScaleX(nWidth), g_metrics.ScaleY(nHeight)

   ,hWndParent,NULL, hInstance, NULL);

return hwndButton ;

}

#include <uxtheme.h>

#include <vsstyle.h>

#pragma comment(lib, "uxtheme.lib")

case WM_PAINT:

    {

        hdc = BeginPaint(hWnd, &amp;ps);        

        if (hdc)

        {

            RECT rcText;

            GetClientRect(hWnd, &amp;rcText);

            OffsetRect(&amp;rcText, g_metrics.ScaleX(8), g_metrics.ScaleY(8));



            HTHEME hTheme = OpenThemeData(hWnd, VSCLASS_TEXTSTYLE);

            if (hTheme)

            {

                DrawThemeText(hTheme, hdc, TEXT_BODYTEXT, 0, szText, -1, DT_SINGLELINE,

                    0, &amp;rcText);

                CloseThemeData(hTheme);

            }

            else

            {

                // 視覚スタイルが有効になっていない場合

                DrawText(hdc, szText, -1, &amp;rcText, DT_SINGLELINE);

            }

            EndPaint(hWnd, &amp;ps);

        }

    }

    break;</code></pre>

今度は、144 DPI でアプリケーションを実行すると、アプリケーションの元の値からのレイアウトのサイズ変更に基づいて、ボタンやテキストが適切に表示されます。DPI 仮想化が有効になっている場合に、アプリケーションが DPI 対応であることを宣言するマニフェスト エントリをまだ追加していない状態で実行すると、表示結果が違ってくることに注意してください。その場合、アプリケーションでは仮想化されたシステム メトリックを使用することになるからです。次のスクリーン ショットは、すべてが適切に表示されているようすを示しています。

すべてが適切に表示されている例

ヒント: 最初に DPI 仮想化が無効になっている状態でアプリケーションをテストすることは有益です。これを行う最も簡単な方法は、アプリケーション マニフェストに <dpiAware> 要素を追加するか、コントロール パネルから DPI 仮想化を無効にするかです。

ページのトップへ

g. 最低有効解像度に対処する

一部のアプリケーションでは、最低限必要な解像度 (800x600、1024x768 など) が決まっています。高 DPI 設定では、より多くのピクセルを使用してテキストや UI 要素がレンダリングされます。これにより、アプリケーションで使用できる画面面積が少なくなります。そのため、アプリケーションでは、物理的な解像度ではなく有効解像度を検知する必要があります。

たとえば、120 DPI で物理的な画面解像度が 1280x1024 で実行されているアプリケーションがある場合、そのアプリケーションの有効解像度は 1024x819 です。この場合、有効解像度は物理的な解像度よりも 20% 低くなります。

アプリケーションで警告メッセージやエラー メッセージを表示する際には、使用できる画面面積をより広くするために考えられる対応策として、画面解像度と DPI 設定の両方をユーザーに伝える必要があります。次のコード例は、有効画面解像度を実行時に検知する方法を示しています。

CDPI g_metrics;
// 処理を続行する前に、有効解像度が最低限必要な値を満たしていることを確認します。

if (!g_metrics.IsResolutionAtLeast(800, 600))

{

    if (MessageBox(NULL, 

        L&quot;Effective screen resolution must be at least 800x600. 

It is recommended that you either increase your screen resolution setting or

reduce your DPI scaling setting. Continue?",

        L&quot;Warning&quot;, 

        MB_YESNO | MB_ICONWARNING)

        == IDNO)

    {

        return FALSE;

    }

}</code></pre>

ページのトップへ


6. まとめ

Windows® XP や Windows Vista® で実行する DPI 対応の Win32® アプリケーションを記述するのは、それほど複雑なことではありません。DPI 関連の機能、高 DPI 関連の問題、および適切な DPI 評価戦略についての知識があれば、一貫して視覚的に魅力のある UI を実現することは可能です。

ページのトップへ


7. 詳細情報

ページのトップへ


8. 付録 A: Windows で高 DPI を設定する

以下のトピックでは、Windows® XP と Windows Vista® で DPI 設定を変更する方法を説明します。

a. Windows XP で高 DPI を設定する

Windows® XP で DPI 設定を変更するには

  1. Windows デスクトップを右クリックし、[プロパティ] をクリックします。
  2. [設定] タブをクリックし、[詳細設定] をクリックします。
  3. [全般] タブで、[DPI 設定] ボックスの一覧の [大きなサイズ (120 DPI)] をクリックします。
  4. 変更が適用された状態を確認するには、すべてのプログラムを終了して Windows を再起動します。

高 DPI の設定例

Windows XP でカスタム DPI 設定を設定するには

  1. Windows デスクトップを右クリックし、[プロパティ] をクリックします。
  2. [設定] タブをクリックし、[詳細設定] をクリックします。
  3. [全般] タブで、[DPI 設定] ボックスの一覧の [カスタム設定] をクリックします。
  4. [カスタム DPI 設定] ダイアログ ボックスの [標準サイズに対してこの割合で大きさを変える] ボックスにお望みの割合を入力し、[OK] をクリックします。
  5. 変更が適用された状態を確認するには、すべてのプログラムを終了して Windows を再起動します。

カスタム DPI 設定

ページのトップへ

b. Windows Vista で高 DPI を設定する

ここでは、Windows Vista® で DPI 設定を変更する方法と DPI 設定をカスタマイズする方法を説明します。

Windows Vista で DPI 設定を変更するには

  1. [スタート] メニューで、[コントロール パネル]、[デスクトップのカスタマイズ]、[個人設定] の順にクリックします。次のスクリーン ショットに示すように、個人設定画面が表示されます。
    個人設定画面
  2. 左ペインの [フォント サイズ (DPI) の調整] をクリックします。管理者パスワードの入力を求められた場合は、パスワードを入力します。また、処理を続行してよいか確認するメッセージが表示された場合は、続行を許可します。
  3. 次のスクリーン ショットに示す [DPI スケール] ダイアログ ボックスで、次のいずれかの操作を行います。
    • 画面上の項目 (テキストなど) のサイズを大きくするには、[大きなスケール (120 DPI) - 読みやすいテキスト] をクリックし、[OK] をクリックします。
    • 画面上の項目 (テキストなど) のサイズを小さくするには、[既定のスケール (96 DPI) - より多くの情報] をクリックし、[OK] をクリックします。
    [DPI スケール] ダイアログ ボックス
  4. 変更が適用された状態を確認するには、すべてのプログラムを終了して Windows を再起動します。

 

Windows Vista でカスタム DPI 設定を設定するには

  1. [スタート] メニューで、[コントロール パネル]、[デスクトップのカスタマイズ]、[個人設定] の順にクリックします。個人設定ウィンドウが表示されます。
  2. 左ペインの [フォント サイズ (DPI) の調整] をクリックします。管理者パスワードの入力を求められた場合は、パスワードを入力します。また、処理を続行してよいか確認するメッセージが表示された場合は、続行を許可します。
  3. [DPI スケール] ダイアログ ボックスで [カスタム DPI] をクリックすると、次のスクリーン ショットに示すような [カスタム DPI 設定] ダイアログ ボックスが表示されます。
    [カスタム DPI 設定] ダイアログ ボックス
  4. [標準サイズに対してこの割合で大きさを変える] ボックスにお望みの割合を入力し、[OK] をクリックします。この場合、既定値である 96 DPI の 150% は 144 DPIに相当します。[Windows XP 形式の DPI スケーリングを使用する] というチェック ボックスに注目してください。このチェック ボックスをオンにすると、DPI 仮想化が無効になります。
  5. 変更が適用された状態を確認するには、すべてのプログラムを終了して Windows を再起動します。

ページのトップへ


9. 付録 B: DPI サンプル コード

次のコード例は、CDPI という名前の C++ クラスを示しています。このクラスは、いくつかの一般的な高 DPI 関連機能を提供します。これには、システム DPI 設定を確認する機能、論理ピクセル値を物理ピクセル サイズに対応するようにサイズ変更する機能、有効画面解像度を確認する機能などがあります。

 

// 定義: 相対ピクセル = 96 DPI の場合を 1 ピクセルとして実際の DPI に基づいてサイズ変更されます。

class CDPI

{

public:

CDPI() : _fInitialized(false), _dpiX(96), _dpiY(96) { }



// 画面の DPI を取得します。

int GetDPIX() { _Init(); return _dpiX; }

int GetDPIY() { _Init(); return _dpiY; }



// 絶対ピクセルと相対ピクセル間の変換を行います。

int ScaleX(int x) { _Init(); return MulDiv(x, _dpiX, 96); }

int ScaleY(int y) { _Init(); return MulDiv(y, _dpiY, 96); }

int UnscaleX(int x) { _Init(); return MulDiv(x, 96, _dpiX); }

int UnscaleY(int y) { _Init(); return MulDiv(y, 96, _dpiY); }



// 画面サイズ (相対ピクセル単位) を求めます。

int ScaledScreenWidth() { return _ScaledSystemMetricX(SM_CXSCREEN); }

int ScaledScreenHeight() { return _ScaledSystemMetricY(SM_CYSCREEN); }



// 四角形のサイズを絶対ピクセルから相対ピクセルに変更します。

void ScaleRect(__inout RECT *pRect)

{

    pRect-&gt;left = ScaleX(pRect-&gt;left);

    pRect-&gt;right = ScaleX(pRect-&gt;right);

    pRect-&gt;top = ScaleY(pRect-&gt;top);

    pRect-&gt;bottom = ScaleY(pRect-&gt;bottom);

}

// 画面解像度が最低値 (相対ピクセル単位) を満たしているかどうかを

// 確認します。

bool IsResolutionAtLeast(int cxMin, int cyMin) 

{ 

    return (ScaledScreenWidth() &gt;= cxMin) &amp;&amp; (ScaledScreenHeight() &gt;= cyMin); 

}



// ポイント サイズ (1/72 インチ) を絶対ピクセルに変換します。

int PointsToPixels(int pt) { return MulDiv(pt, _dpiY, 72); }



// キャッシュされたメトリックをすべて無効にします。

void Invalidate() { _fInitialized = false; }

private:

void _Init()

{

    if (!_fInitialized)

    {

        HDC hdc = GetDC(NULL);

        if (hdc)

        {

            _dpiX = GetDeviceCaps(hdc, LOGPIXELSX);

            _dpiY = GetDeviceCaps(hdc, LOGPIXELSY);

            ReleaseDC(NULL, hdc);

        }

        _fInitialized = true;

    }

}



int _ScaledSystemMetricX(int nIndex) 

{ 

    _Init(); 

    return MulDiv(GetSystemMetrics(nIndex), 96, _dpiX); 

}



int _ScaledSystemMetricY(int nIndex) 

{ 

    _Init(); 

    return MulDiv(GetSystemMetrics(nIndex), 96, _dpiY); 

}

private:

bool _fInitialized;



int _dpiX;

int _dpiY;

};

ページのトップへ


10. 付録 C: 最適な DPI 構成の例

次の表は、いくつかの一般的なディスプレイの DPI 特性を一覧にしたものです。パネル DPI は、パネルのネイティブなピクセル密度を示します。一方、OS DPI は、ディスプレイを標準の設定に対応させるために使用する、最も近い OS DPI の値を示します。

説明 水平方向
(ピクセル単位)
垂直方向
(ピクセル単位)

(インチ単位)
パネルDPI OS DPI サイズ変更レベル
17 インチ WXGA+ 1440 900 17 100 96 100%
15.4 インチ WXGA+ 1440 900 15.4 110 96 100%
15.4 インチ WXGA 1280 768 15.4 97 96 100%
14.1 インチ WXGA 1280 768 14.1 106 96 100%
13.3 インチ WXGA 1280 768 13.3 112 96 100%
17 インチ WUXGA 1920 1200 17 133 120 125%
17 インチ WSXGA+ 1680 1050 17 117 120 125%
15.4 インチ WSXGA+ 1680 1050 15.4 129 120 125%
14.1 インチ WXGA+ 1440 900 14.1 120 120 125%
13.3 インチ WXGA+ 1440 900 13.3 127 120 125%
12.1 インチ WXGA+ 1280 768 12.1 123 120 125%
15.4 インチ WUXGA 1920 1200 15.4 147 144 150%

次の表は、ユーザーが適切な高 DPI 設定を使用した場合にピクセルの忠実度がどの程度向上するかを示しています。"最適な設定" 列に記載されている構成を使用すると、適切に動作しているアプリケーションは、"現在のユーザー設定" 列に記載されている構成とほぼ同じ物理サイズでレンダリングされます。

ディスプレイのネイティブな解像度 現在のユーザー設定 最適な設定 ピクセルの忠実度の向上
1200x1000 1024x768 120 DPI、1200x1000 52%
1600x1200 1024x768 144 DPI、1600x1200 144%
1600x1200 1200x1000 120 DPI、1600x1200 60%

ページのトップへ