午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

マウスカーソルの下にあるウィンドウのRect取得してみた、GetWindowRectとGetClientRect

f:id:gogowaten:20201112103844g:plain
マウスカーソルの下にあるウィンドウのRect取得してるところ
2020WPF/20201111_カーソル下のウィンドウ取得
github.com

GetWindowRectはタイトルバーや枠も含めた領域
GetClientRectは枠の内側の領域

MainWindow.xaml

<Window x:Class="_20201111_カーソル下のウィンドウ取得.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:_20201111_カーソル下のウィンドウ取得"
        mc:Ignorable="d"
        Title="MainWindow" Height="120" Width="600">
  <Grid>
    <StackPanel>
      <TextBlock x:Name="MyTextBlock1" Text="text" Margin="8,2"/>
      <TextBlock x:Name="MyTextBlock2" Text="text" Margin="8,2"/>
      <TextBlock x:Name="MyTextBlock3" Text="text" Margin="8,2"/>
    </StackPanel>
  </Grid>
</Window>

f:id:gogowaten:20201112104951p:plain

MainWindow.xaml.cs

using System;
using System.Text;
using System.Windows;
using System.Runtime.InteropServices;
using System.Windows.Threading;

//1秒毎にマウスカーソルの下にあるウィンドウのRectを取得
namespace _20201111_カーソル下のウィンドウ取得
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region WindowsAPI^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        //Rect取得用
        private struct RECT
        {
            //型はlongじゃなくてintが正解!!!!!!!!!!!!!!
            //longだとおかしな値になる
            public int left;
            public int top;
            public int right;
            public int bottom;
        }
        //座標取得用
        private struct POINT
        {
            public int x;
            public int y;
        }


        //ウィンドウのクライアント領域のRect取得
        [DllImport("user32.dll")]
        private static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

        //ウィンドウのRect取得
        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        //手前にあるウィンドウのハンドル取得
        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        //指定座標にあるウィンドウのハンドル取得
        [DllImport("user32.dll")]
        private static extern IntPtr WindowFromPoint(POINT pOINT);

        //ウィンドウ名取得
        [DllImport("user32.dll")]
        private static extern int GetWindowText(IntPtr hWin, StringBuilder lpString, int nMaxCount);

        //マウスカーソルの位置取得
        [DllImport("user32.dll")]
        private static extern bool GetCursorPos(out POINT lpPoint);

        //ウィンドウ系のAPI
        //Windows(Windowsおよびメッセージ)-Win32アプリ | Microsoft Docs
        // https://docs.microsoft.com/en-us/windows/win32/winmsg/windows

        //        スクリーン上でのウィンドウクライアント領域の取得 - 捨てられたブログ
        //https://blog.recyclebin.jp/archives/863

        //        【C#】アクティブウィンドウのウィンドウ名を取得 - プログラミングとかブログ
        //https://shirakamisauto.hatenablog.com/entry/2016/03/26/110000

        #endregion ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^



        DispatcherTimer MyTimer;
        public MainWindow()
        {
            InitializeComponent();

            //1秒毎のタイマー
            MyTimer = new DispatcherTimer();
            MyTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000);
            MyTimer.Tick += MyTimer_Tick;
            MyTimer.Start();

        }

        private void MyTimer_Tick(object sender, EventArgs e)
        {
            //一番手前のウィンドウのハンドル取得
            //IntPtr hw = GetForegroundWindow();

            //マウスカーソルの位置取得
            GetCursorPos(out POINT cursorP);

            //マウスカーソルの下にあるウィンドウのハンドル取得
            //右クリックメニューやボタン、テキストボックスとかのコントロールも取得される
            IntPtr handleWin = WindowFromPoint(cursorP);

            //ウィンドウのRect取得、これは透明や半透明の枠も含むので見た目より大きな値になる
            //座標は画面全体での位置になる
            if (GetWindowRect(handleWin, out RECT wRect))
            {
                MyTextBlock1.Text = $"WindowRect 左上座標({wRect.left}, {wRect.top}) 右下({wRect.right}, {wRect.bottom})";
            }

            //ウィンドウのクライアント領域(枠の内側領域)のRect取得
            //leftとtopは常に0、なので実質、rightは横幅、bottomは縦幅ってことになる
            if (GetClientRect(handleWin, out RECT cRect))
            {
                MyTextBlock2.Text = $"ClientRect ({cRect.left}, {cRect.top}) (横幅:{cRect.right}, 縦幅:{cRect.bottom})";
            };

            //ウィンドウの名前表示
            var winName = new StringBuilder(65535);
            GetWindowText(handleWin, winName, 65535);
            MyTextBlock3.Text = $"ウィンドウ名:{winName}";

        }
    }

}

今回もWindowsAPIばかりで、WPFあんまり関係ないね

f:id:gogowaten:20201112111645p:plain
ウィンドウ領域とクライアント領域
青がウィンドウ領域で、紫がクライアント領域
ウィンドウの左右と下側の枠は半透明になっていて、ここもウィンドウの領域になるので、見た目より大きなサイズになっているみたい

メニューもウィンドウとして取得される
f:id:gogowaten:20201112113101p:plain
メモ帳のメニューの書式を開いて、その上にマウスカーソルを置いたところ、メモ帳とは別のウィンドウとして取得された
サイズを見てみるとウィンドウサイズは横幅が1405-1220=185、縦幅は447-397=50
その領域を青にしてみると
f:id:gogowaten:20201112114027p:plain
多分こんなかんじ
メニューには右下に影を落とす処理がされているけど、この部分は領域外みたいねえ

クライアント領域サイズは179と44
ピンクにしてみると
f:id:gogowaten:20201112114045p:plain

両方重ねてみると
f:id:gogowaten:20201112114200p:plain
こうかなあ

ボタンもウィンドウ?
f:id:gogowaten:20201112115419p:plain
ウィンドウ名にはボタンに書いてある文字が表示された、このアプリはWindowsFormで作ったもの

WPFで作ったのは?
f:id:gogowaten:20201112120616p:plain
ボタンの上においてもボタンじゃなくて、アプリのウィンドウが取得された
同じボタンでもアプリによって違うんだなあ


参照したところ
WindowsWindowsおよびメッセージ)-Win32アプリ | Microsoft Docs
docs.microsoft.com

スクリーン上でのウィンドウクライアント領域の取得 - 捨てられたブログ
blog.recyclebin.jp

C#】アクティブウィンドウのウィンドウ名を取得 - プログラミングとかブログ
shirakamisauto.hatenablog.com




関連記事
前回のWindowsAPI記事は昨日
gogowaten.hatenablog.com

次回は4日後

gogowaten.hatenablog.com