为什么 GetWindowRect 在我的 WPF window 中包含标题栏?

Why does GetWindowRect include the title bar in my WPF window?

我正在尝试使用 GetWindowRect()(和 GetGUIThreadInfo())获得插入符位置:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Runtime.InteropServices;

namespace WpfApplication1
{
    public class CaretInfo
    {
        public double Width { get; private set; }
        public double Height { get; private set; }
        public double Left { get; private set; }
        public double Top { get; private set; }

        public CaretInfo(double width, double height, double left, double top)
        {
            Width = width;
            Height = height;
            Left = left;
            Top = top;
        }
    }

    public class CaretHelper
    {

        public static CaretInfo GetCaretPosition()
        {
            // Get GUI info containing caret poisition
            var guiInfo = new GUITHREADINFO();
            guiInfo.cbSize = (uint)Marshal.SizeOf(guiInfo);
            GetGUIThreadInfo(0, out guiInfo);

            // Get width/height
            double width = guiInfo.rcCaret.right - guiInfo.rcCaret.left;
            double height = guiInfo.rcCaret.bottom - guiInfo.rcCaret.top;

            // Get left/top relative to screen
            RECT rect;
            GetWindowRect(guiInfo.hwndFocus, out rect);

            double left = guiInfo.rcCaret.left + rect.left + 2;
            double top = guiInfo.rcCaret.top + rect.top + 2;


            int capacity = GetWindowTextLength(guiInfo.hwndFocus) * 2;
            StringBuilder stringBuilder = new StringBuilder(capacity);
            GetWindowText(guiInfo.hwndFocus, stringBuilder, stringBuilder.Capacity);
            Console.WriteLine("Window: " + stringBuilder);
            Console.WriteLine("Caret: " + guiInfo.rcCaret.left + ", " + guiInfo.rcCaret.top);
            Console.WriteLine("Rect : " + rect.left + ", " + rect.top);

            return new CaretInfo(width, height, left, top);
        }

        [DllImport("user32.dll", EntryPoint = "GetGUIThreadInfo")]
        public static extern bool GetGUIThreadInfo(uint tId, out GUITHREADINFO threadInfo);

        [DllImport("user32.dll")]
        public static extern bool ClientToScreen(IntPtr hWnd, out Point position);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetWindowRect(IntPtr handle, out RECT lpRect);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetClientRect(IntPtr hWnd, ref Rect rect);

        [StructLayout(LayoutKind.Sequential)]
        public struct GUITHREADINFO
        {
            public uint cbSize;
            public uint flags;
            public IntPtr hwndActive;
            public IntPtr hwndFocus;
            public IntPtr hwndCapture;
            public IntPtr hwndMenuOwner;
            public IntPtr hwndMoveSize;
            public IntPtr hwndCaret;
            public RECT rcCaret;
        };

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int GetWindowTextLe

对于记事本和几乎任何其他地方都可以正确获取坐标:

在我的 WPF(和任何其他 WPF)中 window,但是,GetWindowRect() 决定包含标题栏并将插入符号顶部位置偏移标题栏的高度:

知道为什么吗?

我也尝试使用 DwmGetWindowAttribute(),但它获得的 WPF window 坐标与 GetWindowRect().

相同

编辑:

有了 Brian Reichle 的回答,我已经能够确定一种获取客户区坐标的方法:

[DllImport("user32.dll")]
public static extern bool ClientToScreen(IntPtr hWnd, ref System.Drawing.Point lpPoint);

System.Drawing.Point point = new System.Drawing.Point(0, 0);
ClientToScreen(guiInfo.hwndFocus, ref point)

0,0guiInfo.hwndFocus指定的window客户区的左上角坐标,总是0,0,因为它是相对于window的客户区。 ClientToScreen() 将坐标转换为相对于屏幕的坐标(必须是 System.Drawing.PointSystem.Windows.Point 将不起作用)。

包含标题栏是因为它是 window 的一部分,如果您不想要 non-client 区域,则需要请求客户区域矩形 (GetClientRect) .

记事本造成的混淆可能是因为您使用的是文本框的 window 句柄而不是 window 本身。请记住,WPF 对整个 window 使用单个句柄,而 win32 通常(但不总是)对 window.

中的每个控件使用单独的句柄

在您提到 GetClientRect 'returned' 0,0 的评论中,您是否检查过它返回的是 true(成功)还是 false(失败)?如果返回false,你检查GetLastError()的结果了吗?