【问题标题】:Get form handle from point using pinvoke使用 pinvoke 从点获取表单句柄
【发布时间】:2016-03-04 10:17:34
【问题描述】:

我正在尝试使用 p/invoke 从点获取窗口句柄,其中窗口是一个表单,而不是任何子控件。我有一个简单的界面,用户输入 X 和 Y,然后使用 Find 按钮调用 win32 并获取必要的信息。我的问题是窗口不一定是窗体,它也可以是控件。请参见下面的屏幕截图 - (100,100) 恰好是记事本的文本区域,其中写有“StackOverflow”。结果,Found 窗口显示“StackOverflow”。

有什么方法可以将窗口类型限制为表单?以下测试用例的预期结果是“无标题 - 记事本”。或者,有没有办法让另一个应用程序的控件提供它的表单句柄?简而言之,我需要从 (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);
}

【问题讨论】:

  • 你能否使用 Control FromHandle(IntPtr handle) 从句柄中获取控件并检查控件的类型以查看它是否为表单?如果它不是表单,您可以使用控件的 Parent 属性并遍历树直到找到表单。
  • @LindosPechos:这似乎只在句柄属于我的应用程序时才有效。否则 Control.FromHandle 返回 null。
  • GetParent(hWnd) != null?找到顶层窗口

标签: c# winapi pinvoke


【解决方案1】:

您需要找到顶层窗口。从GetWindowHandleFromPoint 产生的窗口开始。然后反复调用GetParent,直到找到没有父窗口的窗口。没有父窗口的那个窗口是您要查找的顶级窗口。

【讨论】:

  • 谢谢,成功了!您还可以为未来的访问者添加一些代码。我找不到比嵌套 if 中重复反转条件的 while 循环更好的东西了——也许你可以建议一种更简洁的方法(没有递归)。 pastebin.com/i2Vea4FS
  • 你的代码没问题。我可能会提前检查 FindWindow 是否为 null 并提前保释,然后使用 while (true)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-22
  • 2012-07-23
  • 1970-01-01
  • 1970-01-01
  • 2012-12-04
相关资源
最近更新 更多