午後わてんのブログ

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

WPF、他のアプリの右クリックメニューのウィンドウハンドル取得方法がわからん

目的は右クリックメニューのRectか画像としての取得で、ウィンドウハンドルさえ取得できればいいんだけどねえ、少し試したけどわからんかったって話
結果から言うと、できることはできたけど、マウスカーソルが右クリックメニューの上にあるっていう条件付き

API.GetCursorPos(out API.POINT cP);
IntPtr hWnd = API.WindowFromPoint(cP);

GetCursorPos()でマウスカーソル座標取得して、それをWindowFromPoint()に渡せば取得できた
でも、これだとどのアプリの右クリックメニューなのかわからないから、違うんだよなあ

github.com

実行環境

API.sc

using System;
using System.Text;

using System.Runtime.InteropServices;//dllインポート用

namespace _20210129_最前面アプリの右クリックメニュー取得
{
    static class API
    {
        //Rect取得用
        public struct RECT
        {
            //型はlongじゃなくてintが正解!!!!!!!!!!!!!!
            //longだとおかしな値になる
            public int left;
            public int top;
            public int right;
            public int bottom;
            public override string ToString()
            {
                return $"横:{right - left:0000}, 縦:{bottom - top:0000}  ({left}, {top}, {right}, {bottom})";
            }
        }
        //座標取得用
        public struct POINT
        {
            public int X;
            public int Y;
        }
        //ウィンドウ情報用
        public struct WINDOWINFO
        {
            public int cbSize;
            public RECT rcWindow;
            public RECT rcClient;
            public uint dwStyle;
            public uint dwExStyle;
            public uint dwWindowStatus;
            public uint cxWindowBorders;
            public uint cyWindowBorders;
            public ushort atomWindowType;
            public short wCreatorVersion;
        }
        public enum WINDOW_STYLE : uint
        {
            WS_BORDER = 0x00800000,
            WS_CAPTION = 0x00C00000,
            WS_CHILD = 0x40000000,
            WS_CHILDWINDOW = 0x40000000,
            WS_CLIPCHILDREN = 0x02000000,
            WS_CLIPSIBLINGS = 0x04000000,
            WS_DISABLED = 0x08000000,
            WS_DLGFRAME = 0x00400000,
            WS_GROUP = 0x00020000,
            WS_HSCROLL = 0x00100000,
            WS_ICONIC = 0x20000000,
            WS_MAXIMIZE = 0x01000000,
            WS_MAXIMIZEBOX = 0x00010000,
            WS_MINIMIZE = 0x20000000,
            WS_MINIMIZEBOX = 0x00020000,
            WS_OVERLAPPED = 0x00000000,
            WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
            //The window is an overlapped window.Same as the WS_TILEDWINDOW style.
            WS_POPUP = 0x80000000,
            WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
            WS_SIZEBOX = 0x00040000,
            WS_SYSMENU = 0x00080000,
            WS_TABSTOP = 0x00010000,
            WS_THICKFRAME = 0x00040000,
            WS_TILED = 0x00000000,
            WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW,
            //(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
            WS_VISIBLE = 0x10000000,
            WS_VSCROLL = 0x00200000,
        }

        //DWM(Desktop Window Manager)
        //見た目通りのRectを取得できる、引数のdwAttributeにDWMWA_EXTENDED_FRAME_BOUNDSを渡す
        //引数のcbAttributeにはRECTのサイズ、Marshal.SizeOf(typeof(RECT))これを渡す
        //戻り値が0なら成功、0以外ならエラー値
        [DllImport("dwmapi.dll")]
        internal static extern long DwmGetWindowAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE dwAttribute, out RECT rect, int cbAttribute);

        //ウィンドウ属性
        //列挙値の開始は0だとずれていたので1からにした
        internal enum DWMWINDOWATTRIBUTE
        {
            DWMWA_NCRENDERING_ENABLED = 1,
            DWMWA_NCRENDERING_POLICY,
            DWMWA_TRANSITIONS_FORCEDISABLED,
            DWMWA_ALLOW_NCPAINT,
            DWMWA_CAPTION_BUTTON_BOUNDS,
            DWMWA_NONCLIENT_RTL_LAYOUT,
            DWMWA_FORCE_ICONIC_REPRESENTATION,
            DWMWA_FLIP3D_POLICY,
            DWMWA_EXTENDED_FRAME_BOUNDS,//見た目通りのウィンドウのRect
            DWMWA_HAS_ICONIC_BITMAP,
            DWMWA_DISALLOW_PEEK,
            DWMWA_EXCLUDED_FROM_PEEK,
            DWMWA_CLOAK,
            DWMWA_CLOAKED,
            DWMWA_FREEZE_REPRESENTATION,
            DWMWA_LAST
        };


        //
        [DllImport("user32.dll")]
        internal static extern IntPtr GetActiveWindow();

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

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

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

        //パレントウィンドウ取得
        [DllImport("user32.dll")]
        internal static extern IntPtr GetParent(IntPtr hWnd);

        [DllImport("user32.dll")]
        internal static extern IntPtr GetWindow(IntPtr hWnd, GETWINDOW_CMD uCmd);//本当のuCmdはuint型
        public enum GETWINDOW_CMD
        {
            GW_CHILD = 5,
            //指定されたウィンドウが親ウィンドウである場合、取得されたハンドルは、Zオーダーの最上位にある子ウィンドウを識別します。
            //それ以外の場合、取得されたハンドルはNULLです。この関数は、指定されたウィンドウの子ウィンドウのみを調べます。子孫ウィンドウは調べません。
            GW_ENABLEDPOPUP = 6,
            //取得されたハンドルは、指定されたウィンドウが所有する有効なポップアップウィンドウを識別します
            //(検索では、GW_HWNDNEXTを使用して最初に見つかったそのようなウィンドウが使用されます)。
            //それ以外の場合、有効なポップアップウィンドウがない場合、取得されるハンドルは指定されたウィンドウのハンドルです。
            GW_HWNDFIRST = 0,
            //取得されたハンドルは、Zオーダーで最も高い同じタイプのウィンドウを識別します。
            //指定されたウィンドウが最上位のウィンドウである場合、ハンドルは最上位のウィンドウを識別します。
            //指定されたウィンドウがトップレベルウィンドウである場合、ハンドルはトップレベルウィンドウを識別します。
            //指定されたウィンドウが子ウィンドウの場合、ハンドルは兄弟ウィンドウを識別します。

            GW_HWNDLAST = 1,
            //取得されたハンドルは、Zオーダーで最も低い同じタイプのウィンドウを識別します。
            //指定されたウィンドウが最上位のウィンドウである場合、ハンドルは最上位のウィンドウを識別します。指定されたウィンドウがトップレベルウィンドウである場合、ハンドルはトップレベルウィンドウを識別します。指定されたウィンドウが子ウィンドウの場合、ハンドルは兄弟ウィンドウを識別します。

            GW_HWNDNEXT = 2,
            //取得されたハンドルは、指定されたウィンドウの下のウィンドウをZオーダーで識別します。
            //指定されたウィンドウが最上位のウィンドウである場合、ハンドルは最上位のウィンドウを識別します。
            //指定されたウィンドウがトップレベルウィンドウである場合、ハンドルはトップレベルウィンドウを識別します。
            //指定されたウィンドウが子ウィンドウの場合、ハンドルは兄弟ウィンドウを識別します。

            GW_HWNDPREV = 3,
            //取得されたハンドルは、指定されたウィンドウの上のウィンドウをZオーダーで識別します。
            //指定されたウィンドウが最上位のウィンドウである場合、ハンドルは最上位のウィンドウを識別します。
            //指定されたウィンドウがトップレベルウィンドウである場合、ハンドルはトップレベルウィンドウを識別します。
            //指定されたウィンドウが子ウィンドウの場合、ハンドルは兄弟ウィンドウを識別します。

            GW_OWNER = 4,
            //取得されたハンドルは、指定されたウィンドウの所有者ウィンドウを識別します(存在する場合)。詳細については、「所有するWindows」を参照してください。
        }

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

        //クライアント領域の座標を画面全体での座標に変換
        [DllImport("user32.dll")]
        internal static extern bool ClientToScreen(IntPtr hWnd, out POINT lpPoint);


        [DllImport("user32.dll")]
        internal static extern int GetWindowInfo(IntPtr hWnd, ref WINDOWINFO info);

        [DllImport("user32.dll")]
        internal static extern IntPtr GetLastActivePopup(IntPtr hWnd);

        /// <summary>
        /// 指定したWindowの一番上のChildWindowを返す
        /// </summary>
        /// <param name="hWnd">IntPtr.Zeroを指定すると一番上のWindowを返す</param>
        /// <returns>ChildWindowを持たない場合はnullを返す</returns>
        [DllImport("user32.dll")]
        internal static extern IntPtr GetTopWindow(IntPtr hWnd);

        /// <summary>
        /// 指定したWindowのメニューのハンドルを返す
        /// </summary>
        /// <param name="hWnd">Windowのハンドル</param>
        /// <returns>Windowがメニューを持たない場合はnullを返す</returns>
        [DllImport("user32.dll")]
        internal static extern IntPtr GetMenu(IntPtr hWnd);

        /// <summary>
        /// キーボードフォーカスを持つWindowのハンドルを返す
        /// </summary>
        /// <returns></returns>
        [DllImport("user32.dll")]
        internal static extern IntPtr GetFocus();

        [DllImport("user32.dll")]
        internal static extern IntPtr GetMenuBarInfo(IntPtr hWnd, MenuObjectId idObject, long idItem, MENUBARINFO pmbi);

        public struct MENUBARINFO
        {
            public long cbSize;
            public RECT rcBar;
            public IntPtr hMenu;
            public bool fBarFocused;
            public bool fFocused;
        }
        public enum MenuObjectId : long
        {
            OBJID_CLIENT = 0xFFFFFFFC,
            OBJID_MENU = 0xFFFFFFFD,
            OBJID_SYSMENU = 0xFFFFFFFF,
        }

        [DllImport("user32.dll")]
        internal static extern IntPtr GetMenuItemRect(IntPtr hWnd, IntPtr hMenu, uint uItem, out RECT rect);


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

        //祖先ウィンドウを取得
        [DllImport("user32.dll")]
        internal static extern IntPtr GetAncestor(IntPtr hWnd, AncestorType type);

        public enum AncestorType
        {
            GA_PARENT = 1,
            GA_ROOT = 2,//Parentを辿ってルートを取得
            GA_ROOTOWNER = 3,//GetParentを使ってルートを取得

        }

        [DllImport("user32.dll")]
        internal static extern IntPtr GetDesktopWindow();


        [DllImport("user32.dll")]
        internal static extern IntPtr GetShellWindow();

        [DllImport("user32.dll")]
        internal static extern IntPtr GetSubMenu(IntPtr hWnd, int nPos);

        [DllImport("user32.dll")]
        internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);




        //public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam, List<IntPtr> intPtrs);
        //[DllImport("user32.dll")]
        //[return: MarshalAs(UnmanagedType.Bool)]
        //internal static extern bool EnumChildWindows(IntPtr hWnd, EnumWindowsDelegate enumWindows, IntPtr lparam);


        //internal static List<IntPtr> GetChildWindows(IntPtr hWnd)
        //{
        //    List<IntPtr> childList = new();
        //    EnumChildWindows(hWnd, new EnumWindowsDelegate(EnumWindowCallBack), IntPtr.Zero);
        //    return childList;
        //}
        //private static bool EnumWindowCallBack(IntPtr hWnd, IntPtr lparam, List<IntPtr> childList)
        //{
        //    childList.Add(hWnd);
        //    return true;
        //}














        //グローバルホットキー登録用
        internal const int WM_HOTKEY = 0x0312;
        [DllImport("user32.dll")]
        internal static extern int RegisterHotKey(IntPtr hWnd, int id, int modkyey, int vKey);
        [DllImport("user32.dll")]
        internal static extern int UnregisterHotKey(IntPtr hWnd, int id);

        //マウスカーソル座標
        [DllImport("user32.dll")]
        internal static extern bool GetCursorPos(out POINT lpPoint);


        //Bitmap描画関連
        //DC取得
        //nullを渡すと画面全体のDCを取得、ウィンドウハンドルを渡すとそのウィンドウのクライアント領域DC
        //失敗した場合の戻り値はnull
        //使い終わったらReleaseDC
        [DllImport("user32.dll")]
        internal static extern IntPtr GetDC(IntPtr hWnd);

        //渡したDCに互換性のあるDC作成
        //失敗した場合の戻り値はnull
        //使い終わったらDeleteDC
        [DllImport("gdi32.dll")]
        internal static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        //指定されたDCに関連付けられているデバイスと互換性のあるビットマップを作成
        //使い終わったらDeleteObject
        [DllImport("gdi32.dll")]
        internal static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int cx, int cy);

        //DCにオブジェクトを指定する、オブジェクトの種類はbitmap、brush、font、pen、Regionなど
        [DllImport("gdi32.dll")]
        internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr h);

        //画像転送
        [DllImport("gdi32.dll")]
        internal static extern bool BitBlt(IntPtr hdc, int x, int y, int cx, int cy, IntPtr hdcSrc, int x1, int y1, uint rop);
        internal const int SRCCOPY = 0x00cc0020;
        internal const int SRCINVERT = 0x00660046;

        ////
        //[DllImport("user32.dll")]
        //private static extern bool PrintWindow(IntPtr hWnd, IntPtr hDC, uint nFlags);
        //private const uint nFrags_PW_CLIENTONLY = 0x00000001;

        //[DllImport("user32.dll")]
        //private static extern bool DeleteDC(IntPtr hdc);

        [DllImport("user32.dll")]
        internal static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

        [DllImport("gdi32.dll")]
        internal static extern bool DeleteObject(IntPtr ho);


    }
}

WinAPI用、今回使っていないメソッドがたくさんあるのはコピペだから

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Input;

using System.Windows.Interop;

//最前面(アクティブ)アプリの右クリックメニューの、ウィンドウハンドルを取得したい
//唯一できた方法はカーソル下のウィンドウハンドルを取得する方法
//でもこれじゃ右クリックメニューって判定しているわけじゃないから、これじゃない感じ

namespace _20210129_最前面アプリの右クリックメニュー取得
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //ホットキー
        private const int HOTKEY_ID1 = 0x0001;//ID
        private IntPtr MyWindowHandle;//アプリのハンドル

        //ウィンドウ探査loopの回数上限値
        private const int LOOP_LIMIT = 3;

        public MainWindow()
        {
            InitializeComponent();

            MyInitializeHotKey();

            //ホットキーにPrintScreenキーを登録
            ChangeHotKey(Key.PrintScreen, HOTKEY_ID1);

            //アプリ終了時にホットキーの解除
            Closing += MainWindow_Closing;
        }

        #region ホットキー関連
        private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            //ホットキーの登録解除
            _ = API.UnregisterHotKey(MyWindowHandle, HOTKEY_ID1);
            ComponentDispatcher.ThreadPreprocessMessage -= ComponentDispatcher_ThreadPreprocessMessage;
        }

        private void MyInitializeHotKey()
        {
            MyWindowHandle = new WindowInteropHelper(this).Handle;
            ComponentDispatcher.ThreadPreprocessMessage += ComponentDispatcher_ThreadPreprocessMessage;
        }
        private void ChangeHotKey(Key Key, int hotkeyId)
        {
            ChangeHotKey(KeyInterop.VirtualKeyFromKey(Key), hotkeyId);
        }
        private void ChangeHotKey(int vKey, int hotkeyId)
        {
            //上書きはできないので、古いのを削除してから登録
            _ = API.UnregisterHotKey(MyWindowHandle, hotkeyId);

            //int mod = GetModifierKeySum();
            //int mod = 2;//ctrl
            //int mod = 1;//alt
            int mod = 0;//修飾キーなし
            if (API.RegisterHotKey(MyWindowHandle, hotkeyId, mod, vKey) == 0)
            {
                MessageBox.Show("登録に失敗");
            }
            else
            {
                //MessageBox.Show("登録完了");
            }
        }
        #endregion ホットキー関連


        //ホットキー判定
        private void ComponentDispatcher_ThreadPreprocessMessage(ref MSG msg, ref bool handled)
        {
            if (msg.message != API.WM_HOTKEY) return;

            //ホットキー(今回はPrintScreen)が押されたら
            else if (msg.wParam.ToInt32() == HOTKEY_ID1)
            {
                var foreInfo = GetWindowRectAndText(API.GetForegroundWindow());//メモ帳のウィンドウ
                var focusInfo = GetWindowRectAndText(API.GetFocus());//なし
                var shellInfo = GetWindowRectAndText(API.GetShellWindow());//program manager
                var activeInfo = GetWindowRectAndText(API.GetActiveWindow());

                //Foregroundwindowから取得できるウィンドウ
                IntPtr foreW = API.GetForegroundWindow();
                var ancesParent = GetWindowRectAndText(API.GetAncestor(foreW, API.AncestorType.GA_PARENT));//Textなしの全画面サイズ
                var ancesRoot = GetWindowRectAndText(API.GetAncestor(foreW, API.AncestorType.GA_ROOT));//メモ帳のウィンドウ
                var ancesRootOwner = GetWindowRectAndText(API.GetAncestor(foreW, API.AncestorType.GA_ROOTOWNER));//メモ帳のウィンドウ
                var lastPop = GetWindowRectAndText(API.GetLastActivePopup(foreW));//メモ帳のウィンドウ
                var menu = GetWindowRectAndText(API.GetMenu(foreW));//none
                var parent = GetWindowRectAndText(API.GetParent(foreW));//none
                var topChild = GetWindowRectAndText(API.GetTopWindow(foreW));//Textなし、メモ帳のウィンドウのクライアント

                //Foregroundwindowのcmd各種
                var childs = GetWindowRectAndTexts(GetCmdWindows(foreW, API.GETWINDOW_CMD.GW_CHILD, LOOP_LIMIT));//メモ帳のウィンドウのクライアント、その後はnone
                var popups = GetWindowRectAndTexts(GetCmdWindows(foreW, API.GETWINDOW_CMD.GW_ENABLEDPOPUP, LOOP_LIMIT));//none
                var first = GetWindowRectAndTexts(GetCmdWindows(foreW, API.GETWINDOW_CMD.GW_HWNDFIRST, LOOP_LIMIT));//TextはすべてDefault IME
                var last = GetWindowRectAndTexts(GetCmdWindows(foreW, API.GETWINDOW_CMD.GW_HWNDLAST, LOOP_LIMIT));//TextはすべてProgram Manager
                var nexts = GetWindowRectAndTexts(GetCmdWindows(foreW, API.GETWINDOW_CMD.GW_HWNDNEXT, LOOP_LIMIT));//全て関係ないアプリ
                var prevs = GetWindowRectAndTexts(GetCmdWindows(foreW, API.GETWINDOW_CMD.GW_HWNDPREV, LOOP_LIMIT));//すべて関係ないアプリ
                var owners = GetWindowRectAndTexts(GetCmdWindows(foreW, API.GETWINDOW_CMD.GW_OWNER, LOOP_LIMIT));//none


                //カーソルの下のウィンドウハンドル
                API.GetCursorPos(out API.POINT cP);
                IntPtr hWnd = API.WindowFromPoint(cP);
                var CursorW = GetWindowRectAndText(hWnd);//右クリックメニュー

                var AncesParent = GetWindowRectAndText(API.GetAncestor(hWnd, API.AncestorType.GA_PARENT));//Textなしの全画面サイズ
                var AncesRoot = GetWindowRectAndText(API.GetAncestor(hWnd, API.AncestorType.GA_ROOT));//右クリックメニュー
                var AncesRootOwner = GetWindowRectAndText(API.GetAncestor(hWnd, API.AncestorType.GA_ROOTOWNER));//右クリックメニュー
                var LastPop = GetWindowRectAndText(API.GetLastActivePopup(hWnd));//右クリックメニュー
                var Menu = GetWindowRectAndText(API.GetMenu(hWnd));//none
                var Parent = GetWindowRectAndText(API.GetParent(hWnd));//none
                var TopChild = GetWindowRectAndText(API.GetTopWindow(hWnd));//none

                var Childs = GetWindowRectAndTexts(GetCmdWindows(hWnd, API.GETWINDOW_CMD.GW_CHILD, LOOP_LIMIT));//すべてnone
                var Popups = GetWindowRectAndTexts(GetCmdWindows(hWnd, API.GETWINDOW_CMD.GW_ENABLEDPOPUP, LOOP_LIMIT));//none
                var First = GetWindowRectAndTexts(GetCmdWindows(hWnd, API.GETWINDOW_CMD.GW_HWNDFIRST, LOOP_LIMIT));//TextはすべてDefault IME
                var Last = GetWindowRectAndTexts(GetCmdWindows(hWnd, API.GETWINDOW_CMD.GW_HWNDLAST, LOOP_LIMIT));//TextはすべてProgram Manager
                var Nexts = GetWindowRectAndTexts(GetCmdWindows(hWnd, API.GETWINDOW_CMD.GW_HWNDNEXT, LOOP_LIMIT));//右クリックメニューの影ウィンドウ、それ以降は関係ないアプリ
                var Prevs = GetWindowRectAndTexts(GetCmdWindows(hWnd, API.GETWINDOW_CMD.GW_HWNDPREV, LOOP_LIMIT));//8個noneが続いたあと関係ないアプリ
                var Owners = GetWindowRectAndTexts(GetCmdWindows(hWnd, API.GETWINDOW_CMD.GW_OWNER, LOOP_LIMIT));//none

            }
        }
     
        //指定したAPI.GETWINDOW_CMDを収集
        private List<IntPtr> GetCmdWindows(IntPtr hWnd, API.GETWINDOW_CMD cmd, int loopCount)
        {
            List<IntPtr> v = new();
            IntPtr temp = API.GetWindow(hWnd, cmd);
            for (int i = 0; i < loopCount; i++)
            {
                v.Add(temp);
                temp = API.GetWindow(temp, cmd);                
            }
            return v;
        }


        //ウィンドウハンドルからText(タイトル名)やRECTを取得
        private (IntPtr, API.RECT re, string text) GetWindowRectAndText(IntPtr hWnd)
        {
            return (hWnd, GetWindowRect(hWnd), GetWindowText(hWnd));
        }
        private (List<IntPtr> ptrs, List<API.RECT> rs, List<string> strs) GetWindowRectAndTexts(List<IntPtr> pList)
        {
            List<IntPtr> ptrs = new();
            List<API.RECT> rs = new();
            List<string> strs = new();
            foreach (var item in pList)
            {
                ptrs.Add(item);
                rs.Add(GetWindowRect(item));
                strs.Add(GetWindowText(item));
            }
            return (ptrs, rs, strs);
        }

        //ウィンドウハンドルからRECT取得
        private API.RECT GetWindowRect(IntPtr hWnd)
        {
            _ = API.GetWindowRect(hWnd, out API.RECT re);
            return re;
        }

        //ウィンドウハンドルからText取得
        private string GetWindowText(IntPtr hWnd)
        {
            var text = new StringBuilder(65535);
            _ = API.GetWindowText(hWnd, text, 65535);
            return text.ToString();
        }

    }
}


WinAPIを使って最前面ウィンドウのハンドルやそのRectとか取得する、一時停止で確認する
f:id:gogowaten:20210202131446p:plain
一時停止の場所は132行目

取得タイミングはホットキーを押した瞬間
ホットキーはPrintScreenキー

ウィンドウハンドルからRECTとText(タイトル名?)を取得する
f:id:gogowaten:20210202105314p:plain
これでRect(座標とサイズ)とText(タイトル名)から、何のウィンドウなのか判別できる

↑のをまとめたもの
f:id:gogowaten:20210202105404p:plain
ハンドルを渡すとRECTとTextを返す

↑のをListにしただけ
f:id:gogowaten:20210202105410p:plain
これでまとめて確認できる

APIのGetWindow()用
f:id:gogowaten:20210202105407p:plain
GetWindowにAPI.GETWINDOW_CMDのNEXTを渡すと、
ウィンドウの下にあるウィンドウを取得できる
NEXTの代わりにPREVを渡すと上にあるウィンドウを取得できる
繰り返せばどんどん辿って行けるので
CMDの指定と、たどる回数の上限値を指定してまとめて取得できるようにしたもの

あとはWinAPIの中から適当にそれっぽいのを使って、取得したウィンドウハンドルを渡して、一時停止して確認

WinAPIのGet~をいくつか試した過程

デスクトップ画面左上にあるメモ帳
f:id:gogowaten:20210202112839p:plain
右クリックメニューを表示した状態で、ホットキーに指定してあるPrintScreenキーを押して確認

f:id:gogowaten:20210202111647p:plain
GetForegroundwindow()はメモ帳自体のウィンドウハンドルを取得しているのがわかる
それ以外のメソッドは関係ないか何も取得できていない
つまりGetForegroundwindow()にしか期待できない

|WinAPI|結果| |---|---| |GetForegroundwindow()|アプリのウィンドウ| |GetFocus()|none| |GetShellWindow()|関係ないウィンドウ|

Rectを確認
f:id:gogowaten:20210202112604p:plain
GetForegroundwindowで取得したウィンドウハンドルの、Rectに合わせた半透明の黒画像を重ねてみたところ
ウィンドウ枠より一回り大きいのは、枠のドロップシャドウ部分を含んでいるから、これであっている

GetForegroundwindow()で得たウィンドウハンドル(メモ帳)を渡す

WinAPI 結果
GetAncestor + PARENT none
GetAncestor + ROOT アプリのウィンドウ
GetAncestor + ROOTOWNER アプリのウィンドウ
GetLastActivePopup アプリのウィンドウ
GetMenu none
GetParent none
GetTopWindow アプリのクライアント領域ウィンドウ

一時停止
f:id:gogowaten:20210202113916p:plain
どれも違う

CMD各種
f:id:gogowaten:20210202115050p:plain
それぞれ3つまで辿ってみる

GW_CHILD
f:id:gogowaten:20210202115119p:plain
最初のがそれっぽいけど、Rectをあわせてみると
f:id:gogowaten:20210202115442p:plain
メモ帳のクライアント領域だった

GW_ENABLEDPOPUP
f:id:gogowaten:20210202115532p:plain
Popupっていう名前からして期待していたけど、何も得られず

GW_HWNDFIRST
f:id:gogowaten:20210202115621p:plain
Default IMEっていうTextがついた0x0のウィンドウハンドルが得られただけ

GW_HWNDLAST
f:id:gogowaten:20210202115750p:plain
デスクトップと同じ大きさのRect、TextはProgram Manager、これも関係ない

GW_HWNDNEXT
f:id:gogowaten:20210202115857p:plain
下にあるウィンドウ3枚、どれも関係ない

GW_HWNDPREV
f:id:gogowaten:20210202120037p:plain
上にあるウィンドウ3枚、なんか下にあるウィンドウ3枚と似てる

GW_OWNER
f:id:gogowaten:20210202120206p:plain
何も取得できていない

まとめると

GetWindowにCMD各種 結果
CHILD アプリのクライアント領域ウィンドウのあとは全部none
ENABLEDPOPUP 全部none
FIRST 全部none
LAST 関係ないウィンドウ
NEXT 関係ないウィンドウ
PREV 関係ないウィンドウ
OWNER 全部none

GetWindowも全滅
ってことで、ここまでの方法では右クリックメニューのウィンドウハンドルを取得することができなかった

逆に
じゃあ次は右クリックメニューのウィンドウハンドルから元のアプリ、ここではメモ帳のウィンドウハンドルを取得することができれば、関連していることがわかるからそれでもいいかなと

カーソル下のウィンドウハンドル取得
f:id:gogowaten:20210202120918p:plain
右クリックメニューの上にマウスカーソルを置いて

これで得られたRectを確認してみると
f:id:gogowaten:20210202121242p:plain
右クリックメニューにピッタリ合ったので、これで取得できている
でも、このままだと何のウィンドウなのか不明なので、このウィンドウハンドルはメモ帳の右クリックメニューです、っていうことが確認できればいい
名前的にはGetParentやGetAncestor、GetWindowのOWNERとかで取得できそうなんだけどねえ(できなかった)

右クリックメニューのウィンドウハンドルを各種Get~に渡した結果
f:id:gogowaten:20210202121919p:plain

WinAPI 結果
GetAncestor + PARENT Textなし、RECTは全画面
GetAncestor + ROOT 右クリックメニュー
GetAncestor + ROOTOWNER 右クリックメニュー
GetLastActivePopup 右クリックメニュー
GetMenu none
GetParent none
GetTopWindow none

メモ帳のウィンドウハンドルは取得できず


GetWindow()でCMD各種
f:id:gogowaten:20210202122621p:plain
hWndが右クリックメニューのウィンドウハンドル

GW_CHILD
f:id:gogowaten:20210202123057p:plain
何も取得できず

GW_ENABLEDPOPUP
f:id:gogowaten:20210202123139p:plain
これも、何も取得できず

GW_HWNDFIRST
f:id:gogowaten:20210202123222p:plain
またIMEかあ

GW_HWNDLAST
f:id:gogowaten:20210202123253p:plain
またProgram Managerかあ

GW_HWNDNEXT
f:id:gogowaten:20210202123326p:plain
なんか取得できた

[0]のRectを確認してみると
f:id:gogowaten:20210202123629p:plain
右クリックメニューより右と下に5ピクセル大きいこのRectは、ドロップシャドウ専用ウィンドウ?だった、ぬか喜び
他の[1][2]も関係ないウィンドウ

GW_HWNDPREV
f:id:gogowaten:20210202123911p:plain
PREVは上のウィンドウだから何も取得できず

GW_OWNER
f:id:gogowaten:20210202123949p:plain
OWNERには期待していたけど、何も得られず

右クリックメニューのウィンドウハンドルに対してGetWindowにCMD各種

CMD 結果
CHILD 全部none
ENABLEDPOPUP 全部none
FIRST 全部none
LAST 関係ないウィンドウ
NEXT 関係ないウィンドウ
PREV 全部none
OWNER 全部none

ってことで、これも全滅
この状態はメモ帳以外のアプリでも同じ
根本的に間違っているのかも
おわり


不可解
右クリックメニューに階層がある場合は取得できる
f:id:gogowaten:20210202125053p:plain
サブメニューっていうのかな、>マークが付いているメニュー項目を選択すると、さらにメニューウィンドウがある場合、そのウィンドウハンドルは

取得できる
f:id:gogowaten:20210202125514p:plain
103行目、メモ帳のウィンドウハンドルをGetWindow()にGW_ENABLEDPOPUPとともに渡したところ
[0]にあるRectを確認してみると

f:id:gogowaten:20210202125758p:plain
右クリックメニューにピッタリ重なった

しかも逆に辿ることができる
f:id:gogowaten:20210202130309p:plain
GetAncestorのROOTOWNERとGetParentでメモ帳のウィンドウハンドルが取得できている



関連記事
次回のWPF記事は2日後

gogowaten.hatenablog.com

前回のWPF記事は5日前

gogowaten.hatenablog.com

9日前
gogowaten.hatenablog.com

1ヶ月前
gogowaten.hatenablog.com
これで、ウィンドウ枠外の右クリックメニューとかを切り抜いたスクショを撮りたくて、もがいているのが最近の記事