5

I've seen all the other questions regarding creating a single instance app using WPF and I've chosen to use the Microsoft's approach as described here: https://codereview.stackexchange.com/a/25667

This is working fine, but now I'd like to using Caliburn.Micro on this application and this code does not play well with caliburn.

How can I have a single instance wpf application using caliburn micro?

The requirement are quite simple: .net 4.5 and only one instance of the application per user session

Thanks

Community
  • 1
  • 1
Adabada
  • 303
  • 3
  • 15
  • On the program startup, you can simply check to see if another process with the same image name is running. If so, just exit the application. – Arian Motamedi Nov 19 '13 at 21:02
  • 1
    @PoweredByOrange thanks, but thats not a very robust approach. I'm currently using a mutex. The question was how to plug that behaviour in calibur.micro – Adabada Nov 19 '13 at 21:10

3 Answers3

5

I use a named mutex in my main method and show a dialog if the mutex already exists.

Check this stack - WPF Single Instance Best Practices

Community
  • 1
  • 1
Gusdor
  • 14,001
  • 2
  • 52
  • 64
  • thanks, but I'm already using mutexes. My qyestion is how to plug that behaviour into caliburn.micro since it uses a bootstrapper to load the application. – Adabada Nov 19 '13 at 21:22
  • Do it before you call the bootstrapper. See _main method_ above. Process level behavior does not require the framework. – Gusdor Nov 19 '13 at 21:29
  • But isn't doing application initialization logic the point of having a bootstrapper? – Adabada Nov 19 '13 at 21:41
  • @Adabada Caliburn.Micro exposes `OnStartup` method that you can override in the bootstrapper. – Patryk Ćwiek Nov 19 '13 at 22:17
  • @PatrykĆwiek so is the onstartup the best place for this kind of stuff? I cannot find any examples of single instance apps using caliburn – Adabada Nov 19 '13 at 23:25
  • @Adabada Try it and see, if it works, it works! The goal on single instance is to prevent competition for shared resources and related side effects. As long as you can make the app close before anything inits, bingo! – Gusdor Nov 20 '13 at 08:18
3

In case anyone is having the same issue, I want to clarify the steps.

First, you have to change what happens in program entry point. As others mentioned, the Main() function that acts as an entry point to WPF programs is auto-generated (App.g.i.cs); So we have to take control of it somehow. As mentioned in this answer, there are several ways to do so. Personally I prefer the Third Approach:

Include another class in your project that defines the Main method as below:

class Startup
{
    [STAThread]
    public static void Main()
    {
        // Your single instance control (shown in below code)
        ...
    }
}

Identify the class whose main you want the application to use as entry point. This can be done via the project properties (right-click on your project >properties. or alt+enter while your project is selected in Solution Explorer). In the Application tab, modify the Startup object properties from the drop down:

Second, you have to decide for a mechanism to know if your program is being run more than once. There are several ways to do that (as the other answers mentioned). The one I prefer is this:

        ...
        // Your single instance control:
        bool firstInstance = true;
        System.Threading.Mutex mutex = new System.Threading.Mutex(true, "some_unique_name_that_only_your_project_will_use", out firstInstance);
        if (firstInstance)
        {
            // Everything that needs to be done in main class, for example:
            YourProject.App app = new YourProject.App();
            app.InitializeComponent();
            app.Run();
        }
        else
        {
            // Your procedure for additional instances of program
            MessageBox.Show("Another instance of this application is already running.");
        }

These two steps together are one of the easiest ways to achieve your goal, even before Caliburn.Micro takes control of your program.

Amir Maleki
  • 313
  • 1
  • 11
0

I had difficulty attempting this in the OnStartup() method. Basically, you want to create a Main method (see No Main() in WPF? ) and wrap the contents using the mutex (see What is a good pattern for using a Global Mutex in C#?)

Mine looked like this:

class SingleGlobalInstance : IDisposable

{



    public bool _hasHandle = false;

    Mutex _mutex;



    private void InitMutex()

    {

        string appGuid = "My App Name"; //((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value;

        string mutexId = string.Format("Global\\{{{0}}}", appGuid);

        _mutex = new Mutex(false, mutexId);



        var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);

        var securitySettings = new MutexSecurity();

        securitySettings.AddAccessRule(allowEveryoneRule);

        _mutex.SetAccessControl(securitySettings);

    }



    public SingleGlobalInstance(int timeOut)

    {

        InitMutex();

        try

        {

            if(timeOut < 0)

                _hasHandle = _mutex.WaitOne(Timeout.Infinite, false);

            else

                _hasHandle = _mutex.WaitOne(timeOut, false);



            if (_hasHandle == false)

            {

                MessageBox.Show("Another instance is already running");

                System.Windows.Application.Current.Shutdown();

            }

        }

        catch (AbandonedMutexException)

        {

            _hasHandle = true;

        }

    }





    public void Dispose()

    {

        if (_mutex != null)

        {

            if (_hasHandle)

                _mutex.ReleaseMutex();

            _mutex.Close();

        }

    }

}

And my App.xaml.cs contained:

    [STAThread]
    public static void Main()
    {
        using (new SingleGlobalInstance(1000))
        {
            var application = new App();
            application.InitializeComponent();
            application.Run();
        }
    }