Skip to content

WindowsFormsApplicationBase.IsSingleInstance applications are global on a machine, not per user #3715

@zanchey

Description

@zanchey
  • .NET Core Version:
    v5.0.0-preview.7

  • Have you experienced this same bug with .NET Framework?:
    No

Problem description:
The implementation of WindowsFormsApplicationBase.IsSingleInstance from #3200 uses a named pipe as its synchronization and communication tool, but named pipes are in a global namespace (at least on Windows) and thus any assembly can only be running once on any given machine.

Expected behavior:
Able to start this executable in multiple user accounts (under terminal services or fast user switching) at once.

Actual behaviour:
Starting the executable in a second user account produces an uncaught CantStartSingleInstanceException exception.

Strictly speaking this is a potential denial of service issue; by using a named pipe with a predictable name, users can prevent others on the same machine from ever starting the application. Adding the session identifier to the pipe name is enough to make the named pipe session-local, but not enough to prevent others from creating a pipe with the same name.

I don't know what other options there are; mutexes and event handles have a session namespace, but don't allow for data transfer. Local TCP runs into trouble with firewalls (as the old Remoting implementation did). Cursory reading shows that named memory mappings are Windows only, and require marshalling/synchronisation to a much greater degree, though perhaps a memory mapping containing only the randomly-generated named pipe name is the way to go (like WCF).

Minimal repro:
Borrowed from https://stackoverflow.com/a/19326/125549:

public class SingleInstanceApplication : System.Windows.Application
{
    protected override void OnStartup(System.Windows.StartupEventArgs e)
    {
        // Call the OnStartup event on our base class
        base.OnStartup(e);

        // Create our MainWindow and show it
        MainWindow window = new MainWindow();
        window.Show();
    }

    public void Activate()
    {
        // Reactivate the main window
        MainWindow.Activate();
    }
}

public class SingleInstanceManager : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
    private SingleInstanceApplication _application;
    private System.Collections.ObjectModel.ReadOnlyCollection<string> _commandLine;

    public SingleInstanceManager()
    {
        IsSingleInstance = true;
    }

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

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
        // Subsequent launches
        base.OnStartupNextInstance(eventArgs);
        _commandLine = eventArgs.CommandLine;
        _application.Activate();
    }
}

public class EntryPoint
{
    [STAThread]
    public static void Main(string[] args)
    {
        SingleInstanceManager manager = new SingleInstanceManager();
        manager.Run(args);
    }
}

Metadata

Metadata

Labels

🪲 bugProduct bug (most likely)🚧 work in progressWork that is current in progressarea-VisualBasicblocking-migrationAn issue that is preventing the developer from migrating from .NET Framework or earlier .NETtenet-compatibilityIncompatibility with previous versions or with WinForms for .NET Framework

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions