C# 使用pinvoke从点获取表单句柄

C# 使用pinvoke从点获取表单句柄,c#,winapi,pinvoke,C#,Winapi,Pinvoke,我试图使用p/invoke从点获取窗口句柄,其中窗口是一个窗体,而不是任何子控件。我有一个简单的界面,其中X和Y由用户输入,然后使用“查找”按钮调用win32并获取必要的信息。我的问题是窗口不一定是一个窗体,它也可以是一个控件。请看下面的屏幕截图-在(100100)处碰巧是记事本的文本区域,上面写着“StackOverflow”。结果,发现窗口显示“StackOverflow” 有没有办法限制窗口类型为窗体?下面的测试用例的预期结果是“Untitled-Notepad”。或者,是否有方法要求另

我试图使用p/invoke从点获取窗口句柄,其中窗口是一个窗体,而不是任何子控件。我有一个简单的界面,其中X和Y由用户输入,然后使用“查找”按钮调用win32并获取必要的信息。我的问题是窗口不一定是一个窗体,它也可以是一个控件。请看下面的屏幕截图-在(100100)处碰巧是记事本的文本区域,上面写着“StackOverflow”。结果,发现窗口显示“StackOverflow”

有没有办法限制窗口类型为窗体?下面的测试用例的预期结果是“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类的主要部分来自以下答案:

下面提供了完整的Win32类代码:

public class Win32
{
  /// <summary>
  /// 
  /// </summary>
  /// <param name="hwnd"></param>
  /// <remarks>https://stackoverflow.com/questions/4604023/unable-to-read-another-applications-caption</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);
}
公共类Win32
{
/// 
/// 
/// 
/// 
/// https://stackoverflow.com/questions/4604023/unable-to-read-another-applications-caption
公共静态字符串GetWindowTitle(IntPtr hwnd)
{
if(hwnd==IntPtr.Zero)
抛出新的ArgumentNullException(“hwnd”);
int length=Win32.SendMessageGetTextLength(hwnd,WM_GETTEXTLENGTH,IntPtr.Zero,IntPtr.Zero);
if(长度>0&&length
您需要找到顶层窗口。从
GetWindowHandleFromPoint
生成的窗口开始。然后反复调用
GetParent
,直到找到一个没有父项的窗口。没有父窗口的窗口是您要查找的顶级窗口

您能否使用controlfromHandle(IntPtr handle)从句柄获取控件,并检查控件的类型以查看它是否是表单?如果它不是表单,您可以使用控件的父属性并在树中迭代,直到找到表单为止。@LindosPechos:只有当句柄属于我的应用程序时,这似乎才有效。否则Control.FromHandle返回null.GetParent(hWnd)!=无效的找到顶级窗口谢谢,它成功了!您还可以为将来的访问者添加一些代码。在嵌套的if中,我找不到比while循环更好的方法了,while循环具有重复的反转条件——也许你可以建议一种更干净的方法(没有递归)。你的代码很好。我可能会检查FindWindow是否为null,并提前退出,然后使用while(true)