画面サイズを取得するには?[Windows 10 UWPアプリ開発]:WinRT/Metro TIPS
DirectXが提供するAPIを利用して、UWPアプリからデスクトップ画面全体のサイズを取得する方法を解説する。
powered by Insider.NET
Windows 10のユニバーサルWindowsプラットフォーム用のアプリ(以降、UWPアプリ)は、PCやノートブックではデスクトップ上の独立したウィンドウとして動作する。そうなると、画面のサイズ(=画面全体の縦/横のピクセル数)を知りたくなることがあるだろう。例えば、画面に対して一定の割合のサイズでウィンドウを表示したいというような場合が考えられる*1。ところが、WindowsランタイムにはそのようなAPIが用意されていないのだ(本稿執筆時点)。何とかならないだろうか?
画面サイズの取得は、DirectXを使えば可能である。本稿ではその方法を解説する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #120」からダウンロードできる。
※本稿の内容を試すために必要な開発環境は、本稿の末尾でまとめている(なお、Visual Studio Tools for Universal Windows Appsの最新バージョンは2015年11月30日にリリースされたVisual Studio 2015 Update 1で1.2に上がっている。それらの情報も含めて記載しているので、実際にコードを試してみようという方は参考にしてほしい)。
*1 ウィンドウをリサイズする方法は「WinRT/Metro TIPS:ウィンドウのサイズを変えるには?[Windows 10 UWPアプリ開発]」を参照。
画面の物理サイズを取得するには?
DirectXのIDXGIOutputインターフェースを利用する*2。
DirectXのコーディングはC++で行うことになる*3。C#/VBプログラマーにとってはハードルが高いのだが、幸いにもイスラエルMicrosoftのTomer Shamam氏がサンプルコードをフリーで公開してくれているので、それを使わせていただこう。
*2 UWPアプリでWin32 APIを使うときは、それが利用できることを必ず確認してほしい。確認を怠っていると、Windowsストアの審査で引っ掛かり、根本から作り直すはめにもなりかねない。DirectX関連で利用できるAPIの一覧は、MSDNドキュメント「Win32 and COM for Universal Windows Platform (UWP) apps (graphics)」を参照。また、Win32 APIを使うコードを書いたら、早めにWindows アプリ認定キット(WACK)でチェックすることをお勧めする。
*3 DirectXをラップしたライブラリ(SharpDXなど)を使ってC#/VBでコーディングする方法もある。参考「WinRT/Metro TIPS:SharpDXを使ってDirectXで音声ファイルを再生するには?[ユニバーサルWindowsアプリ開発]」。あるいは、DllImportを使ってC#/VBのコードから直接APIを利用する方法も考えられるが、単純なAPI呼び出し一つだけだというのならともかく、今回のようなコードではC++で書いてしまった方が楽だろう。
次に挙げるShamam氏のブログ記事の文中に「MetroScreenRes.zip」ファイルへのリンクがあるので、それをダウンロードしていただきたい(別途公開のサンプルには、以降の手順でUWPアプリ対応にしたプロジェクトを含めてある)。
ダウンロードしたzipファイルに含まれているプロジェクトはWindows 8用のものなので、そのままではUWPアプリから利用できない。以下の手順で、UWPアプリ用のWindowsランタイムコンポーネントに移植する(詳しくは、MSDNドキュメント「方法: ユニバーサル Windows アプリで既存の C++ コードを使用する」の「C++ ライブラリをWindows ランタイム コンポーネントに移植する」を参照)。
- C#/VB用のUWPアプリのプロジェクトとソリューションを用意する
- C++用のWindowsランタイムコンポーネントのプロジェクトを、「MetroHelpers」という名前でソリューションに追加する(下の画像)。まだビルドしてはいけない。
- ソリューションを閉じ、Windowsのエクスプローラーを使って、「MetroHelpers」プロジェクトから全ての*.h/*.cppファイルを削除する
- エクスプローラーを使って、「MetroScreenRes.zip」ファイル中のMetroHelpersフォルダーから全ての*.h/*.cppファイルを「MetroHelpers」プロジェクトのフォルダーにコピーする
- ソリューションを開き、上でコピーしたファイルを「MetroHelpers」プロジェクトに追加する
- 「MetroHelpers」プロジェクトをビルドする
- C#/VBプロジェクトの参照に、「MetroHelpers」プロジェクトを追加する
以上で、C#/VBプロジェクトの中から「MetroAssist」クラス(「MetroHelpers」名前空間)が利用できるようになる。
C++用のWindowsランタイムコンポーネントのプロジェクトを追加する
ソリューションに新しいプロジェクトを追加するときのダイアログである。
[インストール済み]のテンプレートから[他の言語]−[Visual C++]−[Windows]−[ユニバーサル]を選び、中央部分のリストで[Windows ランタイム コンポーネント (ユニバーサル Windows)]を選択し、プロジェクト名には「MetroHelpers」と付け、プロジェクトを作成する。プロジェクト名がそのまま名前空間の名前になるので、注意してほしい(異なるプロジェクト名にすると、コードの修正が必要になる)。
この「MetroAssist」クラスを使えば、次のようにしてC#/VBのコードから画面の物理サイズを取得できる。
// UIにバインドするため、メンバー変数に結果を保持する
private double ScreenWidth { get; set; }
private double ScreenHeight { get; set; }
private void GetScreenInfo()
{
// 画面のサイズ(物理ピクセル)
var res
= (new MetroHelpers.MetroAssist()).ScreenResolution;
ScreenWidth = res.X; // 画面の幅
ScreenHeight = res.Y; // 画面の高さ
}
' UIにバインドするため、メンバー変数に結果を保持する
Private Property ScreenWidth As Double
Private Property ScreenHeight As Double
Private Sub GetScreenInfo()
' 画面のサイズ(物理ピクセル)
Dim res _
= (New MetroHelpers.MetroAssist()).ScreenResolution
ScreenWidth = res.X ' 画面の幅
ScreenHeight = res.Y ' 画面の高さ
End Sub
このコードは、MainPage.xaml.cs/.vbファイルに追加する。この他、画面が表示されたときなどに、この「GetScreenInfo」メソッドを呼び出すコードが必要である。
画面の実効サイズを取得するには?
UWPアプリで扱うピクセルは、物理ピクセルではなく実効ピクセル(effective pixels、「有効ピクセル」とも)である(MSDNドキュメント「ユニバーサル Windows プラットフォーム (UWP) アプリ用レスポンシブ デザイン 101」参照)。上述の方法で取得した画面サイズは物理ピクセルなので、プログラム中で扱うには実効ピクセルに換算する必要がある。
物理ピクセルと実効ピクセルの比率がDisplayInformationクラス(Windows.Graphics.Display名前空間)のRawPixelsPerViewPixelプロパティで得られるので、次のコードのようにして実効ピクセルを求める。
// UIにバインドするため、メンバー変数に結果を保持する
private double ScreenWidth { get; set; }
private double ScreenHeight { get; set; }
private double ScreenEffectiveWidth { get; set; }
private double ScreenEffectiveHeight { get; set; }
private void GetScreenInfo()
{
// 画面のサイズ(物理ピクセル)
var res
= (new MetroHelpers.MetroAssist()).ScreenResolution;
ScreenWidth = res.X; // 画面の幅
ScreenHeight = res.Y; // 画面の高さ
// 画面のサイズ(実効ピクセル)
double scale
= Windows.Graphics.Display.DisplayInformation
.GetForCurrentView().RawPixelsPerViewPixel;
ScreenEffectiveWidth = ScreenWidth / scale;
ScreenEffectiveHeight = ScreenHeight / scale;
}
' UIにバインドするため、メンバー変数に結果を保持する
Private Property ScreenWidth As Double
Private Property ScreenHeight As Double
Private Property ScreenEffectiveWidth As Double
Private Property ScreenEffectiveHeight As Double
Private Sub GetScreenInfo()
' 画面のサイズ(物理ピクセル)
Dim res _
= (New MetroHelpers.MetroAssist()).ScreenResolution
ScreenWidth = res.X ' 画面の幅
ScreenHeight = res.Y ' 画面の高さ
' 画面のサイズ(実効ピクセル)
Dim scale As Double _
= Windows.Graphics.Display.DisplayInformation _
.GetForCurrentView().RawPixelsPerViewPixel
ScreenEffectiveWidth = ScreenWidth / scale
ScreenEffectiveHeight = ScreenHeight / scale
End Sub
太字の部分を追加した。
実行結果
以上のコードを組み込んで、別途公開のサンプルを作成した。このサンプルには、さらに画面の向き(DisplayInformationクラスのCurrentOrientationプロパティ)と、アプリ自身のウィンドウサイズを表示するようにしてある*4。
*4 アプリ自身のウィンドウサイズを求める方法は、「WinRT/Metro TIPS:画面のサイズを取得するには?[ユニバーサルWindowsアプリ開発]」の「論理サイズを取得するには?」の項を参照。この方法で得られる値は、Windows 8.x/Windows Phone 8.1ではDIP(デバイス非依存ピクセル)単位であったが、UWPアプリでは実効ピクセル単位になっている。
PCのシミュレーターで動作させると次の画像のようになる。
- 横置き(Landscape)、ウィンドウ表示
- 縦置き(Portrait)、ウィンドウ表示
- 横置き、最大化表示
- 横置き、全画面モード
画面の向きや表示モードによらず、画面のサイズが物理ピクセル(「px」)と実効ピクセル(「epx」)で正しく取得できている(実効ピクセルは小数点以下1桁で四捨五入している)。最後の全画面モードでは、ウィンドウのサイズが画面のサイズと一致している。
なお、「最大化表示」と「全画面モード」は異なるものである。「WinRT/Metro TIPS:ウィンドウを全画面モードで表示するには?[Windows 10 UWPアプリ開発]」を参照してほしい。
次の画像は、Windows 10 Mobileのエミュレーターで実行した場合だ。モバイルでは、DirectXで取得できる画面のサイズが画面の向きによらず常に同じになることに注意してほしい。
モバイルエミュレーターでの実行例
物理ピクセルで480×800ピクセルの画面である。
画面の向きによらず、どちらも幅480ピクセル/高さ800ピクセルとレポートされていることに注意。PCとモバイルに対応したUWPアプリの場合は、デバイスによって幅と高さの値を入れ替えるロジックが必要になる。デバイスを識別する方法は「WinRT/Metro TIPS:OSのバージョンやデバイスファミリーを識別するには?[Windows 10 UWPアプリ開発]」を参照。
なお、モバイルでは常に全画面モードなので、アプリ自身のウィンドウサイズは画面サイズに等しい。モバイルだけならDirectXを使わずに済むのである。
まとめ
DirectXを使えば、画面のサイズを取得できる。それにはC++で作成したWindowsランタイムコンポーネントが必要になるが、別途公開のサンプルを活用していただければ幸いである。なお、画面の向きを回転させたときに得られる値がPCとモバイルでは異なるので、ご注意願いたい。
事前準備
デスクトップ用とモバイル(=Windows 10を搭載したスマートフォン)用のUWPアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Community 2015 Update 1(およびVisual Studio Tools for Universal Windows Appsバージョン1.2)を使っている。
- SLAT対応のPC*5
- 64bit版*6 Windows 10 Pro*7
- Visual Studio 2015(以降、VS 2015)*8
- Windows SDK for Windows 10*9、およびVisual Studio Tools for Universal Windows Apps*10
*5 SLAT対応ハードウエアは、モバイルエミュレーターの実行に必要だ。ただし未対応でも、ソースコードのビルドと実機でのデバッグは可能である。SLAT対応のチェック方法はMSDNブログの「Windows Phone SDK 8.0 ダウンロードポイント と Second Level Address Translation (SLAT) 対応PCかどうかを判定する方法」を参照。なお、SLAT対応ハードウエアであっても、VM上ではエミュレーターが動作しないことがあるのでご注意願いたい。
*6 Windows 10 Mobileエミュレーターを使用しないのであれば、32bit版でもよい。
*7 開発に使うWindows 10は「開発者モード」を有効にしておくこと(「設定アプリ」の[更新とセキュリティ]−[開発者向け]で、[開発者モード]ラジオボタンを選択)。そうしないと、VS 2015のXAMLエディターがエラーになる。また、本稿の内容が適用できるのは、Windowsのライフサイクル管理の上では「Windows 10, released in July 2015」と呼ばれるリリース、またはそれ以降。WinVerコマンドで表示されるバージョンは「10.0 (ビルド 10240)」。UWPアプリ開発におけるデバイスファミリーのバージョン指定としては「10.0.0.0」(Package.appxmanifestファイルのTargetDeviceFamily)である。なお、モバイルエミュレーターを使用しないのであれば、Home版でもよい。
*8 本稿に掲載したコードを試すだけなら、無償のExpressエディションやCommunityエディションで構わない。Visual Studio Express 2015 for Windows 10(製品版)はマイクロソフトのページから無償で入手できる(ページの左側で[Visual Studio 2015]−[Express 2015 for Windows 10]と選ぶ)。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、UWPアプリの開発には「for Windows 10」を使う(「for Desktop」はWPF/Windowsフォーム/Win32 APIのアプリ開発用)。また、Visual Studio Community 2015(製品版)もマイクロソフトのページから無償で入手できる。なお、英語版がインストールされた場合には、Microsoft Visual Studio 2015 Language Packの日本語版を追加インストールし、[オプション]ダイアログで言語を切り替える。
*9 使用しているVS 2015に含まれていない場合は、Windows SDK for Windows 10のページからダウンロードできる。本稿の内容が適用できるのは、作成したプロジェクトのプロパティに表示されるターゲットバージョンが「10.0; ビルド 10240」またはそれ以降。ただし、プレビュー版のSDKを使うと、Windowsストアには提出できなくなるので注意してほしい。
*10 Visual Studio Tools for Universal Windows Appsは、2015年9月16日付でバージョン1.1に上がっている。それ以前に開発環境を整えた人は、個別にアップデートする必要がある。「Update 1.1: Release Notes and Installation Instructions」(英語)を参照。なお、2015年11月30日付でリリースされたVisual Studio 2015 Update 1を使っている場合は、Visual Studio Tools for Universal Windows Appsをインストールするだけでよい(バージョン1.2が入る)。
Copyright© Digital Advantage Corp. All Rights Reserved.