WinAPIを使ってWindowの情報を取得して一覧表示します。
開発に役立てれればと思っておいておきます。
解説なし&てきとうコーディングなので、あしからず。
このアプリでは影響するか分かりませんが
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; //すべてのウィンドウを列挙する
}
}