0

I've written a program that uses an ApplicationContext to continue running when its forms are closed. The form can be shown again by clicking on a tray icon, but I'd like for it to also show its form when the application is run again instead of running another instance of the program.

I've tried implementing the solution suggested in the question Send message to a Windows process (not its main window) but I can't seem to get the PostMessage to work. I've successfully implemented named pipes instead, but I'm having problems getting the thread that waits for a message to communicate with the ApplicationContext.

I know I've seen other applications that have this feature, so my question is: How is this usually done? All of the solutions I've found seem to work for others, so I must be missing something simple.

Edit:

Apologies for not including an example. I figured out the problem, I was using SynchronizationContext.Current before it was set. I fixed it by constructing my MessageReceiver class after I constructed the form instead of before. Here's my code in case anyone might find it useful.

In the Program class:

    private const byte Activate = 3 << 3;

    [STAThread]
    private static void Main()
        {
        using (var mtx =
            new Mutex(true, "{F6878DF0-C021-4B4F-934A-7FB03957E09F}",
                      out var createdNew))
            {
            if (createdNew)
                {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new AppContext(Activate));
                }
            else
                {
                //if the program is already running, make a NamedPipeServer
                //and wait for it to connect, then write the Activate message
                using (var pipeServer =
                    new NamedPipeServerStream("testpipe",
                                              PipeDirection.Out))
                    {
                    pipeServer.WaitForConnection();
                    pipeServer.WriteByte(Activate);
                    }
                }
            }
        }

in the ApplicationContext:

private TestForm _form;
private readonly MessageReceiver _receiver;

public AppContext(byte activateMessage)
        {
        _form = new TestForm();
        _receiver = new MessageReceiver(activateMessage);
        _receiver.MessageReceived += OnMessageReceived;
        //tell the receiver to begin its wait thread
        _receiver.BeginWait();
        //make sure the thread stops when the application closes
        ThreadExit += (sender, args) => _receiver.StopWait();
        _form.Show();
        }

private void OnMessageReceived(object sender, EventArgs args)
    {
    //recreate the form if it's been disposed
    if (_form.IsDisposed)
        _form = new TestForm();
    //bring it to the front if it's already open, or
    //show it if it's not open
    if (_form.Visible)
        _form.BringToFront();
    else
        _form.Show();
    }

The MessageReceiver class:

private class MessageReceiver
    {
        public event EventHandler MessageReceived;
        private readonly SynchronizationContext _sync;
        private Thread _waitThread;
        private readonly byte _activate;

        public MessageReceiver(byte activateMessage)
            {
            _sync = SynchronizationContext.Current;
            _activate = activateMessage;
            }

        public void StopWait()
            {
            _waitThread.Abort();
            }

        private void WaitInternal()
            {
            while (true)
                {
                using (var client = new NamedPipeClientStream(
                    ".", "testpipe", PipeDirection.In))
                    {
                    //block until we can connect to the pipe server
                    client.Connect();
                    //read a byte from the stream and check if it's
                    //the activate signal
                    if (client.ReadByte() != _activate) continue;
                    //if so, invoke the MessageRecieved event on the
                    //thread that created the MessageReceiver
                    _sync.Send(
                        state =>
                            MessageReceived?.Invoke(
                                this, EventArgs.Empty), null);
                    }
                }
            }

        public void BeginWait()
            {
            if (_waitThread.ThreadState == ThreadState.Running
             || _waitThread.ThreadState == ThreadState.AbortRequested)
                return;
            _waitThread = new Thread(WaitInternal);
            _waitThread.Start();
            }
    }
mousebyte
  • 61
  • 7
  • Please show your code. We want a [mcve]. – Enigmativity Jul 09 '18 at 03:15
  • Named pipes is a good direction to go. Share the code related to that part and where you met the problems. – PepitoSh Jul 09 '18 at 03:25
  • There are different possibilities. Using a Mutex, Messages, Named Pipes, UI Automation or a combination of some/all the above (also Mutex + Registry Key or RegisterWindowMessage + PostThreadMessage/PostMessage...). You need to show what you're doing and explain where it fails or doesn't work as expected in defined cases. – Jimi Jul 09 '18 at 03:37
  • This code re-invents a feature that is already implemented in the .NET Framework. https://stackoverflow.com/a/29260770/17034 – Hans Passant Jul 09 '18 at 09:41

0 Answers0