43

This is what I have so far:

Dim bProcess = Process.GetProcessesByName("By").FirstOrDefault
If bProcess IsNot Nothing Then
    SwitchToThisWindow(bProcess.MainWindowHandle, True)
Else
    Process.Start("C:\Program Files\B\B.exe")
End If

It has two problems.

  1. Some people have told me that SwitchToThisWindow is deprecated.
  2. If application B is minimized, this function silently fails from the user's perspective.

So what's the right way to do this?

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Jonathan Allen
  • 68,373
  • 70
  • 259
  • 447
  • Does this answer your question? [How do I focus a foreign window?](https://stackoverflow.com/questions/444430/how-do-i-focus-a-foreign-window) – Wai Ha Lee Aug 11 '21 at 11:10

8 Answers8

47

Get the window handle (hwnd), and then use this user32.dll function:

VB.net declaration:

Declare Function SetForegroundWindow Lib "user32.dll" (ByVal hwnd As Integer) As Integer 

C# declaration:

[DllImport("user32.dll")] public static extern int SetForegroundWindow(int hwnd) 

One consideration is that this will not work if the window is minimized, so I've written the following method which also handles this case. Here is the C# code, it should be fairly straight forward to migrate this to VB.

[System.Runtime.InteropServices.DllImport("user32.dll")]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
private static extern bool ShowWindow(IntPtr hWnd, ShowWindowEnum flags);

[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hwnd);

private enum ShowWindowEnum
{
    Hide = 0,
    ShowNormal = 1, ShowMinimized = 2, ShowMaximized = 3,
    Maximize = 3, ShowNormalNoActivate = 4, Show = 5,
    Minimize = 6, ShowMinNoActivate = 7, ShowNoActivate = 8,
    Restore = 9, ShowDefault = 10, ForceMinimized = 11
};

public void BringMainWindowToFront(string processName)
{
    // get the process
    Process bProcess = Process.GetProcessesByName(processName).FirstOrDefault();

    // check if the process is running
    if (bProcess != null)
    {
        // check if the window is hidden / minimized
        if (bProcess.MainWindowHandle == IntPtr.Zero)
        {
            // the window is hidden so try to restore it before setting focus.
            ShowWindow(bProcess.Handle, ShowWindowEnum.Restore);
        }

        // set user the focus to the window
        SetForegroundWindow(bProcess.MainWindowHandle);
    }
    else
    {
        // the process is not running, so start it
        Process.Start(processName);
    }
}

Using that code, it would be as simple as setting the appropriate process variables and calling BringMainWindowToFront("processName");

caesay
  • 16,932
  • 15
  • 95
  • 160
  • Why are declaring it as Integer instead of IntPtr? – Jonathan Allen Feb 23 '10 at 03:52
  • It doesn't seem to work. Can you show me how to use it in conjunction with the code in the original question? – Jonathan Allen Feb 23 '10 at 03:53
  • I'm getting a zero for SetActiveWindow's return value, which indicates an error. But I'm also getting 0 for GetLastWin32Error, which indicates success. Any idea where to look next? – Jonathan Allen Feb 23 '10 at 04:29
  • 1
    For ShowWindow, I need to pass bProcess.MainWindowHandle. What's really frustrating is that your code works on every application except the one I need it for. – Jonathan Allen Feb 23 '10 at 18:23
  • 1
    Int32 as handle will not work correctly on 64-bit. It just *seems* to work, since you're on a 32-bit machine or OS. IntPtr is defined to be of the same size as the bit-ness of the operating system, so running on a 64-bit OS will give you 64-bit IntPtr's. – Lasse V. Karlsen Sep 17 '10 at 10:09
  • Note that `SetActiveWindow()` simply activates a foreground window. If you want to switch focus, use `SetForegroundWindow()` – lambinator Nov 18 '15 at 21:14
  • This only works for me in windows 10. But it does not work on windows 2008 server R2 Standar. Any idea why? – xavendano Mar 17 '17 at 15:34
  • According to the docs, the bProcess.MainWindowHandle will only be 0 if there's zero UI; if it's just minimized this will not trap that case. https://msdn.microsoft.com/en-us/library/system.diagnostics.process.mainwindowhandle(v=vs.110).aspx – dansan Apr 20 '18 at 12:44
  • It does not look like it's working when an app has two or more windows. – Nicke Manarin Feb 16 '20 at 12:27
45

There is another way, which uses the not well-known UI Automation API:

AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
if (element != null)
{
    element.SetFocus();
}

Most of the time, this will work if it's possible to switch to that window. There are a lot of limitations in Windows (security, UAC, specific configuration, etc...) that can prevent you to change the end-user focus.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • +1 For highlighting a little-known technique. Very nice and thanks for expanding my knowledge. – Basic May 02 '11 at 22:52
  • 8
    .NET 4.5: AutomationElement is in UIAutomationClient (in UIAutomationClient.dll) – teynon Jan 20 '14 at 16:31
  • 2
    Worth noting that this won't work if the window has been hidden and doesn't have a task bar icon. (MainWindowHandle will be null) – caesay Mar 15 '17 at 00:13
  • Thanks for the heads-up, @teynon but just for diligence, AutomationElement seems to exist from .NET 3.0 onwards: https://msdn.microsoft.com/en-us/library/system.windows.automation.automationelement(v=vs.110).aspx – Alfie Jul 31 '17 at 12:20
  • 1
    Thanks! Really helped med out :) Works much better than SetForegroundWindow for my use – boomdrak Apr 21 '21 at 12:26
8

Create a New Class in your project and copy-paste the below code in it.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

namespace MyProject
{
    public class ProcessHelper
    {
        public static void SetFocusToExternalApp(string strProcessName)
        {
            Process[] arrProcesses = Process.GetProcessesByName(strProcessName);
            if (arrProcesses.Length > 0)
            {

                IntPtr ipHwnd = arrProcesses[0].MainWindowHandle;
                Thread.Sleep(100);
                SetForegroundWindow(ipHwnd);

            }
        }

    //API-declaration
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool SetForegroundWindow(IntPtr hWnd);

    }
}

Now copy-paste the below code in your required area.

string procName = Process.GetCurrentProcess().ProcessName;
ProcessHelper.SetFocusToExternalApp(procName);

Here you are calling the function to bring focus to the other application's window.

Tijo Tom
  • 487
  • 6
  • 13
5

In VB.Net, you can use the AppActivate function.

Dim App As Process() = Process.GetProcessesByName("program.exe")
If App.Length > 0 Then
   AppActivate(App(0).Id)
End If
Snake
  • 81
  • 1
  • 4
4

I used SetForegroundWindow to make the window from another application appear.

[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);

How can I give another Process focus from C#?

Daniel N
  • 419
  • 8
  • 20
3

This work for me

[DllImport("user32.dll", SetLastError = true)]
static extern void SwitchToThisWindow(IntPtr hWnd, bool turnOn);
[STAThread]
        static void Main()
        {
            Process bProcess = Process.GetProcessesByName(processnamehere).FirstOrDefault() ;
            if (bProcess != null)
                    {
                        SwitchToThisWindow(bProcess.MainWindowHandle, true);
                    }
                GC.Collect();
        }   
Kelin
  • 349
  • 2
  • 5
2

Imports:

Imports System.Runtime.InteropServices

Put this in a Module

<DllImport("user32.dll")> _
Private Function SetForegroundWindow(hWnd As IntPtr) As Boolean
End Function

Public Sub FocusWindow(ByVal ProcessName As String)
    Dim p As System.Diagnostics.Process = System.Diagnostics.Process.GetProcessesByName(ProcessName).FirstOrDefault
    If p IsNot Nothing Then
        SetForegroundWindow(p.MainWindowHandle)
        SendKeys.SendWait("~") ' maximize the application if it's minimized
    End If
End Sub

Usage:

FocusWindow("Notepad")

Source: http://www.codeproject.com/Tips/232649/Setting-Focus-on-an-External-application#_rating

CrazyTim
  • 6,695
  • 6
  • 34
  • 55
1

Following worked for me, when I tried for applications like SQL Server Management Studio and Visual Studio, running as Administrator, using the exe got from the following code.

Toggle Helper

public class ToggleHelper
{
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
    private static extern bool ShowWindow(IntPtr hWnd, EnumForWindow enumVal);

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern int SetForegroundWindow(IntPtr hwnd);

    private enum EnumForWindow
    {
        Hide = 0,
        ShowNormal = 1, ShowMinimized = 2, ShowMaximized = 3,
        Maximize = 3, ShowNormalNoActivate = 4, Show = 5,
        Minimize = 6, ShowMinNoActivate = 7, ShowNoActivate = 8,
        Restore = 9, ShowDefault = 10, ForceMinimized = 11
    };

    public void SetForeGroundMainWindowHandle(string processName)
    {

        Console.WriteLine("SetForeGroundMainWindowHandle Called - for "+ processName);
        Process p = Process.GetProcessesByName(processName).FirstOrDefault();

        if (p != null)
        {
            Console.WriteLine(p.MainWindowHandle.ToString());
            ShowWindow(p.MainWindowHandle, EnumForWindow.Restore);
            System.Threading.Thread.Sleep(5000);
            ShowWindow(p.MainWindowHandle, EnumForWindow.ShowMaximized);
            //System.Threading.Thread.Sleep(5000);
            SetForegroundWindow(p.MainWindowHandle);
        }
        else
        {
            Process.Start(processName);
        }
    }
}

Client

class Program
{
    static void Main(string[] args)
    {
        //RUN AS Administrator
        var y = Process.GetProcesses().Where(pr => pr.MainWindowHandle != IntPtr.Zero);
        foreach (Process proc in y)
        {
            Console.WriteLine(proc.ProcessName);
        }

        ToggleHelper t = new ToggleHelper();

        int a = 0;
        while (a <= 1)
        {
            t.SetForeGroundMainWindowHandle("Ssms");
            System.Threading.Thread.Sleep(30000);
            t.SetForeGroundMainWindowHandle("devenv");
            System.Threading.Thread.Sleep(18000);
        }
        Console.ReadLine();
    }
}
LCJ
  • 22,196
  • 67
  • 260
  • 418