最後に「【PropertyGrid】とはいえ、何の情報が見たいのか?」を書いた時からちょっと時間が経ちましたが、

 

PCの記憶装置(SSDやHD)に保存しているファイルの属性情報とか、画像ファイルの属性情報や、現在動いているウィンドウの属性情報を調べるツール

 

として表題の"PropertyAny"("Any"はいかにも大袈裟ですが)というプログラムを作りましたので、ソースプログラム(PropertyAny.cs ↓参照)をご紹介いたします。

 

実は最初「こんなプログラム書くのも面倒」とChat-GPTに書かせたら、画面レイアウトが「落第以下」の状態でプッシュボタンが画面の左 2 / 3 で、右にPropertyGridが 1 / 3という奇天烈ものだったので、 彼の基本レイアウトコントロール(SplitterとPanel)を生かして何とかみられるようにしたのが、

 

 

です。でもこれ、ダイアログを操作する際にメインのPropertyGrid画面を交差して(右利き用)マウスを動かすのって(後天的右利きで、原初的左利きである私にも)煩わしく感じます。

 

ということで、

 

結局やり直して

 

 

このようにしました。(起動すると最初に表示されるのはPropertyAnyプログラム自体のプロパティです。)

 

右に3つあるプッシュボタンが「ファイルを開く」ダイアログで選択したファイルの「ファイル情報」、

 

(2025年10月9日追記-ファイルの選択はドラッグアンドドロップでもできます。)

 

次が「ファイルを開く」ダイアログで選択したイメージファイルの「画像情報」で、

 

(2025年10月9日追記-ファイルの選択はドラッグアンドドロップでもできます。)

 

最後に現在デスクトップで動いている(ウィンドウタイトルを持つ)ウィンドウプログラムをListViewで列挙して表示し、その内の好きなものを"SelectWindow”ダイアログ(

:これはWindowInfoクラスを内蔵する"SelectWindows.cs"というスタンドアローンプログラムを作って、それを加工して導入しました。(「ウィンドウタイトルを持つ」プロセスに限定したのは、これで制限しないと「Program Manager状態」になるからです。)

 

 

で選択する「ウィンドウ情報」(以下↓はPropertyAny自体)

 

 

を見ることが出来ます。

 

そっ、唯それだけ。

 

ですが、この仕組みを応用すると他のアプリで面白いことが出来そうな可能性を感じますね。

 

【PropertyAny.cs】

/////////////////////
// PropertyAny.cs
// 参考サイト:https://dobon.net/vb/dotnet/control/propertygrid.html
/////////////////////

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.ComponentModel;                //PropertyGridの[]機能を使う為
using System.IO;                                                 //FileInfoオブジェクト取得の為
using System.Diagnostics;                              //GetProcessを使う為
using System.Runtime.InteropServices;    //GetWindowRectを使用するために必要

namespace PropertyAny
{
    public class App : Form
    {
        private SplitContainer split;    //解説:クライアントエリアを分割するスプリッターコントロールです。
        private PropertyGrid propGrid;    //解説:主人公のPropertyGridコントロールです。
        private Panel panel;    //解説:ペインにコントロールを並べるためのコンテナコントロールです。
        private Button btnExit;    //解説:終了ボタン
        private Button btnFile;    //解説:ファイル情報用ボタン
        private Button btnImage;    //解説: 画像情報用ボタン
        private Button btnPickWindow;    //解説:デスクトップのウィンドウ用ボタン

        [STAThread]
        public static void Main()
        {
            Application.Run(new App());
        }

        public App()
        {
            this.Text = "PropertyAny";
            this.Width = 640;
            this.Height = 480;
            this.MinimumSize = new Size(640, 240);
            //Drag and Dropでの処理
            this.AllowDrop = true;
            this.DragEnter += PropGrid_DragEnter;
            this.DragDrop += PropGrid_DragDrop;
            //ウィンドウアイコンをプログラムアイコンにする
            this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
/*        //解説:
            Application.ExecutablePathは実行可能ファイル(.exe)の完全なパスを、
            myOwn.Locationはコードが実行されている場所のファイルパスを取得します。
            Application.ExecutablePathはデスクトップアプリケーションで用いられ、
            myOwn.Locationは主にASP.NETなどのウェブアプリケーション環境で、現在の
            リソースの物理パスを取得するために使用されます。但し、myOwnを使うには
            (1)using System.Reflection;    //Assemblyを使う為
            (2)Assembly myOwn = Assembly.GetEntryAssembly();
            とすることが必要です。
*/

            //スプリッター
            split = new SplitContainer();
            split.Dock = DockStyle.Fill;
            split.Orientation = Orientation.Vertical;
            split.SplitterDistance = 240;
            this.Controls.Add(split);
            //左PropertyGrid
            propGrid = new PropertyGrid();
            propGrid.Dock = DockStyle.Fill;
            propGrid.SelectedObject = this;
            split.Panel1.Controls.Add(propGrid);
            //右側側パネル
            panel = new Panel();
            panel.Dock = DockStyle.Fill;
            split.Panel2.Controls.Add(panel);
            //ボタン(解説:panelのコントロールにする)
            btnExit = new Button {Text = "終了", Dock = DockStyle.Bottom};
            btnExit.Height = 30;
            btnExit.Click += BtnExit_Click;
            panel.Controls.Add(btnExit);
            btnPickWindow = new Button {Text = "ウィンドウ情報", Dock = DockStyle.Top};
            btnPickWindow.Height = 30;
            btnPickWindow.Click += BtnPickWindow_Click;
            panel.Controls.Add(btnPickWindow);
            btnImage = new Button {Text = "画像情報", Dock = DockStyle.Top};
            btnImage.Height = 30;
            btnImage.Click += BtnImage_Click;
            panel.Controls.Add(btnImage);
            btnFile = new Button {Text = "ファイル情報", Dock = DockStyle.Top};
            btnFile.Height = 30;
            btnFile.Click += BtnFile_Click;
            panel.Controls.Add(btnFile);
        }

        //終了処理
        private void BtnExit_Click(object sender, EventArgs e)
        {
            DialogResult dr = MessageBox.Show("終了しますか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if(dr == DialogResult.Yes)
                Close();    //終了する
        }

        //ファイル情報
        private void BtnFile_Click(object sender, EventArgs e)
        {
            using(OpenFileDialog dlg = new OpenFileDialog())
            {
                dlg.Filter = "すべてのファイル|*.*";
                if(dlg.ShowDialog() == DialogResult.OK)
                {
                    propGrid.SelectedObject = new FileInfo(dlg.FileName);    //解説:FileInfoクラスは既存のクラスです。
                }
            }
        }

        //画像情報
        private void BtnImage_Click(object sender, EventArgs e)
        {
            using(OpenFileDialog dlg = new OpenFileDialog())
            {
                dlg.Filter = "画像ファイル|*.bmp;*.png;*.jpg;*.jpeg;*.gif;*.ico";    //解説:画像はGDI+のものです。
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    propGrid.SelectedObject = new ImageInfo(dlg.FileName);    //解説:ImageInfoクラスは本プログラムで定義します。
                }
            }
        }

        //ウィンドウ情報
        private void BtnPickWindow_Click(object sender, EventArgs e)
        {
            WindowInfo wi = new WindowInfo();    //解説:WindowInfoクラスは本プログラムで定義します。
            using(WinSelect wsDlg = new WinSelect(ref wi))
            {
                wsDlg.ShowDialog();
                propGrid.SelectedObject = wi;
            }    
        }

        private void PropGrid_DragEnter(object sender, DragEventArgs e)
        {
            if(e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
        }

        private void PropGrid_DragDrop(object sender, DragEventArgs e)
        {
            string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
            if(files.Length > 0)
            {
                string ImgExtentions = ".bmp, .png, .jpg, .jpeg, .gif, .ico";
                if(ImgExtentions.Contains(Path.GetExtension(files[0]).ToLower()))    //解説:イメージファイルの場合
                {
                    DialogResult dr = MessageBox.Show("ファイル情報にしますか(はい)、またはイメージ情報にしますか(いいえ)?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    if(dr == DialogResult.Yes)    //解説:ファイル情報で表示するか、画像情報で表示するか選択する必要があります。
                        propGrid.SelectedObject = new FileInfo(files[0]);
                    else
                        propGrid.SelectedObject = new ImageInfo(files[0]);
                }
                else
                    propGrid.SelectedObject = new FileInfo(files[0]);
            }
        }
    }

    //イメージ情報取得クラス解説:表示用の情報をプロパティにしておきます。)
    [DefaultProperty("Path")]
    public class ImageInfo
    {
        private Image image;
        private string path;

        [Description("イメージファイルのパスです。")]
        public string Path {get{return path;}}
        [Description("イメージファイルのサイズです。")]
        public Size Size {get{return image.Size;}}
        [Description("イメージファイルのピクセルフォーマットです。")]
        public PixelFormat PixelFormat{get{return image.PixelFormat;}}
        [Description("イメージファイルの画像フォーマットです。")]
        public ImageFormat Format{get{return image.RawFormat;}}

        public ImageInfo(string filePath)
        {
            path = filePath;
            image = Image.FromFile(filePath);
        }
    }

    //ウィンドウ情報取得クラス解説:表示用の情報をプロパティにしておきます。)
    [DefaultProperty("wTitle")]
    public class WindowInfo
    {
        //メンバープロパティ
        [Description("ウィンドウタイトルです。")]
        public string wTitle {get; set;}
        [Description("プロセス名です。")]
        public string wProc {get; set;}
        [Description("ウィンドウハンドルの値です。")]
        public ulong wHnd {get; set;}
        [Description("ウィンドウの幅です。")]
        public int wWidth {get; set;}
        [Description("ウィンドウの高さです。")]
        public int wHeight {get; set;}
        [Description("ウィンドウプログラムのファイルパスです。")]
        public string wPath {get; set;}
    }

    //ウィンドウ選択ダイアログ(解説:現在動いているプロセスの内、ウィンドウタイトルがあるものをピックアップしてリストビューで表示させ、それから好みのものを選択するダイアログです。)
    public partial class WinSelect : Form
    {    //解説:ウィンドウサイズを取得する為に、Win32の構造体と関数を一部使用しています。
        //RECT構造体を定義
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }
        //GetWindowRect関数を宣言
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        //メンバーコントロール
        ListView listView;
        Button btnOK, btnCancel;
        //メンバーフィールド
        WindowInfo wi;

        //コンストラクター
        public WinSelect(ref WindowInfo WinInfo)    //解説:参照引数WinInfoに書き込む形にしました。
        {
            wi = WinInfo;
            this.FormBorderStyle = FormBorderStyle.Sizable; 
            this.MinimizeBox = false;
            this.MaximizeBox = false;
            this.Width = 420;
            this.Height = 240;
            this.Text = "Select Windows";
            this.Load += WinSelect_Load;
        }
 
         //WM_CLOSE時処理
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            string msg;
            if(listView.SelectedItems.Count > 0)
                msg = "\"" + listView.SelectedItems[0].Text + "\"が選択されました。\r\n";
            else
                msg = "何も選択されていませんが、";
            DialogResult dr = MessageBox.Show(msg + "宜しいですか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if(dr == DialogResult.No)    //解説:ダイアログ終了時に選択内容を確認します。
            {
                listView.SelectedItems.Clear();
                e.Cancel = true;
            }
        }

        //WM_CREATE時処理
        private void WinSelect_Load(object sender, EventArgs e)
        {
            //OKボタン
            btnOK = new Button();
            btnOK.Location = new Point(ClientSize.Width - btnOK.Width - 10, ClientSize.Height - btnOK.Height - 10);
            btnOK.Text = "OK";
            btnOK.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right);
            btnOK.Click += btnOK_Click;
            this.Controls.Add(btnOK);

            //Cancelボタン
            btnCancel = new Button();
            btnCancel.Location = new Point(10, ClientSize.Height - btnCancel.Height - 10);
            btnCancel.Text = "Cancel";
            btnCancel.Anchor = (AnchorStyles.Bottom | AnchorStyles.Left);
            btnCancel.Click += btnCancel_Click;
            this.Controls.Add(btnCancel);

            //リストビュー
            listView = new ListView();
            //位置
            listView.Location = new Point(10, 10);
            listView.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right);
            //サイズ
            listView.Width = ClientSize.Width - 20;
            listView.Height = ClientSize.Height - btnOK.Height - 30;
            //複数選択
            listView.MultiSelect = false;
            //一行選択
            listView.FullRowSelect = true;
            //グリッドラインの表示
            listView.GridLines = true;
            //並び替え表示
            listView.Sorting = SortOrder.Ascending;
            //ポイントで選択できるようにする
            listView.HoverSelection = false;
            //シングルクリック選択
            listView.Activation = ItemActivation.OneClick;
            //チェックボックス有効化
            listView.CheckBoxes = false;
            //リストビューの表示方法
            listView.View = View.Details;
            //ヘッダー定義
            ColumnHeader columnTitle = new ColumnHeader{Text = "タイトル", Width = 140};
            ColumnHeader columnProc = new ColumnHeader{Text = "プロセス", Width = 140};
            ColumnHeader columnHdl = new ColumnHeader{Text = "ハンドル", Width = 100};
            ColumnHeader columnWid = new ColumnHeader{Text = "幅", Width = 50};
            ColumnHeader columnHgt = new ColumnHeader{Text = "高さ", Width = 50};
            ColumnHeader columnPath = new ColumnHeader{Text = "ファイルパス", Width = 240};
            ColumnHeader[] colHeaderRegValue = {                //ヘッダー設定
                columnTitle,
                columnProc,
                columnHdl,
                columnWid,
                columnHgt,
                columnPath
            };
            listView.Columns.AddRange(colHeaderRegValue);        //ヘッダー追加
            //データ設定
            foreach (Process proc in Process.GetProcesses())    //全てのプロセスを列挙する
            {
                //メインウィンドウのタイトルがある時だけ列挙する
                if(proc.MainWindowTitle.Length > 0)
                {
                    ListViewItem item = listView.Items.Add(proc.MainWindowTitle);
                    item.SubItems.Add(proc.ProcessName);
                    item.SubItems.Add(proc.MainWindowHandle.ToString());
                    RECT rec;
                    GetWindowRect(proc.MainWindowHandle, out rec);
                    int w = rec.Right - rec.Left;
                    int h = rec.Bottom - rec.Top;
                    item.SubItems.Add(w.ToString());
                    item.SubItems.Add(h.ToString());
                    item.SubItems.Add(proc.MainModule.FileName);
                }
            }
            //FormにListViewを追加
            this.Controls.Add(listView);
        }

        //解説:OKボタン(「はい」)を押した際のエラーチェック、参照引数への書き込み処理
        private void btnOK_Click(object sender, EventArgs e)
        {
            if(listView.SelectedItems.Count == 0)
            {
                MessageBox.Show("リスト項目が未選択です。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            wi.wTitle = listView.SelectedItems[0].SubItems[0].Text;
            wi.wProc = listView.SelectedItems[0].SubItems[1].Text;
            ulong val;
            if(!ulong.TryParse(listView.SelectedItems[0].SubItems[2].Text, out val))
            {
                MessageBox.Show("ハンドルの値が不正です。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                wi.wHnd = 0;
            }
            else
                wi.wHnd = val;
            int len;
            if(!int.TryParse(listView.SelectedItems[0].SubItems[3].Text, out len))
            {
                MessageBox.Show("幅の値が不正です。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                wi.wWidth = 0;
            }
            wi.wWidth = len;
            if(!int.TryParse(listView.SelectedItems[0].SubItems[4].Text, out len))
            {
                MessageBox.Show("高さの値が不正です。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                wi.wHeight = 0;
            }
            wi.wHeight = len;
            wi.wPath = listView.SelectedItems[0].SubItems[5].Text;
            Close();
        }

        //解説:Cancelボタン(「いいえ」)を押した際の初期化処理
        private void btnCancel_Click(object sender, EventArgs e)
        {
            listView.SelectedItems.Clear();
            wi.wTitle = "";
            wi.wProc = "";
            wi.wHnd = 0;
            wi.wWidth = 0;
            wi.wHeight = 0;
            wi.wPath = "";
            Close();
        }
    }
}