繁体   English   中英

GetClientRect 没有提供正确的矩形

[英]GetClientRect is not giving the correct Rectangle

我正在尝试创建一个覆盖外部 window 内容的覆盖表单(不包括边框等)。 我相信GetClientRect是用于此目的的正确 winapi 但它似乎不起作用。

我创建了一个示例,我将表单加载为黑框并将其显示在打开的记事本上。

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

    }
}

这个的output是: 记事本完全被表格覆盖

我预计 output 是: 只有记事本的内容被表格覆盖

从我所有的搜索中,我认为GetClientRect API 应该只返回客户区,但不确定我遗漏了什么。

喝杯咖啡,这将是一个很长的答案。

首先,看看下面记事本的图像。 从您问题的措辞来看,您似乎希望 GetClientRect 返回标记为红色的区域。 GetClientRect 返回您提供的 window 句柄的尺寸,没有边框或滚动条。 换句话说,它将为您提供绿色矩形的内部区域。

带标记的记事本

没有单独的“客户端窗口”——在屏幕截图中,菜单栏下方是一个子项 window。屏幕底部的状态栏也是如此。 您似乎感兴趣的编辑器空间也是如此。

那么,如何获得编辑器空间的hWnd呢? 我不知道有任何不依赖黑魔法的问题的答案。 这一切都充满了危险......你是否遍历所有孩子 windows 并选择最大的? 在 window 的死点中选择一个点并使用 API 调用在该位置找到 hWnd? 无论你做什么,它都不是一门精确的科学。 不过,出于这个问题的目的,我将展示一种方法。

每个 window 都有一个与之关联的 Class 名称。 在下图中,我使用名为 Spy++ 的工具来揭示有关各种 windows 的信息。如您所见,构成编辑器空间的 window 的 class 名称为“RichEditD2DPT”

在此处输入图像描述

我想再次强调这个解决方案是多么脆弱。 您可以拥有给定 class 的多个子控件 window。应用程序开发人员可以在没有警告的情况下更改为具有不同名称 class 的不同控件。 尽管如此,下面的代码通过子 windows 进行枚举,直到找到所需的 class。 之后,我只是将该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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM