【发布时间】:2017-05-16 16:44:43
【问题描述】:
我正在尝试使用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(),但它在 WPF 窗口中的坐标与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,0 是guiInfo.hwndFocus 指定的窗口客户区的左上角坐标,它始终是0,0,因为它是相对于窗口的客户区的。 ClientToScreen() 将坐标转换为相对于屏幕的坐标(必须为 System.Drawing.Point,System.Windows.Point 将不起作用)。
【问题讨论】:
-
包含标题栏是因为它是窗口的一部分,如果您不想要非客户区,那么您需要请求客户区矩形(GetClientRect)。记事本的混乱可能是因为您使用的是文本框的窗口句柄而不是窗口本身。请记住 WPF 对整个窗口使用单个句柄,而 win32 经常(但不总是)为窗口中的每个控件使用单独的句柄。
-
GetClientRect()返回 0,0,但我认为您对窗口句柄是正确的 - 我至少可以用 Spy++ 确认,所以如果您想放弃它作为答案,我可以接受.