2017-01-02 5 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()と同じです。

編集:私はクライアント領域の座標を取得する方法を決定することができました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.PointSystem.Windows.Pointは動作しません)。

+0

タイトルバーが含まれているのは、ウィンドウの一部です。クライアント以外の領域を必要としない場合は、クライアント領域rect(GetClientRect)を要求する必要があります。メモ帳の混乱はおそらく、ウィンドウ自体ではなくテキストボックスのウィンドウハンドルを使用しているためです。 WPFはウィンドウ全体に単一のハンドルを使用するのに対し、win32はしばしばウィンドウ内の各コントロールに別々のハンドルを使用します(常にそうとは限りません)。 –

+0

'GetClientRect()'は0,0を返しますが、ウィンドウハンドルについてはあなたが正しいと思います。私はSpy ++で少なくとも確認することができます。 – Woodgnome

答えて

4

タイトルバーが含まれているのは、ウィンドウの一部です。クライアント以外の領域を必要としない場合は、クライアント領域rect(GetClientRect)を要求する必要があります。

メモ帳からの混乱は、おそらくウィンドウ自体ではなくテキストボックスのウィンドウハンドルを使用しているためです。 WPFはウィンドウ全体に対して単一のハンドルを使用しますが、win32はしばしばウィンドウ内の各コントロールに別々のハンドルを使用します(常にそうとは限りません)。

GetClientRect「返された」0,0のコメントで、true(成功)またはfalse(失敗)を返したかどうかを確認しましたか? falseを返した場合、GetLastError()の結果を確認しましたか?

+0

'GetClientRect()'は意図したとおりに動作しています。これはウィンドウのクライアント領域のクライアント領域に関連しているため、 'left' /' top'は常に '0,0'です。使用する正しい関数は 'ClientToScreen()'です(私はどのように表示するために質問を更新しました)。 – Woodgnome

+0

これは正しいです。クライアント矩形の左上の点は常に(0、0)です。 'ClientToScreen()'が動作するか、より一般的な 'MapWindowPoints'を使用します。しかし、@ woodgnome、あなたの質問はあなたが "キャレットの位置"がほしいと言っています。このコードは、ウィンドウのクライアント領域の画面座標を返しますが、キャレットは左上隅にない可能性があります。 –

+0

私は 'GUITREADINFO.rcCaret'を使ってキャレットの位置をオフセットしています。' GetClientRect() 'はちょっとした問題を引き起こしました。代わりに' ClientToScreen() 'に置き換えました。 – Woodgnome

関連する問題