マルチディスプレイ情報の紐づけについて

Windows11 において取得できるディスプレイ情報

Windows11 において、取得できるディスプレイの情報として次のようなものがあります。

Windows11 の設定にあるディスプレイ情報

設定 > システム > ディスプレイ より確認できるディスプレイ情報です。複数ディスプレイが接続されていると、複製モード・拡張モードの選択やディスプレイの配置などを設定することができます。

WMIから取得できるディスプレイ情報

WMI より WmiMonitorID より、ディスプレイの型名・シリアル番号・製造年などを取得することができます。複数ディスプレイの場合、全ディスプレイの情報を取得することができます。

.NET Framework の Screen クラスから取得できる情報

ディスプレイの位置情報やサイズを取得することができます。

ディスプレイ情報の紐づけについて

上記のようにディスプレイの情報は、それぞれ取得することができますが、それぞれ個別の情報であるため、ある型番のディスプレイがどこに配置されているかは簡単に取得できません。

ネットでこれらの紐づけについて調べていると、取得できる配列順で紐づけているソースコードが見られましたが(どのサイトか失念しました。。。海外のサイトでした。)、ディスプレイが2台構成で 1 番のディスプレイがメインであれば問題なく紐づけることができるでしょう。しかし、今回 3 台構成での紐づけで上手くいかないケースがありました。

ディスプレイを抜き差しすることにより「Windows11 の設定にあるディスプレイ情報」と「WMIから取得できるディスプレイ情報」の順番は異なることがあります。

また 3 台構成の場合、ディスプレイの複製モードは 2 台のディスプレイに限られますが、複製モードと拡張モードの変更を繰り返すことにより、「WMIから取得できるディスプレイ情報」と「.NET Framework の Screen クラスから取得できる情報」の配列順が異なることがあることを確認しました。すなわち、これはプログラム上で自動でディスプレイ情報を紐づけることが危険であることが判ります。

どうすれば紐づけることができるか

実は今のところ、ある型番のディスプレイがどこに配置されているかは、正しい情報の取得方法が判らない状況です。少なくとも上記情報を紐づけができないことが判りました。最終的に私は自動で判別することを諦め、設定画面を設け手動で紐づけるようにしました。

確認した環境

  • Windows11
  • .NET Framework 4.7.2

ディスプレイ構成

  • ディスプレイ1 … ノート PC のディスプレイ (DELL Inspiron 14 5420)
  • ディスプレイ2 … acer G225
  • ディスプレイ3 … SHARP TV

ソースコード

using System;
using System.Data;
using System.Linq;
using System.Windows.Forms;
using System.Management;

namespace DisplayInfo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            //WmiMonitorIDを取得
            GetWmiMonitorID();

            //AllScreensを取得
            GetAllScreens();
        }

        /// <summary>
        /// WmiMonitorIDを取得
        /// </summary>
        private void GetWmiMonitorID()
        {
            string log = "===WmiMonitorID" + Environment.NewLine;

            var searcher = new ManagementObjectSearcher("root\\WMI", "SELECT * FROM WmiMonitorID");
            int i = 0;

            foreach (var queryObj in searcher.Get())
            {
                string UserFriendlyName = UInt16ArrayToString((ushort[])queryObj["UserFriendlyName"]);
                string SerialNumberID = UInt16ArrayToString((ushort[])queryObj["SerialNumberID"]);

                log += (i + 1).ToString() + ": " + UserFriendlyName + "/" + SerialNumberID + Environment.NewLine;

                i++;
            }

            Console.WriteLine(log);
        }

        /// <summary>
        /// AllScreensを取得
        /// </summary>
        private void GetAllScreens()
        {
            string log = "===AllScreens" + Environment.NewLine;

            var screens = Screen.AllScreens;
            for (int i = 0; i < screens.Length; i++)
            {
                log += (i + 1).ToString() + ": " +
                    screens[i].Bounds.X.ToString() + ", " +
                    screens[i].Bounds.Y.ToString() + ", " +
                    screens[i].Bounds.Width.ToString() + ", " +
                    screens[i].Bounds.Height.ToString() + ", " +
                    Environment.NewLine;
            }

            Console.WriteLine(log);
        }

        private static string UInt16ArrayToString(ushort[] ushortArray)
        {
            if (ushortArray == null)
            {
                return "";
            }
            else
            {
                return new string(ushortArray.Select(u => (char)u).Where(c => c != 0).ToArray());
            }
        }
    }
}

情報が異なっていたケース

acer G225 はノートPCディスプレイの左側にあるため (-1920, 0) に位置しますが、 All Screens の配列の2番目を見ると (1920, 0) になっています。このことから「WMIから取得できるディスプレイ情報」と「.NET Framework の Screen クラスから取得できる情報」の配列順が異なることがあるということが判ります。

まとめ

今回は .NET Framework で取得できる情報において、「WMIから取得できるディスプレイ情報」と「.NET Framework の Screen クラスから取得できる情報」からプログラム上で自動で紐づけることができないことを紹介しました。できれば自動で紐づけたいですが、今のところ判明していません。。。とりあえず手動で紐づける方法で実装を進めます。