ここでは、自分のアプリケーションではなく、外部のアプリケーションのウィンドウをアクティブにする方法(つまり、「フォームをアクティブにする」が使えない場合の方法)を紹介します。
外部アプリケーションのウィンドウをアクティブにするには、VB.NETであればAppActivateメソッドを使うことができます。使い方は、アクティブにしたいウィンドウのタイトルを指定して呼び出すだけです。大文字と小文字は区別しません。完全に一致するタイトルのウィンドウがなかった場合は、指定された文字列で終わるタイトルのウィンドウをアクティブにします。それでも見つからなかった場合は、例外ArgumentExceptionをスローします。
以下に、「メモ帳」というタイトルのウィンドウをアクティブにする例を示します。C#では、Microsoft.VisualBasic.dllを参照設定に追加する必要があります。
'「メモ帳」というタイトルのウィンドウをアクティブにする
AppActivate("メモ帳")
//「メモ帳」というタイトルのウィンドウをアクティブにする
Microsoft.VisualBasic.Interaction.AppActivate("メモ帳");
AppActivateは、プロセスのIDを指定して呼び出すこともできます。目的のプロセスのIDを取得する方法は、以下のような記事を参考にしてください。
以下に、この方法でメモ帳をアクティブにする例を示します。
'メモ帳のプロセスを探す Dim ps As System.Diagnostics.Process() = _ System.Diagnostics.Process.GetProcessesByName("notepad") If 0 < ps.Length Then '見つかった時は、アクティブにする Microsoft.VisualBasic.Interaction.AppActivate(ps(0).Id) End If
//メモ帳のプロセスを探す System.Diagnostics.Process[] ps = System.Diagnostics.Process.GetProcessesByName("notepad"); if (0 < ps.Length) { //見つかった時は、アクティブにする Microsoft.VisualBasic.Interaction.AppActivate(ps[0].Id); }
AppActivateを使わないのであれば、Win32 APIのSetForegroundWindowを使用してウィンドウをアクティブにできます。ただしこの場合、SetForegroundWindowにはウィンドウのハンドルを指定しなければならず、それを見つけるのは結構大変です。その方法については、「ウィンドウのタイトルからプロセスを探す」を参考にしてください。
以下の例では、ウィンドウのタイトルに「メモ帳」という文字列が入ったウィンドウをアクティブにしています。
'Imports System.Runtime.InteropServices <DllImport("user32.dll")> _ Private Shared Function SetForegroundWindow(hWnd As IntPtr) As _ <MarshalAs(UnmanagedType.Bool)> Boolean End Function ''' <summary> ''' エントリポイント ''' </summary> Public Shared Sub Main() 'すべてのプロセスを列挙する For Each p As System.Diagnostics.Process In _ System.Diagnostics.Process.GetProcesses() '"メモ帳"がメインウィンドウのタイトルに含まれているか調べる If 0 <= p.MainWindowTitle.IndexOf("メモ帳") Then 'ウィンドウをアクティブにする SetForegroundWindow(p.MainWindowHandle) Exit For End If Next End Sub
//using System.Runtime.InteropServices; [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetForegroundWindow(IntPtr hWnd); /// <summary> /// エントリポイント /// </summary> public static void Main() { //すべてのプロセスを列挙する foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses()) { //"メモ帳"がメインウィンドウのタイトルに含まれているか調べる if (0 <= p.MainWindowTitle.IndexOf("メモ帳")) { //ウィンドウをアクティブにする SetForegroundWindow(p.MainWindowHandle); break; } } }
上で紹介して方法では指定したウィンドウを必ずアクティブにするとは限らず、条件によってはそうなりません(その条件は、MSDNの「SetForegroundWindow」の説明にあります)。そうならないのにはそれなりの理由があり、本来ならばそのようなOSのルールを尊重し、従うべきです。しかし、それを承知のうえで、どうしてもウィンドウをアクティブにしたいという状況もあります。そこでここからは、その対策を紹介します。なお具体例は最後にまとめて示します。
最小化されているウィンドウは元の状態に戻さないとフォアグランドウィンドウになりません。ウィンドウが最小化されているかはIsIconic関数で分かります。最小化されたウィンドウを元に戻すのは、ShowWindowAsync関数やShowWindow関数でできます。
「Windows XP 環境への既存アプリケーションの移行」によると、Windows XPからは、あるプロセスが別のプロセスのウィンドウをフォアグランドにするには、幾つかの条件が必要になりました。条件が満たされない場合は、ウィンドウがフォアグランドになる代わりに、タスクバーのアイコンが点滅します。
同ページにある解決法によると、もしフォアグランドにしたいウィンドウのアプリケーションを修正できるならば、AllowSetForegroundWindowを使用します。それができないのであれば(通常はこちらでしょう)、AttachThreadInputを使ってフォアグランドにしたいアプリケーションの入力処理機構にアタッチして、その後にSetForegroundWindowを呼び出します。
ただし、「I warned you: The dangers of attaching input queues」によるとこの方法はかなり危険ということですので、使用する場合はそのことを十分にご理解ください。
「SystemParametersInfo 関数」のSPI_GETFOREGROUNDLOCKTIMEOUTの説明によると、「ユーザーが何かを入力した後、システムは一定の時間にわたって、アプリケーションが自らをフォアグラウンドにすることを禁止します」ということです。よって、この時間(ForegroundLockTimeout)を0にするという対策も考えられます。
ForegroundLockTimeoutは、SystemParametersInfo関数で取得、設定ができます。または、レジストリの「HKEY_CURRENT_USER\Control Panel\Desktop」の「ForegroundLockTimeout」から取得、設定することもできます。
ただし、「Visual Studio 2012 または Visual Studio 2010 を起動すると SPI_GETFOREGROUNDLOCKTIMEOUT フラグを指定し取得されるシステム パラメーターの値が変化する」によると、Visual Studio 2010や2012は起動後にSPI_GETFOREGROUNDLOCKTIMEOUTで取得できる値を変更してしまうため、SystemParametersInfoで正確な値(レジストリの値)を取得することができなくなってしまうということです。Visual Studioだけでなく、他にも同様のことを行なっているアプリケーションがあるかもしれません。ですので、SystemParametersInfoで値を取得した場合は、その値を元に戻す時、レジストリに書き込まないようにした方が良いでしょう。
「SetForegroundWindow Win32-API not always works on Windows-7」によると、Windows VistaからはSetForegroundWindowの代わりにBringWindowToTopを使うと良いそうです。
「winapi - How can I bring a window to the foreground in Vista using C++?」などによると、Windows Vista以降の対策として、SetWindowPosで一度最前面にしてから元に戻すという方法が有効な場合があるそうです。この例は、以下のようなものです。
'Imports System.Runtime.InteropServices ''' <summary> ''' SetWindowPosを使って、指定したウィンドウをフォアグラウンドにする ''' </summary> ''' <param name="hWnd">ウィンドウハンドル</param> Public Shared Sub ActiveWindowWithSetWindowPos(hWnd As IntPtr) SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, _ SWP_NOMOVE Or SWP_NOSIZE) SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, _ SWP_SHOWWINDOW Or SWP_NOMOVE Or SWP_NOSIZE) End Sub <DllImport("user32.dll", SetLastError:=True)> _ Private Shared Function SetWindowPos(hWnd As IntPtr, _ hWndInsertAfter As Integer, _ x As Integer, y As Integer, cx As Integer, cy As Integer, _ uFlags As Integer) _ As <MarshalAs(UnmanagedType.Bool)> Boolean End Function Private Const SWP_NOSIZE As Integer = &H1 Private Const SWP_NOMOVE As Integer = &H2 Private Const SWP_SHOWWINDOW As Integer = &H40 Private Const HWND_TOPMOST As Integer = -1 Private Const HWND_NOTOPMOST As Integer = -2
//using System.Runtime.InteropServices; /// <summary> /// SetWindowPosを使って、指定したウィンドウをフォアグラウンドにする /// </summary> /// <param name="hWnd">ウィンドウハンドル</param> public static void ActiveWindowWithSetWindowPos(IntPtr hWnd) { SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); } [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, int uFlags); private const int SWP_NOSIZE = 0x0001; private const int SWP_NOMOVE = 0x0002; private const int SWP_SHOWWINDOW = 0x0040; private const int HWND_TOPMOST = -1; private const int HWND_NOTOPMOST = -2;
SwitchToThisWindowは廃止予定とされていますので使用すべきではありませんが、これが一番確実であるという投稿も多く見ましたので、一応紹介しておきます。使用例は、以下のようになります。
'Imports System.Runtime.InteropServices ''' <summary> ''' SwitchToThisWindowを使って、指定したウィンドウをフォアグラウンドにする ''' </summary> ''' <param name="hWnd">ウィンドウハンドル</param> Public Shared Sub ActiveWindowWithSwitchToThisWindow(hWnd As IntPtr) SwitchToThisWindow(hWnd, True) End Sub <DllImport("user32.dll", SetLastError:=True)> _ Private Shared Sub SwitchToThisWindow(hWnd As IntPtr, fAltTab As Boolean) End Sub
//using System.Runtime.InteropServices; /// <summary> /// SwitchToThisWindowを使って、指定したウィンドウをフォアグラウンドにする /// </summary> /// <param name="hWnd">ウィンドウハンドル</param> public static void ActiveWindowWithSwitchToThisWindow(IntPtr hWnd) { SwitchToThisWindow(hWnd, true); } [DllImport("user32.dll", SetLastError = true)] private static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
以上の情報と後述する参考記事を元にして、できるだけ指定したウィンドウをフォアグラウンドにするための処理をてんこ盛りにした(つまり、無駄の多い)コードを以下に示します。前述した通り、このコードには危険を伴う部分があることと、ここまでやってもウィンドウをフォアグラウンドできないこともあることをご理解いただいた上で、参考にしてください。
'Imports System.Runtime.InteropServices ''' <summary> ''' できるだけ確実に指定したウィンドウをフォアグラウンドにする ''' </summary> ''' <param name="hWnd">ウィンドウハンドル</param> Public Shared Sub ActiveWindow(hWnd As IntPtr) If hWnd = IntPtr.Zero Then Return End If 'ウィンドウが最小化されている場合は元に戻す If IsIconic(hWnd) Then ShowWindowAsync(hWnd, SW_RESTORE) End If 'AttachThreadInputの準備 'フォアグラウンドウィンドウのハンドルを取得 Dim forehWnd As IntPtr = GetForegroundWindow() If forehWnd = hWnd Then Return End If 'フォアグラウンドのスレッドIDを取得 Dim foreThread As UInteger = _ GetWindowThreadProcessId(forehWnd, IntPtr.Zero) '自分のスレッドIDを収得 Dim thisThread As UInteger = GetCurrentThreadId() Dim timeout As UInteger = 200000 If foreThread <> thisThread Then 'ForegroundLockTimeoutの現在の設定を取得 'Visual Studio 2010, 2012起動後は、レジストリと違う値を返す SystemParametersInfoGet(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, timeout, 0) 'レジストリから取得する場合 'timeout = CUInt(Microsoft.Win32.Registry.GetValue( _ ' "HKEY_CURRENT_USER\Control Panel\Desktop", _ ' "ForegroundLockTimeout", 200000)) 'ForegroundLockTimeoutの値を0にする '(SPIF_UPDATEINIFILE Or SPIF_SENDCHANGE)を使いたいが、 ' timeoutがレジストリと違う値だと戻せなくなるので使わない SystemParametersInfoSet(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, 0) '入力処理機構にアタッチする AttachThreadInput(thisThread, foreThread, True) End If 'ウィンドウをフォアグラウンドにする処理 SetForegroundWindow(hWnd) SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, _ SWP_NOMOVE Or SWP_NOSIZE Or SWP_SHOWWINDOW Or SWP_ASYNCWINDOWPOS) BringWindowToTop(hWnd) ShowWindowAsync(hWnd, SW_SHOW) SetFocus(hWnd) If foreThread <> thisThread Then 'ForegroundLockTimeoutの値を元に戻す 'ここでも(SPIF_UPDATEINIFILE Or SPIF_SENDCHANGE)は使わない SystemParametersInfoSet(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, timeout, 0) 'デタッチ AttachThreadInput(thisThread, foreThread, False) End If End Sub <DllImport("user32.dll")> _ Private Shared Function SetForegroundWindow(hWnd As IntPtr) As _ <MarshalAs(UnmanagedType.Bool)> Boolean End Function <DllImport("user32.dll")> _ Private Shared Function GetForegroundWindow() As IntPtr End Function <DllImport("user32.dll", SetLastError:=True)> _ Private Shared Function BringWindowToTop(hWnd As IntPtr) As _ <MarshalAs(UnmanagedType.Bool)> Boolean End Function <DllImport("user32.dll")> _ Private Shared Function SetFocus(hWnd As IntPtr) As IntPtr End Function <DllImport("user32.dll", SetLastError:=True)> _ Private Shared Function SetWindowPos(hWnd As IntPtr, _ hWndInsertAfter As Integer, x As Integer, y As Integer, cx As Integer, cy As Integer, _ uFlags As Integer) As _ <MarshalAs(UnmanagedType.Bool)> Boolean End Function Private Const SWP_NOSIZE As Integer = &H1 Private Const SWP_NOMOVE As Integer = &H2 Private Const SWP_NOZORDER As Integer = &H4 Private Const SWP_SHOWWINDOW As Integer = &H40 Private Const SWP_ASYNCWINDOWPOS As Integer = &H4000 Private Const HWND_TOP As Integer = 0 Private Const HWND_BOTTOM As Integer = 1 Private Const HWND_TOPMOST As Integer = -1 Private Const HWND_NOTOPMOST As Integer = -2 <DllImport("user32.dll")> _ Private Shared Function ShowWindow(hWnd As IntPtr, nCmdShow As Integer) As _ <MarshalAs(UnmanagedType.Bool)> Boolean End Function <DllImport("user32.dll")> _ Private Shared Function ShowWindowAsync(hWnd As IntPtr, nCmdShow As Integer) As _ <MarshalAs(UnmanagedType.Bool)> Boolean End Function Private Const SW_SHOWNORMAL As Integer = 1 Private Const SW_SHOW As Integer = 5 Private Const SW_RESTORE As Integer = 9 <DllImport("user32.dll")> _ Private Shared Function IsIconic(hWnd As IntPtr) As _ <MarshalAs(UnmanagedType.Bool)> Boolean End Function <DllImport("user32.dll")> _ Private Shared Function GetWindowThreadProcessId(hWnd As IntPtr, _ ProcessId As IntPtr) As UInteger End Function <DllImport("kernel32.dll")> _ Private Shared Function GetCurrentThreadId() As UInteger End Function <DllImport("user32.dll")> _ Private Shared Function AttachThreadInput( _ idAttach As UInteger, idAttachTo As UInteger, fAttach As Boolean) As _ <MarshalAs(UnmanagedType.Bool)> Boolean End Function <DllImport("user32.dll", EntryPoint:="SystemParametersInfo", _ SetLastError:=True)> _ Private Shared Function SystemParametersInfoGet(action As UInteger, _ param As UInteger, ByRef vparam As UInteger, init As UInteger) As _ <MarshalAs(UnmanagedType.Bool)> Boolean End Function <DllImport("user32.dll", EntryPoint:="SystemParametersInfo", _ SetLastError:=True)> _ Private Shared Function SystemParametersInfoSet(action As UInteger, _ param As UInteger, vparam As UInteger, init As UInteger) As _ <MarshalAs(UnmanagedType.Bool)> Boolean End Function Private Const SPI_GETFOREGROUNDLOCKTIMEOUT As UInteger = &H2000 Private Const SPI_SETFOREGROUNDLOCKTIMEOUT As UInteger = &H2001 Private Const SPIF_UPDATEINIFILE As UInteger = &H1 Private Const SPIF_SENDCHANGE As UInteger = &H2
//using System.Runtime.InteropServices; /// <summary> /// できるだけ確実に指定したウィンドウをフォアグラウンドにする /// </summary> /// <param name="hWnd">ウィンドウハンドル</param> public static void ActiveWindow(IntPtr hWnd) { if (hWnd == IntPtr.Zero) { return; } //ウィンドウが最小化されている場合は元に戻す if (IsIconic(hWnd)) { ShowWindowAsync(hWnd, SW_RESTORE); } //AttachThreadInputの準備 //フォアグラウンドウィンドウのハンドルを取得 IntPtr forehWnd=GetForegroundWindow(); if (forehWnd == hWnd) { return; } //フォアグラウンドのスレッドIDを取得 uint foreThread = GetWindowThreadProcessId(forehWnd, IntPtr.Zero); //自分のスレッドIDを収得 uint thisThread = GetCurrentThreadId(); uint timeout = 200000; if (foreThread != thisThread) { //ForegroundLockTimeoutの現在の設定を取得 //Visual Studio 2010, 2012起動後は、レジストリと違う値を返す SystemParametersInfoGet(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, ref timeout, 0); //レジストリから取得する場合 //timeout = (uint)Microsoft.Win32.Registry.GetValue( // @"HKEY_CURRENT_USER\Control Panel\Desktop", // "ForegroundLockTimeout", 200000); //ForegroundLockTimeoutの値を0にする //(SPIF_UPDATEINIFILE | SPIF_SENDCHANGE)を使いたいが、 // timeoutがレジストリと違う値だと戻せなくなるので使わない SystemParametersInfoSet(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, 0); //入力処理機構にアタッチする AttachThreadInput(thisThread, foreThread, true); } //ウィンドウをフォアグラウンドにする処理 SetForegroundWindow(hWnd); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS); BringWindowToTop(hWnd); ShowWindowAsync(hWnd, SW_SHOW); SetFocus(hWnd); if (foreThread != thisThread) { //ForegroundLockTimeoutの値を元に戻す //ここでも(SPIF_UPDATEINIFILE | SPIF_SENDCHANGE)は使わない SystemParametersInfoSet(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, timeout, 0); //デタッチ AttachThreadInput(thisThread, foreThread, false); } } [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool BringWindowToTop(IntPtr hWnd); [DllImport("user32.dll")] static extern IntPtr SetFocus(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, int uFlags); private const int SWP_NOSIZE = 0x0001; private const int SWP_NOMOVE = 0x0002; private const int SWP_NOZORDER = 0x0004; private const int SWP_SHOWWINDOW = 0x0040; private const int SWP_ASYNCWINDOWPOS = 0x4000; private const int HWND_TOP = 0; private const int HWND_BOTTOM = 1; private const int HWND_TOPMOST = -1; private const int HWND_NOTOPMOST = -2; [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); private const int SW_SHOWNORMAL = 1; private const int SW_SHOW = 5; private const int SW_RESTORE = 9; [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool IsIconic(IntPtr hWnd); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId( IntPtr hWnd, IntPtr ProcessId); [DllImport("kernel32.dll")] private static extern uint GetCurrentThreadId(); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool AttachThreadInput( uint idAttach, uint idAttachTo, bool fAttach); [DllImport("user32.dll", EntryPoint = "SystemParametersInfo", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SystemParametersInfoGet( uint action, uint param, ref uint vparam, uint init); [DllImport("user32.dll", EntryPoint = "SystemParametersInfo", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SystemParametersInfoSet( uint action, uint param, uint vparam, uint init); private const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000; private const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001; private const uint SPIF_UPDATEINIFILE = 0x01; private const uint SPIF_SENDCHANGE = 0x02;
注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。