Seaside Laboratory

Posts

EnumDisplaySettings は同じ設定を複数返すことがある

サポートしている画面モードを取得するには EnumDisplaySettings を使用する。

BOOL EnumDisplaySettings(
  LPCTSTR lpszDeviceName,  // ディスプレイデバイス
  DWORD iModeNum,          // グラフィックスモード
  LPDEVMODE lpDevMode      // グラフィックスモードの設定
);

マニュアルに、

まず、iModeNum パラメータを 0 に設定して最初の呼び出しをします。その後、戻り値が 0 になるまで、iModeNum パラメータに 1 ずつ加算して繰り返し呼び出します。

と書かれているように iModeNum を加算しながら呼び出すことで全モードを取得することができる。

設定値は引数 lpDevMode に書きこまれるが、

EnumDisplaySettings 関数は、次の 5 つの DEVMODE メンバに値を設定します。

dmBitsPerPel
dmPelsWidth
dmPelsHeight
dmDisplayFlags
dmDisplayFrequency

有効なのは特定のメンバ変数に限られる。

有効なメンバー変数の組み合わせで一意になるリストを生成するプログラムを書いていたのだが、同じ設定値が複数回返ってくるので重複が発生してしまった。原因がよくわからないのでデバッガーで重複した構造体のメンバー変数を確認したところ、dmDefaultSource と dmDisplayFixedOutput の内容が異なっていた。

dmDefaultSource

給紙方法を指定します。プリンタで利用可能な給紙方法の一覧を取得するには、DC_BINS フラグを指定して DeviceCapabilities 関数を使用します。

プリンタ用の設定っぽいので無視。

dmDisplayFixedOutput

固定解像度ディスプレイデバイスの場合のみ、高解像度ディスプレイでディスプレイが低解像度モードを表示する方法。たとえば、ディスプレイデバイスの解像度が 1024 x 768 ピクセルに固定されていて、そのモードが 640 x 480 ピクセルに設定されているとします。デバイスは、1024 x 768 の画面スペースの内部のどこかに 640 x 480 の画像を表示するか、または 640 x 480 の画像を拡大してより大きな画面スペースを埋めることができます。

これが原因っぽい。値の内訳は以下の通り。

意味
DMDFO_DEFAULT ディスプレイのデフォルト設定
DMDFO_CENTER 低解像度の画像は大きな画面スペースの中央に配置されます。
DMDFO_STRETCH 低解像度の画像は、より広い画面スペースを埋めるように引き伸ばされます。

物理ピクセル数より小さい画面を表示するときにどう見せるのかを指定する値ということか。たしかに列挙された画面モードを見ると、小さい解像度は 3 パターン返ってくるのに対し、液晶の最大解像度だけは DMDFO_DEFAULT の 1 パターンしか返ってこなかった。

このメンバー名で検索して出てきた「Scaling the Desktop Image」というドキュメントを読むと、

The EnumDisplaySettings Win32 function returns DMDFO_DEFAULT in the dmDisplayFixedOutput member of the DEVMODE structure that the lpDevMode parameter points to when the caller requests the Windows 7 scaling types.

EnumDisplaySettings Win32 関数は、呼び出し元が Windows 7 のスケーリングタイプを要求したときに、lpDevMode パラメーターが指す DEVMODE 構造体の dmDisplayFixedOutput メンバーに DMDFO_DEFAULT を返します。

DEVMODE の dmDisplayFixedOutput に値が入ると書かれている。有効になるメンバーは 5 つだけじゃなかったんかい!

このドキュメントには続きがあり、DirectX と ChangeDisplaySettings を組み合わせる際の注意点が書かれている。

Microsoft DirectX 9L and earlier runtimes require that applications always call the ChangeDisplaySettingsEx function without DM_DISPLAYFIXEDOUTPUT set in the dmFields member of the DEVMODE structure that the lpDevMode parameter points to. DirectX 10 and later runtimes allow applications to choose the scaling that those applications pass to ChangeDisplaySettingsEx.

Microsoft DirectX 9L および以前のランタイムでは、アプリケーションは lpDevMode パラメーターが指す DEVMODE 構造体の dmFields メンバーに DM_DISPLAYFIXEDOUTPUT が設定されていない状態で ChangeDisplaySettingsEx 関数を呼び出す必要があります。DirectX 10 以降のランタイムでは、アプリケーションが ChangeDisplaySettingsEx に渡すスケーリングを選択できます。

古い DirectX を使う場合、dmDisplayFixedOutput の指定はするな、ということらしい。

とりあえず今回は画面モードが知りたかっただけなので、DMDFO_DEFAULT 以外を無視することにした。