-1

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);
}
akicsike
  • 371
  • 2
  • 8
  • You could send a standard Windows message to the main window of the other process and make the original update its own controls. It's not possible to do from another process. – Alejandro Dec 09 '20 at 16:22
  • 1
    Use the [StartupNextInstance](https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualbasic.applicationservices.windowsformsapplicationbase.startupnextinstance?view=net-5.0) event, as shown in [this C# example](https://stackoverflow.com/a/424427/2330053). – Idle_Mind Dec 09 '20 at 18:07
  • @Alejandro I suspect that a WM_ COPYDATA message would be needed. – Martin James Dec 09 '20 at 18:46
  • @MartinJames That could be a good candidate if a large chunk of data needs to be passed, or a custom one if just a notification suffices. – Alejandro Dec 09 '20 at 21:34

1 Answers1

3

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)