使用 pinvoke 从点获取表单句柄
Get form handle from point using pinvoke
我正在尝试使用 p/invoke 从点获取 window 句柄,其中 window 是一个表单,而不是任何 child 控件。我有一个简单的界面,其中 X 和 Y 由用户输入,然后 Find 按钮用于调用 win32 并获取必要的信息。我的问题是window不一定是窗体,也可以是控件。请参见下面的屏幕截图 - 在 (100,100) 处恰好是记事本的文本区域,其中写有 "Whosebug"。结果,Found window 显示 "Whosebug".
有什么方法可以将 window 类型限制为表单?以下测试用例的预期结果为 "Untitled - Notepad"。或者,有没有办法要求另一个应用程序的控件提供其窗体句柄?简而言之,我需要从 (x,y) 点获取表单的标题。按钮单击处理程序代码:
private void btn_Find_Click(object sender, EventArgs e)
{
int xPoint = Convert.ToInt32(txt_WindowX.Text);
int yPoint = Convert.ToInt32(txt_WindowY.Text);
IntPtr hWnd = Win32.GetWindowHandleFromPoint(xPoint, yPoint);
txt_FormTitle.Text = Win32.GetWindowTitle(hWnd);
}
Win32 的主要部分 class 来自这个答案:
- Tergiver's answer to "C# - unable to read another application's caption"
完整的 Win32 class 代码如下:
public class Win32
{
/// <summary>
///
/// </summary>
/// <param name="hwnd"></param>
/// <remarks>
public static string GetWindowTitle(IntPtr hwnd)
{
if (hwnd == IntPtr.Zero)
throw new ArgumentNullException("hwnd");
int length = Win32.SendMessageGetTextLength(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
if (length > 0 && length < int.MaxValue)
{
length++; // room for EOS terminator
StringBuilder sb = new StringBuilder(length);
Win32.SendMessageGetText(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
return String.Empty;
}
public static IntPtr GetWindowHandleFromPoint(int x, int y)
{
var point = new Point(x, y);
return Win32.WindowFromPoint(point);
}
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(Point p);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessageGetTextLength(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessageGetText(IntPtr hWnd, int msg, IntPtr wParam, [Out] StringBuilder lParam);
}
您需要找到顶层 window。从 GetWindowHandleFromPoint
产生的 window 开始。然后重复调用 GetParent
,直到找到一个没有父节点的 window。没有父级的 window 就是您正在寻找的顶级 window。
我正在尝试使用 p/invoke 从点获取 window 句柄,其中 window 是一个表单,而不是任何 child 控件。我有一个简单的界面,其中 X 和 Y 由用户输入,然后 Find 按钮用于调用 win32 并获取必要的信息。我的问题是window不一定是窗体,也可以是控件。请参见下面的屏幕截图 - 在 (100,100) 处恰好是记事本的文本区域,其中写有 "Whosebug"。结果,Found window 显示 "Whosebug".
有什么方法可以将 window 类型限制为表单?以下测试用例的预期结果为 "Untitled - Notepad"。或者,有没有办法要求另一个应用程序的控件提供其窗体句柄?简而言之,我需要从 (x,y) 点获取表单的标题。按钮单击处理程序代码:
private void btn_Find_Click(object sender, EventArgs e)
{
int xPoint = Convert.ToInt32(txt_WindowX.Text);
int yPoint = Convert.ToInt32(txt_WindowY.Text);
IntPtr hWnd = Win32.GetWindowHandleFromPoint(xPoint, yPoint);
txt_FormTitle.Text = Win32.GetWindowTitle(hWnd);
}
Win32 的主要部分 class 来自这个答案:
- Tergiver's answer to "C# - unable to read another application's caption"
完整的 Win32 class 代码如下:
public class Win32
{
/// <summary>
///
/// </summary>
/// <param name="hwnd"></param>
/// <remarks>
public static string GetWindowTitle(IntPtr hwnd)
{
if (hwnd == IntPtr.Zero)
throw new ArgumentNullException("hwnd");
int length = Win32.SendMessageGetTextLength(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
if (length > 0 && length < int.MaxValue)
{
length++; // room for EOS terminator
StringBuilder sb = new StringBuilder(length);
Win32.SendMessageGetText(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
return String.Empty;
}
public static IntPtr GetWindowHandleFromPoint(int x, int y)
{
var point = new Point(x, y);
return Win32.WindowFromPoint(point);
}
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(Point p);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessageGetTextLength(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessageGetText(IntPtr hWnd, int msg, IntPtr wParam, [Out] StringBuilder lParam);
}
您需要找到顶层 window。从 GetWindowHandleFromPoint
产生的 window 开始。然后重复调用 GetParent
,直到找到一个没有父节点的 window。没有父级的 window 就是您正在寻找的顶级 window。