2017-01-02 11 views
1

Я пытаюсь получить каре позицию, используя GetWindowRect()GetGUIThreadInfo()):Почему GetWindowRect включает строку заголовка в моем окне WPF?

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().

Edit:

С ответом от 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 не будет работать).

+0

Строка заголовка включена, потому что ее часть окна, если вы не хотите неклиентскую область, тогда вам нужно запросить область клиента rect (GetClientRect). Путаница из блокнота, вероятно, связана с тем, что вы используете дескриптор окна текстового поля, а не самого окна. Помните, что WPF использует единый дескриптор для общего окна, в то время как win32 часто (но не всегда) использует отдельный дескриптор для каждого элемента управления в окне. –

+0

'GetClientRect()' возвращает 0,0, но я думаю, что вы правильно относитесь к дескрипторам окон - по крайней мере, я могу подтвердить с помощью Spy ++, поэтому, если вы хотите отказаться от этого в качестве ответа, я могу его принять. – Woodgnome

ответ

4

Строка заголовка включена, поскольку ее часть окна, если вы не хотите область без клиента, тогда вам нужно запросить зону клиента rect (GetClientRect).

Путаница из блокнота, вероятно, из-за того, что вы используете дескриптор окна текстового поля, а не самого окна. Помните, WPF использует единый дескриптор для общего окна, в то время как win32 часто (но не всегда) использует отдельный дескриптор для каждого элемента управления в окне.

В комментарии вы упомянули, что GetClientRect «возвращено» 0,0, вы проверили, вернули ли он истину (успех) или ложь (отказ)? Если он вернул false, вы проверили результат GetLastError()?

+0

Очевидно, что 'GetClientRect()' работает по назначению - это относительно клиентской области клиентской области окна, поэтому 'left' /' top' всегда '0,0'. Правильной функцией является 'ClientToScreen()' (я обновил свой вопрос, чтобы показать, как). – Woodgnome

+0

Это правильно. Верхняя левая точка прямоугольника клиента всегда находится в (0, 0). 'ClientToScreen()' будет работать или использовать более общие «MapWindowPoints». Однако, @woodgnome, в вашем вопросе говорится, что вы хотите «позицию каретки».Этот код даст вам координаты экрана клиентской области окна, но каретка может не находиться в верхнем левом углу. –

+0

Я использую 'GUITHREADINFO.rcCaret', чтобы компенсировать позицию каретки -' GetClientRect() 'просто вызвало у меня некоторые проблемы, и вместо этого я заменил его на« ClientToScreen() ». – Woodgnome