Search code examples
c#winformsprocessmultiple-instancessingle-instance

How to update a WinForms control of a running application when another instance of this executable program is launched?


I made single instance app, but my problem is, I don't know how to get the first opened FormMain instance and update the form TextBox! Could you help me?

static void Main(string[] args)
{
    bool result;
    Mutex mutex = new System.Threading.Mutex(true, "unique_name", out result);

    if (!result)
    {
        **/* 
          CALL OPENED FORM INSTANCE AND UPDATE TEXTBOX
         */**

        return;
    }

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(true);
    Application.Run(new FormMain(args));

    GC.KeepAlive(mutex);
}

Solution

  • You can use named pipeline interprocess communication like that:

    Usings

    using System;
    using System.IO.Pipes;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Threading;
    

    Static variables

    static public bool AllowApplicationMultipleInstances { get; private set; } = true;
    
    static private Mutex ApplicationMutex;
    
    static private NamedPipeServerStream IPCServer;
    

    Application unique identifier

    static public string GetGUID()
    {
      object[] attributes = Assembly.GetExecutingAssembly()
                                    .GetCustomAttributes(typeof(GuidAttribute), false);
      return ( (GuidAttribute)attributes[0] ).ToString();
    }
    

    Check only one instance and init the server

    static private bool CheckApplicationOnlyOneInstance(AsyncCallback duplicated)
    {
      AllowApplicationMultipleInstances = false;
      string guid = GetGUID();
      ApplicationMutex = new Mutex(true, guid, out bool created);
      if ( created )
        CreateIPCServer(duplicated);
      else
      {
        var client = new NamedPipeClientStream(".", guid, PipeDirection.InOut);
        client.Connect();
        new BinaryFormatter().Serialize(client, "BringToFront");
        client.Close();
      }
      return created;
    }
    

    Create the server

    static private void CreateIPCServer(AsyncCallback duplicated)
    {
      IPCServer = new NamedPipeServerStream(GetGUID(),
                                            PipeDirection.InOut,
                                            1,
                                            PipeTransmissionMode.Message,
                                            PipeOptions.Asynchronous);
      IPCServer.BeginWaitForConnection(duplicated, IPCServer);
    }
    

    Anwser to a request

    static private void IPCRequest(IAsyncResult ar)
    {
      var server = ar.AsyncState as NamedPipeServerStream;
      server.EndWaitForConnection(ar);
      var command = new BinaryFormatter().Deserialize(server) as string;
      if ( command == "BringToFront" )
      {
        Console.WriteLine(command);
        //MainForm.Instance.SyncUI(() => MainForm.Instance.MenuShowHide_Click(null, null));
      }
      server.Close();
      CreateIPCServer(IPCRequest);
    }
    

    Test

    static private void Test()
    {
      if ( !SystemManager.CheckApplicationOnlyOneInstance(IPCRequest) )
        return;
      Console.ReadKey();
    }
    

    Usage

    You can create as string commands as needed.

    For example it allows to pass command line arguments from a process just started to the actual running process.

    Also you can improve this simple behavior to have a more complex system, to use a classes framework instead of string commands.

    For your application you should be able to use:

    static public FormMain MainForm;
    
    static void Main(string[] args)
    {
      if ( !SystemManager.CheckApplicationOnlyOneInstance(IPCRequest) )
        return;
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(true);
      MainForm = new FormMain(args);
      Application.Run(MainForm);
    }
    

    If you modify a WinForm control you must to synchronize with the main UI thread:

    How do I update the GUI from another thread?

    How to access a WinForms control from another thread i.e. synchronize with the GUI thread?

    Some links

    PipeStream

    Full Duplex Asynchronous Read/Write with Named Pipes (CodeProject)

    Inter Process Communication (C# Vault)

    WCF Comparison with Web Services and .NET Remoting (CodeProject)

    Socket Programming In C# (C-SharpCorner)

    Socket Programming in C# (GeeksForGeeks)

    Simple Client-server Interactions using C# (CodeProject)