takrl
takrl

Reputation: 6482

CantStartSingleInstanceException when trying to start second instance

I'm trying to build a single instance application using the approach outlined here.

The reason I tried going with that solution is that I need to pass on the commandlines from the second attempt to start the app to the first instance, and this seemed the easiest way to accomplish that.

OS flavours I need to support:

I've got it working on all three OS versions, however, I have one machine with Windows 7 32Bit where this crashes with a CantStartSingleInstanceException.

Here's the code:

SingleInstanceController.cs:

using System;
using Microsoft.VisualBasic.ApplicationServices;

namespace SingleInstanceTest
{
  public class SingleInstanceController : WindowsFormsApplicationBase
  {
    public SingleInstanceController()
    {
      IsSingleInstance = true;
    }

    protected override void OnCreateMainForm()
    {
      base.OnCreateMainForm();

      Form1 f = new Form1();
      MainForm = f;

      // process first command line
      f.SetCommandLine(Environment.GetCommandLineArgs());
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
      base.OnStartupNextInstance(eventArgs);

      Form1 f = MainForm as Form1;

      // process subsequent command lines
      f.SetCommandLine(eventArgs.CommandLine);
    }
  }
}

Program.cs:

using System;
using System.Windows.Forms;

namespace SingleInstanceTest
{
  static class Program
  {
    [STAThread]
    static void Main()
    {
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
      Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);

      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      SingleInstanceController si = new SingleInstanceController();

      // This triggers the crash on one machine when starting the
      // app for the second time
      si.Run(Environment.GetCommandLineArgs());
    }

    static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
    {
      // this is triggered with CantStartSingleInstanceException
      MessageBox.Show(e.ToString(),"ThreadException");
      MessageBox.Show(e.Exception.ToString(), "ThreadException");
    }

    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
      MessageBox.Show(e.ToString(), "UnhandledException");
      MessageBox.Show(e.ExceptionObject.ToString(), "UnhandledException");
    }
  }
}

For testing purposes, the form is just a plain form containing a listbox that displays the command line arguments.

Any ideas why this doesn't work on that one machine? I've been fiddling with this for two days now and can't figure it out ...

Upvotes: 4

Views: 1279

Answers (2)

Jochen
Jochen

Reputation: 420

Caution

I ran into this Microsoft.VisualBasic.ApplicationServices.CantStartSingleInstanceException with my app when running on a server with multiple users via remote desktop (e.g. in Citrix environments)

When app is started by a 2nd user, the Microsoft Single Instance pattern tries to connect to existing app instance of the other 1st user on same machine - and fails to connect to 1st instance

It seems to be a MS design pattern issue that

  • only 1 app is allowed per machine
  • instead of per machine and user.

At least, it should be able to be chosen by the developer. But I haven't found any option to configure this behaviour.

Upvotes: 0

Xan-Kun Clark-Davis
Xan-Kun Clark-Davis

Reputation: 2843

I ran into the same Problem, but I don't think it has something to do with Windows 7 or 32bit. In my case it turned out, it was a performance issue. Unfortunately, I can't find the source code of WindowsFormsApplicationBase but it uses network to communicate with the main application, so there might be timeouts involved. It is especially bad, when the main application has to do a lot of network I/O anyways. When the main application does not answer the call to Run fast enough, this exception is thrown.

I solved it by fine tuning the processes, tasks ans threads, so the call gets answered first.

And getting rid of WindowsFormsApplicationBase by using mutexes and proper IPC, where I can actually not only choose the time-out, but also catch any exceptions! Actually, for some sorts of IPC, there isn't even a need for a mutex.

See this fine article for more on that topic: https://www.codeproject.com/Articles/1089841/SingleInstance-NET

The two dirtymost workarounds I choose:

  1. Catching the exception and trying again a couple of milliseconds later.
  2. After some testing, spawning a new thread with a low priority in the base application seems to be a good idea (at least it was in my scenario).

    public void SetCommandLineInThread(string[] args) {
        new Thread(() => {
            SetCommandLine(args);
        }) { IsBackground = true, Priority = ThreadPriority.Lowest }.Start();
    }
    

Note, that I make a copy of the command line args as soon as possible.

var args = e.CommandLine.ToArray();

Upvotes: 2

Related Questions