14

Sorry it the title is hard to understand. I wasn't sure how to word it.

I have an application that should only be allowed to run one instance per user session. If the user clicks to launch the application again, I want to bring the one already to focus.

The window will likely have Visibility to collapsed.

If it's visible I know I can use

if (IsIconic(hWnd))
{
    ShowWindowAsync(hWnd, swRestore);
}

SetForegroundWindow(hWnd);

but if the window is collapsed, is there a way for me to bring it back to visible?

Sheridan
  • 68,826
  • 24
  • 143
  • 183
Tsukasa
  • 6,342
  • 16
  • 64
  • 96

2 Answers2

23

You're looking for the Mutex Class. It's pretty complicated, but luckily the Singleton Pattern has been widely discussed. There are several good articles on it, but you can find a good implementation of it in the C# .NET Single Instance Application page on the Sanity Free Coding website. From the linked page:

static class Program {
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            MessageBox.Show("only one instance at a time");
        }
    }
}

Now you're probably wondering how to have a Main method in a WPF Application, right? Well there's a few things that you have to do, but it's not difficult. See the Writing a custom Main() method for WPF applications article which explains this in detail. From that article:

You basically need to change the application’s build action from “Application Definition” to “Page”, create a constructor that calls “InitializeComponent”, and write your Main() by eventually calling one of the application’s “Run” method overloads. ... Don’t forget, also, to remove the “StartupUri” from the App.xaml, otherwise another copy of window will show up (unless you get an error because the URI points to a non existing XAML resource).

So by amalgamating these two resources, we can see that your App.xaml.cs file should look something like this:

public partial class App : Application
{
    private static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    private static MainWindow mainWindow = null;

    App()
    {
        InitializeComponent();
    }

    [STAThread]
    static void Main()
    {
        if(mutex.WaitOne(TimeSpan.Zero, true)) 
        {
            App app = new App();
            mainWindow = new MainWindow();
            app.Run(mainWindow);
            mutex.ReleaseMutex();
        }
        else
        {
            mainWindow.WindowState = WindowState.Normal;
        }
    }
}
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • It seems to me that the above abandons the mutex in the first application instance. From the docs: _"The thread that owns a mutex can request the same mutex in repeated calls to WaitOne without blocking its execution. However, the thread must call the ReleaseMutex method the same number of times to release ownership of the mutex"_. When you create the mutex, if you are the first instance the mutex comes already acquired; then you acquire it again with the call to `WaitOne()`. You need to release it twice if you're going to acquire it twice. – Peter Duniho Dec 05 '15 at 06:07
15

I removed the tag StartupUri from the App.xaml file.

Change the Build Action of App.xaml, from ApplicationDefinition to Page (You can change it on the property window).

Add a reference to Microsoft.VisualBasic (namespace to WindowsFormsApplicationBase).

On the class App.xaml.cs, put this code:

public partial class App : Application
{
    App()
    {
        InitializeComponent();
    }

    [STAThread]
    static void Main()
    {
        SingleInstanceManager manager = new SingleInstanceManager();
        manager.Run(new[] {"teste"});
    }
}

public class SingleInstanceManager : WindowsFormsApplicationBase
{
    SingleInstanceApplication app;

    public SingleInstanceManager()
    {
        this.IsSingleInstance = true;
    }

    protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs e)
    {
        // First time app is launched
        app = new SingleInstanceApplication();
        app.Run();
        return false;
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
        // Subsequent launches
        base.OnStartupNextInstance(eventArgs);
        app.Activate();
    }
}

public class SingleInstanceApplication : Application
{
    protected override void OnStartup(System.Windows.StartupEventArgs e)
    {
        base.OnStartup(e);

        // Create and show the application's main window
        MainWindow window = new MainWindow();
        window.Show();
    }

    public void Activate()
    {
        // Reactivate application's main window

        this.MainWindow.WindowState = WindowState.Normal;
        this.MainWindow.Activate();
    }
}
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
Ricardo França
  • 2,923
  • 2
  • 18
  • 18