10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C#でWindow情報を取得して一覧表にするツール作ってみた

Last updated at Posted at 2019-08-21

WinAPIを使ってWindowの情報を取得して一覧表示します。
開発に役立てれればと思っておいておきます。
解説なし&てきとうコーディングなので、あしからず。

>>機能追加版はこちら<<

こんな感じ↓(部分的にペイントで隠してます)
無題.png

このアプリでは影響するか分かりませんが
STAThreadつけ忘れてたので追加しました。2019.08.22

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;


public class TopLevelWindowInfo
{
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int GetWindowTextLength(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
    
    [DllImport("user32.dll",SetLastError = true)]
    private static extern int GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);
    
    [StructLayout(LayoutKind.Sequential)]
    public struct WINDOWINFO
    {
        public int   cbSize;
        public RECT  rcWindow;
        public RECT  rcClient;
        public int   dwStyle;
        public int   dwExStyle;
        public int   dwWindowStatus;
        public uint  cxWindowBorders;
        public uint  cyWindowBorders;
        public short atomWindowType;
        public short wCreatorVersion;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int left;
        public int top;
        public int right;  // external area. source:  https://docs.microsoft.com/en-us/previous-versions/dd162897(v%3Dvs.85)
        public int bottom; // 
        public int width{get{return right-left;}}
        public int height{get{return bottom-top;}}
    }

    [DllImport("user32.dll", SetLastError = true)]
    public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
    
    
    public const int MaxLength = 500;

    IntPtr _hWnd;
    string _className;
    string _windowText;
    WINDOWINFO _wi;
    int _pid;
    
    public IntPtr hWnd        {get{return _hWnd;}}
    public string className   {get{return _className;}}
    public string windowText  {get{return _windowText;}}
    public int    windowStatus{get{return _wi.dwWindowStatus;}}
    public int    left        {get{return _wi.rcWindow.left;}}
    public int    top         {get{return _wi.rcWindow.top;}}
    public int    width       {get{return _wi.rcWindow.width;}}
    public int    height      {get{return _wi.rcWindow.height;}}
    
    public int    clientLeft  {get{return _wi.rcClient.left;}}
    public int    clientTop   {get{return _wi.rcClient.top;}}
    public int    clientWidth {get{return _wi.rcClient.width;}}
    public int    clientHeight{get{return _wi.rcClient.height;}}

    public int    windowStyle {get{return _wi.dwStyle;}}
    public int    pid         {get{return _pid;}}
    
    public TopLevelWindowInfo(IntPtr arg_hWnd)
    {
        int retCode;
        
        _hWnd = arg_hWnd;
        
        //ウィンドウのクラス名を取得する
        StringBuilder csb = new StringBuilder(MaxLength);
        retCode = GetClassName(hWnd, csb, csb.Capacity);
        
        _className = csb.ToString();
        
        //ウィンドウのタイトルを取得する
        StringBuilder tsb = new StringBuilder(MaxLength);
        retCode = GetWindowText(hWnd, tsb, tsb.Capacity);
        
        if ( retCode > 0 ) {
            _windowText = tsb.ToString();
        }
        else {
            _windowText = "";
        }
        
        _wi = new WINDOWINFO();
        _wi.cbSize = Marshal.SizeOf(_wi);  // sizeof(WINDOWINFO);でもよいようだが sizeof()を使う場合は unsafe{}が必要
        retCode = GetWindowInfo(_hWnd, ref _wi);
        if ( retCode == 0 ) {
            Console.WriteLine("GetWindowInfo returns 0.");
        }
        
        GetWindowThreadProcessId(_hWnd, out _pid);
    }
    
    
    const int WS_VISIBLE = 0x10000000;
    const int WS_ICONIC  = 0x20000000;
    
    public bool IsVisibleWindow()
    {
        if ((_wi.dwStyle & WS_VISIBLE) == 0) return false;
        if ((_wi.dwStyle & WS_ICONIC) == WS_ICONIC) return false;
        if ( width <= 0 || height <= 0 ) return false;
        
        return true;
    }
}


public class MainForm:Form
{
    [DllImport("user32.dll",SetLastError = true)]
    private static extern IntPtr WindowFromPoint(POINT point);
    
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int x;
        public int y;
    }
    
    [DllImport("user32.dll",SetLastError = true)]
    private static extern IntPtr GetAncestor(IntPtr hWnd, uint gaFlags);
    const uint GA_PARENT    = 1;
    const uint GA_ROOT      = 2;
    const uint GA_ROOTOWNER = 3; // 複数windowを持つ場合は、そのownerが返る
    
    
    public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc,   IntPtr lparam);

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }
    
    
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetCursorPos(out POINT lpPoint);

    public enum LsvSortAttr {
        ByNumeric,
        ByHex,
        ByString
    };
    
    
    System.Windows.Forms.Timer timer;
    Button   btnRefresh;
    Button   btnPeriodicRefresh;
    CheckBox chkShowInvisibleWindow;
    ListView lsv;
    List<TopLevelWindowInfo> wndInfos;
    List<LsvSortAttr> SortOrderIdent;
    

    MainForm()
    {
        wndInfos = new List<TopLevelWindowInfo>();
        
        Text = "Listup Window Handles";
        SortOrderIdent = new List<LsvSortAttr>();
        ClientSize = new System.Drawing.Size(700,530);
        
        Shown += (sender,e)=>{EnumWndUpdateList();};
        Resize    += (sender,e)=>{MyResizeHandler();};
        ResizeEnd += (sender,e)=>{MyResizeHandler();};
        
        btnRefresh = new Button();
        btnRefresh.Text = "Refresh";
        btnRefresh.Click += (sender,e)=>{EnumWndUpdateList();};
        Controls.Add(btnRefresh);
        
        btnPeriodicRefresh = new Button();
        btnPeriodicRefresh.Left = 100;
        btnPeriodicRefresh.Text = "Auto";
        btnPeriodicRefresh.Click += (sender,e)=>
        {
            if ( timer==null || !timer.Enabled ) {
                btnPeriodicRefresh.Text = "Stop";
                StartTimer();
            }
            else {
                btnPeriodicRefresh.Text = "Auto";
                StopTimer();
            }
        };
        Controls.Add(btnPeriodicRefresh);
        
        
        chkShowInvisibleWindow = new CheckBox();
        chkShowInvisibleWindow.Left = 200;
        chkShowInvisibleWindow.Text = "ShowInvisible";
        chkShowInvisibleWindow.Click += (sender,e)=>{UpdateListControl();}; // 取得済みデータをフィルタするのは面倒なので 再取得
        Controls.Add(chkShowInvisibleWindow);
        
        
        lsv = new ListView();
        lsv.Location = new Point(0,30);
        lsv.Size = new System.Drawing.Size(700,500);
        lsv.View = View.Details;
        lsv.FullRowSelect = true;
        lsv.GridLines = true;
        lsv.Columns.Add("No"         , 35, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByNumeric);
        lsv.Columns.Add("HWND"       , 70, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByHex);
        lsv.Columns.Add("PID"        , 50, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByNumeric);
        lsv.Columns.Add("ClassName"  ,150, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByString);
        lsv.Columns.Add("WindowText" ,150, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByString);
        lsv.Columns.Add("Left"       , 45, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByNumeric);
        lsv.Columns.Add("Top"        , 45, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByNumeric);
        lsv.Columns.Add("Width"      , 45, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByNumeric);
        lsv.Columns.Add("Height"     , 45, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByNumeric);
        lsv.Columns.Add("cLeft"      , 45, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByNumeric);
        lsv.Columns.Add("cTop"       , 45, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByNumeric);
        lsv.Columns.Add("cWidth"     , 45, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByNumeric);
        lsv.Columns.Add("cHeight"    , 45, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByNumeric);
        lsv.Columns.Add("Style"      , 70, HorizontalAlignment.Left);SortOrderIdent.Add(LsvSortAttr.ByHex);
        lsv.ColumnClick += (sender,e)=>{Lsv_ColumnClick(e);};
        Controls.Add(lsv);
    }
    
    void MyResizeHandler()
    {
        lsv.Size = new System.Drawing.Size(ClientSize.Width, ClientSize.Height - lsv.Top);
    }
    
    void Lsv_ColumnClick(ColumnClickEventArgs e)
    {
        if ( e.Column >= 0 && e.Column < lsv.Columns.Count ) {
            lsv.ListViewItemSorter = new ListViewItemComparer(e.Column, SortOrderIdent[e.Column]);
        }
        else {
            Console.WriteLine("Out of column index");
        }
    }
    
    public class ListViewItemComparer : IComparer
    {
        private int _column;
        private LsvSortAttr _sortAttr;

        public ListViewItemComparer(int col, LsvSortAttr sortAttr)
        {
            _column   = col;
            _sortAttr = sortAttr;
        }
        
        private int CompareInt64(Int64 a, Int64 b)
        {
            if(a<b){return -1;}
            if(a>b){return  1;}
            return 0;
        }

        public int Compare(object obj1, object obj2)
        {
            string s1 = ((ListViewItem)obj1).SubItems[_column].Text;
            string s2 = ((ListViewItem)obj2).SubItems[_column].Text;
            
            if ( _sortAttr == LsvSortAttr.ByNumeric ) {
                Int64 n1;
                Int64 n2;
                try {
                    n1 = Convert.ToInt64(s1);
                    n2 = Convert.ToInt64(s2);
                    return CompareInt64(n1, n2);
                }
                catch(Exception e){Console.WriteLine(e);} // catchしたら文字列比較へ
            }
            else if ( _sortAttr == LsvSortAttr.ByHex ) {
                // 未実装.. とりあえず文字列比較
            }
            return string.Compare(s1, s2);
        }
    }
    
    public ListViewItem ConvertToListViewItem(int index, TopLevelWindowInfo a)
    {
        var ss = new string[]
        {
            index.ToString(),
            a.hWnd.ToString("X8"),
            a.pid.ToString(),
            a.className,
            a.windowText,
            a.left.ToString(),
            a.top.ToString(),
            a.width.ToString(),
            a.height.ToString(),
            a.clientLeft.ToString(),
            a.clientTop.ToString(),
            a.clientWidth.ToString(),
            a.clientHeight.ToString(),
            a.windowStyle.ToString("X8")
        };
        
        var t = new ListViewItem(ss);
        t.Tag = a;
        return t;
    }

    void StartTimer()
    {
        timer = new System.Windows.Forms.Timer();
        timer.Interval = 2000;
        timer.Tick += (sender,e)=>{EnumWndUpdateList();};
        timer.Start();
    }
    
    void StopTimer()
    {
        if ( timer != null ) {
            timer.Stop();
        }
    }

    void EnumWndUpdateList()
    {
        wndInfos = new List<TopLevelWindowInfo>();
        
        //ウィンドウを列挙する
        EnumWindows(EnumWindowCallBack, IntPtr.Zero);
        
        UpdateListControl();
        
        POINT p = new POINT();
        GetCursorPos(out p);
        IntPtr hWnd = WindowFromPoint( p );
        IntPtr hWndRoot = IntPtr.Zero;
        
        if ( hWnd != IntPtr.Zero ) {
            hWndRoot = GetAncestor(hWnd, GA_ROOT);
        }
        
        Console.Write("HWND on cursor = 0x");
        Console.WriteLine(((int)hWndRoot).ToString("X8"));
    }
    
    
    void UpdateListControl()
    {
        bool showInvisWnd = chkShowInvisibleWindow.Checked;
        
        lsv.Items.Clear();
        lsv.BeginUpdate();
        try {
            int itemNo=0;
            foreach ( var t in wndInfos ) {
                itemNo++;
                if ( showInvisWnd || t.IsVisibleWindow() ) {
                    lsv.Items.Add(ConvertToListViewItem(itemNo, t));
                }
            }
        }
        finally {
            lsv.EndUpdate();
        }
    }
    
    /// <summary>
    /// エントリポイント
    /// </summary>
    [STAThread]
    public static void Main(string[] args)
    {
        Application.Run(new MainForm());
    }

    private bool EnumWindowCallBack(IntPtr hWnd, IntPtr lparam)
    {
        wndInfos.Add(new TopLevelWindowInfo(hWnd));
        return true;        //すべてのウィンドウを列挙する
    }
}


10
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?