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();
}
}