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

时间:2017-01-02 15:08:01

标签: c# wpf pinvoke caret

我试图使用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)窗口中,GetWindowRect()决定包含标题栏并将标题栏顶部位置偏移标题栏的高度:

知道为什么吗?

我也尝试使用DwmGetWindowAttribute(),但它获得了与GetWindowRect()相同的WPF窗口坐标。

编辑:

根据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,0是由guiInfo.hwndFocus指定的窗口的客户区域的左上角坐标,它总是0,0,因为它相对于窗口&# 39;客户区。 ClientToScreen()会将坐标转换为相对于屏幕(必须为System.Drawing.PointSystem.Windows.Point无法工作)。

1 个答案:

答案 0 :(得分:4)

包含标题栏是因为它是窗口的一部分,如果你不想要非客户区,那么你需要请求客户区rect(GetClientRect)。

记事本的混乱可能是因为您使用的是文本框的窗口句柄而不是窗口本身。请记住,WPF对整个窗口使用单个句柄,而win32通常(但不总是)为窗口中的每个控件使用单独的句柄。

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