none
”テキスト、アプリ、その他の項目のサイズ”の値を取得するには? RRS feed

  • 質問

  • OSのディスプレイ設定で、”テキスト、アプリ、その他の項目のサイズを変更する”
    がありますが、これが 125%などと変更できます。この値を取得するにはどうしたら良いですか?


    2016年8月2日 5:07

回答

  • なるほどこの方法であれば、DPIawareでなくても取れるのですね。

    ケチばかりですみませんが、CreateDC()したらDeleteDC()すべきです。特にMoveイベント毎に実行されるのであれば。

    # kenjinoteさんのコードもg.ReleaseHdc()とg.Dispose()をすべきですが、こちらは実行回数が1回なので…まだなんとか。とはいえこういうフォーラムのコードを切り貼りして利用されることを考えるときちんと閉じるべきとは思います。

    2016年8月2日 14:15
  • http://stackoverflow.com/questions/5977445/how-to-get-windows-display-settings

    上記のフォーラムに載っていたコードをほぼそのままですが、

    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            [DllImport("gdi32.dll")]
            static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
            public enum DeviceCap
            {
                VERTRES = 10,
                DESKTOPVERTRES = 117,
            }
            private float GetScalingFactor()
            {
                Graphics g = Graphics.FromHwnd(IntPtr.Zero);
                IntPtr desktop = g.GetHdc();
                int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
                int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES);
                float ScreenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;
                return ScreenScalingFactor; // 1.25 = 125%
            }
            public Form1()
            {
                InitializeComponent();
                float fScaling = GetScalingFactor() * 100;
                MessageBox.Show(fScaling.ToString());
            }
        }
    }

    というような感じで取得が可能なようです。



    2016年8月2日 5:24
  • 仰るとおり後からマルチモニタ対応が引っ掛かって調べてました。少々時間がかかりましたが、以下 StackOverFlow のコードをマルチモニタ対応に修正したものです。Move イベント内で実行するよう変更しましたので、プライマリとセカンダリで設定が違う場合、モニタ間で画面移動時にただちに反映されます。何かの参考になれば幸いです。


    using System; using System.Runtime.InteropServices; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public enum DeviceCap { VERTRES = 10, DESKTOPVERTRES = 117, } [DllImport("gdi32.dll")] static extern int GetDeviceCaps(IntPtr hdc, int nIndex); [DllImport("gdi32.dll")] static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData); [DllImport("gdi32.dll", EntryPoint = "DeleteDC")] static extern bool DeleteDC(IntPtr hdc); public Form1() { InitializeComponent(); } private void Form1_Move(object sender, EventArgs e) { float fScaling = GetScalingFactor() * 100; this.label1.Text = fScaling.ToString(); } private float GetScalingFactor() { var sc = Screen.FromHandle(this.Handle); var desktop = CreateDC(sc.DeviceName, null, null, IntPtr.Zero); int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES); int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES); DeleteDC(destop); float ScreenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight; return ScreenScalingFactor; // 1.25 = 125% } } }




    本フォーラムは、ユーザー(開発者)同士で情報交換を行うためのコミュニティです。初めて利用される方は、以下のアナウンスをご覧ください。 https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?




    2016年8月2日 9:18
    モデレータ

すべての返信

  • http://stackoverflow.com/questions/5977445/how-to-get-windows-display-settings

    上記のフォーラムに載っていたコードをほぼそのままですが、

    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            [DllImport("gdi32.dll")]
            static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
            public enum DeviceCap
            {
                VERTRES = 10,
                DESKTOPVERTRES = 117,
            }
            private float GetScalingFactor()
            {
                Graphics g = Graphics.FromHwnd(IntPtr.Zero);
                IntPtr desktop = g.GetHdc();
                int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
                int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES);
                float ScreenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;
                return ScreenScalingFactor; // 1.25 = 125%
            }
            public Form1()
            {
                InitializeComponent();
                float fScaling = GetScalingFactor() * 100;
                MessageBox.Show(fScaling.ToString());
            }
        }
    }

    というような感じで取得が可能なようです。



    2016年8月2日 5:24
  • ちょうど同じ問題について調べてました。GetDeviceCaps は間違った値を返すケースがあるようです。

    GetDeviceCapsが常にDPI96を返す問題と解決方法について

    この Gist のドキュメントに書かれている通り、DPI Aware (高DPI対応) なアプリであることを Windows に明示することで、GetDeviceCapsが正しい値を返すようになるとのことです。以下、grabacr さんの記事も詳しいですし、GitHub のサンプルも参考になります。

    Windows 8.1 で加わった Per-Monitor DPI と WPF での対応方法

    ただしいくつか落とし穴があって、こちらの調査では ClickOnce で配布する場合、Manifest を編集すると配布時に例外が発生します。Visual Studio 2015 でマニフェストを作成した場合は、最初から高DPI対応個所がコメントアウトされてますが、これを解除しても同様にインストールに失敗します。現時点まだ原因つかめていません(汗


    本フォーラムは、ユーザー(開発者)同士で情報交換を行うためのコミュニティです。初めて利用される方は、以下のアナウンスをご覧ください。 https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?

    2016年8月2日 6:01
    モデレータ
  • 私の認識不足かも知れませんが、高DPI対応を宣言していないアプリは、DPI 仮想化によって GetDeviceCaps は、LOGPIXELSX、LOGPIXELSY を引数にした場合、常に 96 を返すようですが、私の提示したサンプルの、VERTRES と
    DESKTOPVERTRES を引数にした場合 GetDeviceCaps は正しい値を返すと思っておりました。

    間違いでしょうか?

    2016年8月2日 6:28
  • StackOverFlow のスレッド読みつつ Surface Pro 4 で試してみました。

    > Both graphics.DpiX and DeviceCap.LOGPIXELSX return 96 on Surface Pro in all scaling levels.

    と回答あるとおり、提示されたコードの GetDeviceCaps の引数をLOGPIXELSX、LOGPIXELSY に置き換えDPIを変更しながら試したところ、どれも GetDeviceCaps は 96 を返しましが、VERTRES と DESKTOPVERTRES の組み合わせで近似値を返却してくれました。これならいけそうです!(あくまで近似値なのに注意)


    このサンプルどおり、DESKTOPVERTRES で実際のピクセル数を取得し、VERTRES の論理ピクセルで除算してDPIの値を求めるのがベストかも知れません。



    本フォーラムは、ユーザー(開発者)同士で情報交換を行うためのコミュニティです。初めて利用される方は、以下のアナウンスをご覧ください。 https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?




    2016年8月2日 7:36
    モデレータ
  • Windows 10ですとPer-Monitor DPIであり、モニター毎に異なる値になりますが、挙げられているコードはどちらのモニターの値を取得するものでしょうか? また、他のモニターの値の取得方法について言及されていませんが、果たしてこれが回答になっているのでしょうか?
    2016年8月2日 8:22
  • 仰るとおり後からマルチモニタ対応が引っ掛かって調べてました。少々時間がかかりましたが、以下 StackOverFlow のコードをマルチモニタ対応に修正したものです。Move イベント内で実行するよう変更しましたので、プライマリとセカンダリで設定が違う場合、モニタ間で画面移動時にただちに反映されます。何かの参考になれば幸いです。


    using System; using System.Runtime.InteropServices; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public enum DeviceCap { VERTRES = 10, DESKTOPVERTRES = 117, } [DllImport("gdi32.dll")] static extern int GetDeviceCaps(IntPtr hdc, int nIndex); [DllImport("gdi32.dll")] static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData); [DllImport("gdi32.dll", EntryPoint = "DeleteDC")] static extern bool DeleteDC(IntPtr hdc); public Form1() { InitializeComponent(); } private void Form1_Move(object sender, EventArgs e) { float fScaling = GetScalingFactor() * 100; this.label1.Text = fScaling.ToString(); } private float GetScalingFactor() { var sc = Screen.FromHandle(this.Handle); var desktop = CreateDC(sc.DeviceName, null, null, IntPtr.Zero); int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES); int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES); DeleteDC(destop); float ScreenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight; return ScreenScalingFactor; // 1.25 = 125% } } }




    本フォーラムは、ユーザー(開発者)同士で情報交換を行うためのコミュニティです。初めて利用される方は、以下のアナウンスをご覧ください。 https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?




    2016年8月2日 9:18
    モデレータ
  • なるほどこの方法であれば、DPIawareでなくても取れるのですね。

    ケチばかりですみませんが、CreateDC()したらDeleteDC()すべきです。特にMoveイベント毎に実行されるのであれば。

    # kenjinoteさんのコードもg.ReleaseHdc()とg.Dispose()をすべきですが、こちらは実行回数が1回なので…まだなんとか。とはいえこういうフォーラムのコードを切り貼りして利用されることを考えるときちんと閉じるべきとは思います。

    2016年8月2日 14:15
  • なるほどこの方法であれば、DPIawareでなくても取れるのですね。

    ケチばかりですみませんが、CreateDC()したらDeleteDC()すべきです。特にMoveイベント毎に実行されるのであれば。

    # kenjinoteさんのコードもg.ReleaseHdc()とg.Dispose()をすべきですが、こちらは実行回数が1回なので…まだなんとか。とはいえこういうフォーラムのコードを切り貼りして利用されることを考えるときちんと閉じるべきとは思います。

    まったくおっしゃる通りです。上のコードにDeleteDC 追加しておきました(汗

    本フォーラムは、ユーザー(開発者)同士で情報交換を行うためのコミュニティです。初めて利用される方は、以下のアナウンスをご覧ください。 https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?

    2016年8月2日 14:38
    モデレータ
  • kenjinote さん、具体的なコーディングありがとうございました。
    こちらでも上手くいきました。

    ひらぽん さん、ありがとうございました。 
    とりあえずは、シングルモニターで良いのですが、
    マルチモニタ対応が必要になったら、利用させて頂きます。

    佐祐理 さん、ありがとうございました。
    コードを切り貼りして利用している方なので、助かります。

    2016年8月3日 2:20