いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

特定のプロセスをアクティブにする-3(ShowWindow, GetForegroundWindow, SetForegroundWindow, GetWindowThreadProcessId, BringWindowToTop, AttachThreadInput, PostMessage, WndProc, 最前面に表示)


過去、2回にわたってアクティブにするためにいろいろやってましたが、知り合いに教えてもらったやり方で
まとまりました。これだと、確実に対象となるプロセスがアクティブになるはずです。


以下、サンプルコードです。

// vim:set ts=4 sw=4 et ws is nowrap ft=cs:
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace WindowsApplication1 {

    public partial class Form1 : Form {

        //////////////////////////////////////////////////////////////////
        //
        // 以下Win32Apiの宣言.
        //
        //
        [DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

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

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true)]
        static extern bool BringWindowToTop(IntPtr hWnd);

        [DllImport("user32.dll")]
        extern static bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll", SetLastError = true)]
        extern static bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);




        /// <summary>
        /// 強制的にウィンドウをアクティブにする際に利用される独自ウィンドウメッセージ (WM_USERの値に1加算)
        /// </summary>
        const int MY_FORCE_FOREGROUND_MESSAGE = 0x400 + 1;

        /// <summary>
        /// コンストラクタ.
        /// </summary>
        public Form1() {
            InitializeComponent();

            //
            // テスト用に10秒間待機する。
            //
            Thread.Sleep(10000);
        }

        /// <summary>
        /// フォームのロード時にコールバックされます。
        /// </summary>
        /// <param name="sender">イベント送信元オブジェクト</param>
        /// <param name="e">イベント引数オブジェクト</param>
        private void Form1_Load(object sender, EventArgs e) {
            //
            // メッセージを投げて、フォームのロード処理が完了次第、ウィンドウをアクティブになるようにする。
            //
            PostMessage(new HandleRef(this, Handle), MY_FORCE_FOREGROUND_MESSAGE, (IntPtr)0, (IntPtr)0);
        }

        /// <summary>
        /// 指定されたハンドルを持つウィンドウを強制的にアクティブにします。
        /// </summary>
        /// <param name="targetHandle">対象となるウィンドウハンドルオブジェクト</param>
        protected void SetForceForegroundWindow(IntPtr targetHandle){

            uint nullProcessId = 0;

            // ターゲットとなるハンドルのスレッドIDを取得.
            uint targetThreadId        = GetWindowThreadProcessId(targetHandle, out nullProcessId);
            // 現在アクティブとなっているウィンドウのスレッドIDを取得.
            uint currentActiveThreadId = GetWindowThreadProcessId(GetForegroundWindow(), out nullProcessId);

            ////////////////////////////////////////////////
            //
            // アクティブ処理
            //
            //
            SetForegroundWindow(targetHandle);
            if(targetThreadId == currentActiveThreadId){
                //
                // 現在アクティブなのが自分の場合は前面に持ってくる。
                //
                BringWindowToTop(targetHandle);
            }else{
                //
                // 別のプロセスがアクティブな場合は、そのプロセスにアタッチし、入力を奪う.
                //
                AttachThreadInput(targetThreadId, currentActiveThreadId, true);
                try {
                    //
                    // 自分を前面に持ってくる。
                    //
                    BringWindowToTop(targetHandle);
                } finally {
                    //
                    // アタッチを解除.
                    //
                    AttachThreadInput(targetThreadId, currentActiveThreadId, false);
                }
            }
        }

        protected override void WndProc(ref Message m) {

            if(m.Msg == MY_FORCE_FOREGROUND_MESSAGE){
                //
                // ウィンドウを強制的にアクティブにする。
                //
                SetForceForegroundWindow(m.HWnd);

                //
                // その後のウィンドウメッセージの処理は必要ないので、処理を戻す。
                //
                return;
            }

            //
            // それ以外の場合は、通常通りメッセージ処理を行ってもらう。
            //
            base.WndProc(ref m);
        }
    }
}


上記のサンプルでは、コンストラクト時に10秒間のSleepをいれてあります。
その後、フォームロード時にPostMessageを使用して独自メッセージを送信しています。
このようにすると、ロード処理が確実に完了した後でアクティブ処理を行う事が出来ます。
(メッセージはキューに溜まっており、順に処理されるため)
アプリを起動し、いろんなウィンドウを重ねていってみてください。
10秒たった後に、ウィンドウが最前面でアクティブな状態で表示されるはずです。


ちなみに過去二回の分は、以下から見れます。