20

I have an application which may only have one instance of itself open at a time. To enforce this, I use this code:

System.Diagnostics.Process[] myProcesses = System.Diagnostics.Process.GetProcesses();
System.Diagnostics.Process me = System.Diagnostics.Process.GetCurrentProcess();
foreach (System.Diagnostics.Process p in myProcesses)
{
    if (p.ProcessName == me.ProcessName)
        if (p.Id != me.Id)
        {
            //if already running, abort this copy.
            return;
        }
}
//launch the application.
//...

It works fine. I would also like it to be able to focus the form of the already-running copy. That is, before returning, I want to bring the other instance of this application into the foreground.

How do I do that?

SetForegroundWindow works, to a point:

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

// ...
if (p.Id != me.Id)
{
    //if already running, focus it, and then abort this copy.
    SetForegroundWindow(p.MainWindowHandle);
    return;
}
// ...

This does bring the window to the foreground if it is not minimized. Awesome. If the window IS minimized, however, it remains minimized.

It needs to un-minimize.

Solution via SwitchToThisWindow (Works!):

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);

[STAThread]
static void Main()
{
    System.Diagnostics.Process me = System.Diagnostics.Process.GetCurrentProcess();
    System.Diagnostics.Process[] myProcesses = System.Diagnostics.Process.GetProcessesByName(me.ProcessName);
    foreach (System.Diagnostics.Process p in myProcesses)
    {
        if (p.Id != me.Id)
        {
            SwitchToThisWindow(p.MainWindowHandle, true);
            return;
        }
    }
    //now go ahead and start our application ;-)
}
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Jude Allred
  • 10,977
  • 7
  • 28
  • 27
  • 2
    Check to see if the window IsIconic, if so call ShowWindow http://msdn.microsoft.com/en-us/library/ms633527(VS.85).aspx http://msdn.microsoft.com/en-us/library/ms633548(VS.85).aspx – cmsjr Jan 14 '09 at 20:25

7 Answers7

10

I had the same problem and SwitchToThisWindow() worked the best for me. The only limitation is that you must have XP sp1 installed. I played with SetForegroundWindow, ShowWindow, and they both had problems pulling the window into view.

scottm
  • 27,829
  • 22
  • 107
  • 159
  • 2
    Take note, though, that the linked MSDN page says "[This function is not intended for general use. It may be altered or unavailable in subsequent versions of Windows.]" – Tom Juergens Jan 25 '11 at 10:17
7

C# equivalent of Tom Juergens's answer. Works like a charm for me.

private const  int SW_SHOWNORMAL = 1;

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);

[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetForegroundWindow(IntPtr hwnd);

public void SetForeground()
{
    Process[] processes = Process.GetProcessesByName("process name");

    foreach (Process p in processes) {
        ShowWindow(p.MainWindowHandle, SW_SHOWNORMAL);
        SetForegroundWindow(p.MainWindowHandle);
    }
}
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Nigrimmist
  • 10,289
  • 4
  • 52
  • 53
5

Same as OP, I found that SetForegroundWindow alone wasn't enough when the window was minimized. Since I didn't want to use SwitchToThisWindow, I chose ShowWindow followed by SetForegroundWindow.

Works well for me!

private const SW_SHOWNORMAL = 1

<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As integer) As Boolean
End Function

<DllImport("user32.dll", SetLastError:=True)> _
Private Function SetForegroundWindow(ByVal hwnd As IntPtr) As Boolean
End Function

Sub SetForeground()
    Dim processes As Process() = Process.GetProcessesByName("myprocess")

    For Each p as Process in processes
        ShowWindow(p.MainWindowHandle, SW_SHOWNORMAL)
        SetForegroundWindow(p.MainWindowHandle)
    Next
End Sub
Tom Juergens
  • 4,492
  • 3
  • 35
  • 32
2

I believe you will want to use SetForegroundWindow

MSDN Example

cmsjr
  • 56,771
  • 11
  • 70
  • 62
2

Complete Side Note...

You can use

Process.GetProcessesByName(me.ProcessName) 

instead of looping over all the processes running on the system...

UPDATE

PInvoke Rules for this sort of thing...

Restore the Data Dumps
  • 38,967
  • 12
  • 96
  • 122
0

Can you grab MainWindowHandle property of the Process object and send it a WM_USER message that you can interpret as "some other instance wants to bring me to the front".

plinth
  • 48,267
  • 11
  • 78
  • 120
0

It is a very frequent behavior in desktop applications, I regularly have to do this when I create a new WPF application. So I have created a SingletonApp class which inherits from Application :

public class SingletonApp : Application
{
    private static readonly System.Threading.Mutex mutex;
    private static readonly string processName;

    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, int flags);

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

    static SingletonApp()
    {
        processName = Process.GetCurrentProcess().ProcessName;
        mutex = new System.Threading.Mutex(false, $"Local\\{processName}");
    }

    /// <summary>
    /// A base class for application needing to prevent multiple instances
    /// </summary>
    public SingletonApp()
    {
        if (!mutex.WaitOne(0, false))
        {
            // Give focus to existing instance before shutdown
            BringToFront(processName);

            Current.Shutdown();
        }
    }

    public void BringToFront(string processName)
    {
        Process process = Process.GetProcessesByName(processName).FirstOrDefault();

        if (process != null)
        {
            // In case of window is minimized
            ShowWindow(process.MainWindowHandle, 1);     // 1 = Normal

            SetForegroundWindow(process.MainWindowHandle);
        }
    }
}

To use it, you just have to inherit from SingletonApp instead of Application in your App.xaml.cs :

public partial class App : SingletonApp

Don't forget to update App.xaml too :

<utils:SingletonApp x:Class="MyApp.App"
             [...]
             xmlns:utils="clr-namespace:MyApp.Utils"
             Startup="App_OnStartup">

With this it becomes very easy to implement this behavior in every new desktop client.

bN_
  • 772
  • 14
  • 20