本文将介绍如何通过鼠标所在坐标位置获取Windows窗口中的控件句柄,包括:窗口、窗口里的控件(如:按钮、文本框等等),同时显示控件的名称标题(如果是文本框将显示文本框的内容,包括密码框可显示密码)。本人是用C#.NET编写代码,由于用的是Windows API函数,其它编程语言也可以参考使用。
实现原理:
1.通过API函数GetCursorPos获取鼠标所在的屏幕坐标,我用的是.NET里面的System.Windows.Forms.Cursor.Position,效果是一样的;
2.通过API函数WindowFromPoint获取坐标位置的控件句柄(如果是非活动的灰色控件甚至隐藏控件,可通过ChildWindowFromPoint获取);
3.通过API函数SendMessage向控件句柄发送WM_GETTEXT消息获取控件的文本信息,如果用GetWindowText也可以,但不能获取文本框的内容。
源代码如下:(点击下载完整代码示例)
public partial class Form1 : Form
{
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, StringBuilder lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
/// <summary>
/// 通过全屏幕坐标获取控件句柄,不获取隐藏或禁止的窗口句柄
/// </summary>
/// <param name="p">屏幕坐标</param>
/// <returns>返回值为包含该点的窗口的句柄。如果包含指定点的窗口不存在,返回值为NULL。如果该点在静态文本控件之上,返回值是在该静态文本控件的下面的窗口的句柄。</returns>
[DllImport("user32.dll")]
public static extern IntPtr WindowFromPoint(Point p);
/// <summary>
/// 通过相对父控件的坐标获取子控件句柄,可获取隐藏或禁止的窗口句柄
/// </summary>
/// <param name="hWndParent">父控件句柄</param>
/// <param name="p">相对父控件的坐标</param>
/// <returns>如果坐标在父控件以外返回NULL,如果坐标在父控件以内但坐标位置没有子控件则返回父控件本身句柄,如果坐标位置有子控件返回子控件的句柄</returns>
[DllImport("user32.dll")]
public static extern IntPtr ChildWindowFromPoint(IntPtr hWndParent, Point p);
/// <summary>
/// 将屏幕坐标转换为控件的相对坐标
/// </summary>
/// <param name="hWnd">控件句柄</param>
/// <param name="lpPoint">引用坐标参数:使用前用屏幕坐标赋值,转换成功则变成控件相对坐标</param>
/// <returns>是否成功</returns>
[DllImport("user32.dll")]
public static extern bool ScreenToClient(IntPtr hWnd, ref Point lpPoint);
//应用程序发送此消息来复制对应窗口的文本到缓冲区
public static int WM_GETTEXT = 0x0D;
//得到与一个窗口有关的文本的长度(不包含空字符)
public static int WM_GETTEXTLENGTH = 0x0E;
bool isRuning = false;
IntPtr lastHwnd = IntPtr.Zero;
System.Threading.AutoResetEvent resetEvent = new System.Threading.AutoResetEvent(true);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
changeState();
}
private void changeState()
{
if (isRuning)
{
isRuning = false;
this.button1.Text = "开始捕捉(Space/Enter)";
}
else
{
isRuning = true;
this.button1.Text = "停止捕捉(Space/Enter)";
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.ProgressChanged += Bw_ProgressChanged;
bw.WorkerReportsProgress = true;
bw.RunWorkerAsync();
}
}
private void Bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
textBox1.Text = string.Format("坐标:X={0},Y={1}", Cursor.Position.X, Cursor.Position.Y);
if (e.ProgressPercentage == 1)
{
IntPtr hwnd = (IntPtr)e.UserState;
textBox2.Text = hwnd.ToString("X8");
int len = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);
StringBuilder sb = new StringBuilder(len + 1);
SendMessage(hwnd, WM_GETTEXT, sb.Capacity, sb);
this.textBox3.Text = sb.ToString();
resetEvent.Set();
}
}
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker me = sender as BackgroundWorker;
while (isRuning)
{
Point p=Cursor.Position;
IntPtr curHwnd = WindowFromPoint(p);//获取当前坐标位置的控件句柄(不能获取Disabled未激活的控件)
ScreenToClient(curHwnd,ref p);//将屏幕坐标转换为窗口客户区坐标
if(p.X>=0&&p.Y>=0)//确保坐标在客户区内,如果在客户区外无法找到子控件
curHwnd = WindowsAPI.API.ChildWindowFromPoint(curHwnd, p);//从当前坐标查找子控件(可获取Disabled未激活的控件).如果没有则返回自己
if(curHwnd!=lastHwnd)
{
resetEvent.WaitOne();
me.ReportProgress(1, curHwnd);
lastHwnd = curHwnd;
}
else
me.ReportProgress(2);
System.Threading.Thread.Sleep(200);
}
}
}