[英]GetClientRect is not giving the correct Rectangle
I am trying to create an overlay form that overlays the contents of an external window (excluding the borders etc).我正在尝试创建一个覆盖外部 window 内容的覆盖表单(不包括边框等)。 I believe that
GetClientRect
is the correct winapi for that purpose however it does not seem to be working.我相信
GetClientRect
是用于此目的的正确 winapi 但它似乎不起作用。
I created an example where I load up a form as a black box and display it over an open Notepad.我创建了一个示例,我将表单加载为黑框并将其显示在打开的记事本上。
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WinFormsApp2
{
public partial class Form1 : Form
{
private Process? targetProc = null;
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetClientRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);
public static Rectangle GetWindowRectangle(IntPtr hWnd)
{
Point point = new Point();
GetClientRect(hWnd, out RECT rect);
ClientToScreen(hWnd, ref point);
return new Rectangle(point.X, point.Y, rect.Right, rect.Bottom);
}
private IntPtr notepadhWnd;
public Form1()
{
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Normal;
this.BackColor = Color.Black;
StartPosition = FormStartPosition.Manual;
targetProc = Process.GetProcessesByName("notepad").FirstOrDefault(p => p != null);
try
{
if (targetProc != null)
{
notepadhWnd = targetProc.MainWindowHandle;
if (notepadhWnd != IntPtr.Zero)
{
Rectangle rect = GetWindowRectangle(notepadhWnd);
Location = new Point(rect.Left, rect.Top);
Size = new Size(rect.Width, rect.Height);
}
}
}
catch (Exception ex)
{
// Log and message or
throw;
}
}
}
}
The output of this is:这个的output是:
I expected the output to be:我预计 output 是:
From all my searches I believe that the GetClientRect
API should only return the client area but not sure what I am missing.从我所有的搜索中,我认为
GetClientRect
API 应该只返回客户区,但不确定我遗漏了什么。
Grab coffee, this will be a long answer.喝杯咖啡,这将是一个很长的答案。
First, take a look at the image of notepad below.首先,看看下面记事本的图像。 From the wording of your question it sounds like you are expecting GetClientRect to return the area marked in red.
从您问题的措辞来看,您似乎希望 GetClientRect 返回标记为红色的区域。 GetClientRect returns the dimensions of the window handle you provide but without the borders or scroll bars.
GetClientRect 返回您提供的 window 句柄的尺寸,但没有边框或滚动条。 In other words, it will give you the interior area of the green rectangle.
换句话说,它将为您提供绿色矩形的内部区域。
There is no single "client window" - in the screenshot below the menu bar is a child window. So is the status bar at the bottom of the screen.没有单独的“客户端窗口”——在屏幕截图中,菜单栏下方是一个子项 window。屏幕底部的状态栏也是如此。 So is the editor space which seems to be what you are interested in.
您似乎感兴趣的编辑器空间也是如此。
So, how do you get the hWnd of the editor space?那么,如何获得编辑器空间的hWnd呢? I'm not aware of any answer to that question that doesn't rely on the dark magic.
我不知道有任何不依赖黑魔法的问题的答案。 It is all fraught with peril... do you iterate through all the child windows and pick the biggest?
这一切都充满了危险......你是否遍历所有孩子 windows 并选择最大的? Pick a point in the dead center of the window and use API calls to find the hWnd at that location?
在 window 的死点中选择一个点并使用 API 调用在该位置找到 hWnd? No matter what you do, it will not be an exact science.
无论你做什么,它都不是一门精确的科学。 For purposes of this question, though, I'll show one approach.
不过,出于这个问题的目的,我将展示一种方法。
Every window has a Class name associated with it.每个 window 都有一个与之关联的 Class 名称。 In the image below I'm using a tool called Spy++ to reveal information about the various windows. As you can see, the window that makes up the editor space has a class name of "RichEditD2DPT"
在下图中,我使用名为 Spy++ 的工具来揭示有关各种 windows 的信息。如您所见,构成编辑器空间的 window 的 class 名称为“RichEditD2DPT”
I want to stress again how brittle this solution is.我想再次强调这个解决方案是多么脆弱。 You could have more than one child window of the given class. The application developer could change to a different control with a different class name without warning.
您可以拥有给定 class 的多个子控件 window。应用程序开发人员可以在没有警告的情况下更改为具有不同名称 class 的不同控件。 Nevertheless, below is code to enumerate through the child windows until the desired class is found.
尽管如此,下面的代码通过子 windows 进行枚举,直到找到所需的 class。 After that, I'm simply passing that hWnd into the code you already had and it seems to work.
之后,我只是将该hWnd 传递到您已有的代码中,它似乎可以正常工作。
public partial class Form1 : Form
{
private readonly StringBuilder _buffer = new StringBuilder(1024);
private IntPtr _notepadMainhWnd;
private IntPtr _notepadEditorhWnd;
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetClientRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
private delegate bool WindowEnumProc(IntPtr hwnd, IntPtr lparam);
public static Rectangle GetWindowRectangle(IntPtr hWnd)
{
Point point = new Point();
GetClientRect(hWnd, out RECT rect);
bool ret = ClientToScreen(hWnd, ref point);
Debug.WriteLine($"{point.X},{point.Y}");
return new Rectangle(point.X, point.Y, rect.Right - rect.Left, rect.Bottom - rect.Top);
}
public Form1()
{
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Normal;
this.BackColor = Color.Black;
StartPosition = FormStartPosition.Manual;
var targetProcess = Process.GetProcessesByName("notepad").FirstOrDefault(p => p != null);
if (targetProcess != null)
{
// Get the main application window
_notepadMainhWnd = targetProcess.MainWindowHandle;
if (_notepadMainhWnd != IntPtr.Zero)
{
// Looks for child windows having the class name "RichEditD2DPT"
EnumChildWindows(_notepadMainhWnd, callback, IntPtr.Zero);
if (_notepadEditorhWnd != IntPtr.Zero)
{
Rectangle rect = GetWindowRectangle(_notepadEditorhWnd);
Location = new Point(rect.Left, rect.Top);
Size = new Size(rect.Width, rect.Height);
}
}
}
}
private bool callback(IntPtr hWnd, IntPtr lParam)
{
if (GetClassName(hWnd, _buffer, _buffer.Capacity) != 0)
{
if (string.CompareOrdinal(_buffer.ToString(), "RichEditD2DPT") == 0)
{
_notepadEditorhWnd = hWnd;
return false;
}
}
return true;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.