Victor Zakharov
Victor Zakharov

Reputation: 26424

Get form handle from point using pinvoke

I'm trying to get a window handle from point using p/invoke, where window is a form, and not any child control. I have a simple interface where X and Y are entered by user, and then Find button is used to call win32 and get necessary information. My problem is that window is not necessarily a form, it can also be a control. See below screenshot - at (100,100) happened to be Notepad's text area with "StackOverflow" written in it. As a result, Found window shows "StackOverflow".

enter image description here

Is there any way I can restrict window type to be a Form? Expected result is "Untitled - Notepad" for below test case. Alternatively, is there a way to ask another application's control to provide its form's handle? In short, I need to get form's title from (x,y) point. Button click handler code:

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);
}

Major portion of Win32 class comes from this answer:

Full Win32 class code is provided below:

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);
}

Upvotes: 1

Views: 2274

Answers (1)

David Heffernan
David Heffernan

Reputation: 612993

You need to locate the top level window. Start from the window that GetWindowHandleFromPoint yielded. Then call GetParent repeatedly until you find a window with no parent. That window with no parent is the top level window that you are looking for.

Upvotes: 1

Related Questions