none
”テキスト、アプリ、その他の項目のサイズ”の値に左右されないディスプレイ解像度の取得方法(DESKTOPVERTRES/DESKTOPHORZRESについて) RRS feed

  • 質問

  • お世話になっております。

    ディスプレイのスクリーンキャプチャをするため、以下のコードを実装しました。

    displayDC = CreateDC(displayName, NULL, NULL, NULL);
    
    width = GetDeviceCaps(displayDC, HORZRES);
    height = GetDeviceCaps(displayDC, VERTRES);
    
    bitmapDC = CreateCompatibleDC(displayDC);
    
    // DIB作成等:省略
    
    BitBlt(bitmapDC, 0, 0, width, height, displayDC, 0, 0, SRCCOPY)

    Win7の環境であれば問題なくキャプチャできていたのですが、Win8以降の"テキスト、アプリ、その他の項目のサイズ"が100%より大きい場合、ディスプレイの一部分(左上を起点とした縮小矩形)しかキャプチャできませんでした。

    デバッグしてみるとwidth/heightが実際のディスプレイ解像度より小さくなっており、上記のパーセンテージで割った解像度が取得されていることがわかりました。

    そこで、この設定に左右されないディスプレイ解像度を取得できないかとググっていたところ、GetDeviceCapsの引数としてDESKTOPVERTRES/DESKTOPHORZRESというものがあることを知り、これを使ってみたところ設定によらず常にディスプレイ解像度が取得でき、正しい画像をキャプチャすることができるようになりました。

    しかし、このDESKTOPVERTRES/DESKTOPHORZRESはGetDeviceCapsのMSDNページ(https://msdn.microsoft.com/ja-jp/library/cc428670.aspx)になぜか載っていません。本当に使ってよいのか、正式にサポートされているのか、うまく動作しない環境等はないのか、といったことが気になっています。

    載っていない理由や、サポート状況など、ご存じの方がいらっしゃいましたらご教授いただけないでしょうか?よろしくお願いいたします。


    • 編集済み makihiro 2017年2月10日 5:55
    2017年2月10日 5:53

回答

  • ドキュメントに載っていない以上、正式にはサポートされないと考えるべきです。wingdi.hには

    // Display driver specific
    
    #define VREFRESH        116  /* Current vertical refresh rate of the    */
                                 /* display device (for displays only) in Hz*/
    #define DESKTOPVERTRES  117  /* Horizontal width of entire desktop in   */
                                 /* pixels                                  */
    #define DESKTOPHORZRES  118  /* Vertical height of entire desktop in    */
                                 /* pixels                                  */
    #define BLTALIGNMENT    119  /* Preferred blt alignment                 */
    

    と定義されていました。推測ではありますがディスプレイドライバー依存であり、DWMが導入されたVista以降は一通りエミュレートされるようには思います。

    ところで、単にディスプレイ解像度のみが得られれば問題が解決するのでしょうか? 根本の原因は作ろうとしているアプリケーションがHigh-DPI未対応なために、仮想化された解像度を渡されているのではないでしょうか。そうであればHigh-DPIに対応することが本筋だと思います。

    • 回答としてマーク makihiro 2017年2月14日 0:33
    2017年2月10日 8:07

すべての返信

  • なぜ、ドキュメントに載っていないのかその真意はわかりませんが、昔のSDKで確認してみましたがWindows 2000のころから定義されているようでした。

    個人的には、使っても問題はないと思いますが(急にサポートされなくなることはないのではないか・・・?)、もしご心配でしたら、他の取得方法で取得するのはどうでしょうか?GetMonitorInfo関数とEnumDisplaySettings関数はドキュメントにも載っていますし、マルチモニタ対応する場合も使えます。

    const POINT ptZero = { 0, 0 };
    HMONITOR hPrimaryMonitor = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
    MONITORINFOEX MonitorInfoEx;
    MonitorInfoEx.cbSize = sizeof(MonitorInfoEx);
    if (GetMonitorInfo(hPrimaryMonitor, &MonitorInfoEx) != 0)
    {
    	DEVMODE dm = { 0 };
    	dm.dmSize = sizeof(DEVMODE);
    	if (EnumDisplaySettings(MonitorInfoEx.szDevice, ENUM_CURRENT_SETTINGS, &dm) != 0)
    	{
    		int nMonitorWidth = dm.dmPelsWidth; // プライマリモニターの横幅(ピクセル数)
    		int nMonitorHeight = dm.dmPelsHeight;  // プライマリモニターの縦幅(ピクセル数)
    	}
    }

    参考サイト:
    https://msdn.microsoft.com/ja-jp/library/cc428707.aspx
    https://msdn.microsoft.com/ja-jp/library/cc428506.aspx
    2017年2月10日 6:46
  • ドキュメントに載っていない以上、正式にはサポートされないと考えるべきです。wingdi.hには

    // Display driver specific
    
    #define VREFRESH        116  /* Current vertical refresh rate of the    */
                                 /* display device (for displays only) in Hz*/
    #define DESKTOPVERTRES  117  /* Horizontal width of entire desktop in   */
                                 /* pixels                                  */
    #define DESKTOPHORZRES  118  /* Vertical height of entire desktop in    */
                                 /* pixels                                  */
    #define BLTALIGNMENT    119  /* Preferred blt alignment                 */
    

    と定義されていました。推測ではありますがディスプレイドライバー依存であり、DWMが導入されたVista以降は一通りエミュレートされるようには思います。

    ところで、単にディスプレイ解像度のみが得られれば問題が解決するのでしょうか? 根本の原因は作ろうとしているアプリケーションがHigh-DPI未対応なために、仮想化された解像度を渡されているのではないでしょうか。そうであればHigh-DPIに対応することが本筋だと思います。

    • 回答としてマーク makihiro 2017年2月14日 0:33
    2017年2月10日 8:07
  • kenjinote様

    ご回答ありがとうございます。

    モニターハンドル経由で取得する方法もあるのですね。大変参考になりました。ありがとうございます。

    大変恐縮ですが、今回は高DPI対応宣言することで解決できましたので、佐祐理様の返信を回答としてマークさせていただきたいと思います。

    2017年2月14日 0:31
  • 佐祐理様

    ご回答ありがとうございます。

    高DPI対応のマニフェストを追加したところ、全く同じコードでディスプレイの解像度を取得することができるようになりました。

    回答としてマークさせていただきます。

    2017年2月14日 0:33
  • ご返信ありがとうございます。

    Windows 8.1よりディスプレイごとにDPI値を設定できますので、もしマニフェストの対応で画面解像度(HORZRES,VERTRES)を取得するということでありましたら、<dpiAware>True</dpiAware>だけでは正しく取得できないことがあります。Per-Monitor DPI 対応<dpiAware>True/PM</dpiAware>と宣言する必要があります。

    2017年2月14日 1:10
  • kenjinote様

    情報ありがとうございます。

    今回はVisualStudioのプロジェクト設定のマニフェストツールで対応し、[モニターごとの高いDPI認識]に設定しましたので、アドバイスいただいた設定になっていると思います。

    [マニフェストツール]→[入出力]→[DPI認識]

    プロジェクトファイルを見たところ、以下が追加されていました。

    <Manifest>
        <EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness>
    </Manifest>

    2017年2月14日 1:59
  • ご返信ありがとうございます。

    Visual Studioの設定で「モニターごとの高いDPI認識」を選択されていれば問題ないと思います。ビルドで生成されるexeの中のマニフェストには<dpiAware xmlns="...略...">True/PM</dpiAware>と宣言されていると思います。

    2017年2月14日 2:22
  • kenjinote様

    失礼しました。プロジェクトファイルと実際のマニフェストは別物ですよね。

    いろいろとフォローいただきありがとうございました。

    2017年2月14日 3:46