ようこそ。睡眠不足なプログラマのチラ裏です。

別プロセスのスレッドに対するアタッチについて

実行中のアプリケーションが何かしらのタイミングで非アクティブとなり、
別のプロセスウィンドウの裏へ隠れてしまうようなことがある。
対応方法としては、単にFormをアクティブにすればよいのだが、
this.Activate() だけでは、うまく Form が前面に移動しない場合がある。
これは既に前面にあるプロセスのウィンドウが、これを拒否するからである。(これはWindowsOSの性質上正しい動き)


Formが別ウィンドウの裏に隠れることなく、必ず前面に移動しアクティブ化したい。
ただし、TopMostプロパティをtrueにすることによりZオーダーをトップに設定する方法はとりたくない。
そんな場合は、下記のようにAPI関数を使って別プロセスのスレッドに対するアタッチが必要となる。

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;  

namespace WindowsApplication1
{
    public partial class Form1 : Form
    {
        #region API

        /// <summary>
        /// 指定されたウィンドウを作成したスレッドの ID を取得します。必要であれば、ウィンドウを作成したプロセスの ID も取得できます。
        /// </summary>
        /// <param name="hWnd">ウィンドウのハンドルを指定します。</param>
        /// <param name="ProcessId">
        /// プロセス ID を受け取る変数へのポインタを指定します。
        /// ポインタを指定すると、それが指す変数にプロセス ID がコピーされます。
        /// NULL を指定した場合は、プロセス ID の取得は行われません。 </param>
        /// <returns>ウィンドウを作成したスレッドの ID が返ります。</returns>
        [DllImport("user32.dll")]
        extern static int GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

        /// <summary>
        /// フォアグラウンドウィンドウ( 現在ユーザーが作業しているウィンドウ)のハンドルを返します。
        /// Windows システムは、フォアグラウンドウィンドウを生成したスレッドに対して、他のスレッドよりも若干高い優先順位を割り当てます。
        /// </summary>
        /// <returns>
        /// フォアグラウンドウィンドウのハンドルが返ります。
        /// フォアグラウンドウィンドウのハンドルは、ウィンドウがフォーカスを失ったなどの
        /// 特定の状況下で NULL になる場合もあります。
        /// </returns>
        [DllImport("user32.dll")]
        extern static IntPtr GetForegroundWindow();

        /// <summary>
        /// 特定のスレッドの入力処理機構を別のスレッドにアタッチします。
        /// </summary>
        /// <param name="idAttach">
        /// 別のスレッドにアタッチするスレッドの識別子を指定します。
        /// システムスレッドをアタッチすることはできません。 </param>
        /// <param name="idAttachTo">
        /// アタッチ先スレッドの識別子を指定します。システムスレッドは指定できません。
        /// スレッドをそれ自体にアタッチすることはできません。
        /// そのため、idAttachTo と idAttach を同じにすることはできません。 
        /// </param>
        /// <param name="fAttach">
        /// スレッドをアタッチするかデタッチするか指定します。
        /// TRUE に設定すると、2 つのスレッドがアタッチされます。
        /// FALSE に設定すると、スレッドがデタッチされます。 
        /// </param>
        /// <returns>
        /// 関数が成功すると、0 以外の値が返ります。
        /// 関数が失敗すると、0 が返ります。拡張エラー情報がないため、GetLastError 関数は使わないでください。
        /// </returns>
        [DllImport("user32.dll")]
        extern static bool AttachThreadInput(int idAttach, int idAttachTo, bool fAttach);

        #endregion

        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// カレントプロセスのスレッドに対してアタッチし、強引に自信をアクティブ化します。
        /// </summary>
        public void AttachThreadFormAtivate()
        {
            //カレントプロセスのスレッドID取得
            int fore_thread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
            
            //システムカレントスレッドID取得
            //int this_thread = AppDomain.GetCurrentThreadId();   //.NET Framework1.x 旧形式
            int this_thread = System.Threading.Thread.CurrentThread.ManagedThreadId;    //.NET Framework2.0

            //同一スレッドの場合は処理を抜ける
            if (fore_thread == this_thread){return;}

            //カレントプロセスのカレントスレッドへアタッチ
            AttachThreadInput(this_thread, fore_thread, true);

            //自信をアクティブへ
            this.Activate();

            //カレントプロセスのカレントスレッドのデタッチ
            AttachThreadInput(this_thread, fore_thread, false);
        }
    }
}