0

I created a program intended to run a single instance, with a form that is hidden until a broadcast message is received. The bug is that the message is not received unless the form is shown on creation. Why is it necessary to show the form at this stage? I have knocked an example together. The first running instance of the program creates the form, further instances broadcast a message to it.

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace HiddenProgram
{
public class Program : ApplicationContext
{
    [DllImport("user32")]
    static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

    [DllImport("user32")]
    static extern int RegisterWindowMessage(string message);

    const int HWND_BROADCAST = 0xffff;

    public static readonly int WM_SHOWME = Program.RegisterWindowMessage("com.holroyd.Gateway.Show");

    public static Program Instance;
    static Mutex mutex = new Mutex(true, "{9BFB3652-CCE9-42A2-8CDE-BBC40A0F5213}");

    MyForm form;

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        if (!mutex.WaitOne(TimeSpan.Zero, true))
        {
            // Show the single instance window
            PostMessage(
                (IntPtr)HWND_BROADCAST,
                WM_SHOWME,
                IntPtr.Zero,
                IntPtr.Zero);
        }
        else
        {
            // Create the hidden window
            Instance = new Program();
            Application.Run(Instance);
            mutex.ReleaseMutex();
        }
    }

    Program()
    {
        form = new MyForm();
        form.FormClosing += form_FormClosing;

        // One of the following two seem necessary to get the broadcast message
        form.Show();
        //MainForm = form;
    }

    void form_FormClosing(object sender, FormClosingEventArgs e)
    {
        ExitThread();
    }
}

public class MyForm : Form
{
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == Program.WM_SHOWME)
            Visible = !Visible;
        else
            base.WndProc(ref m);
    }
}
}
  • It seems like part of .Show is registering to receive these events. Would it be possible to have the hidden program show a splash screen for a few nanoseconds? Then it's technically finished it's .Show at least once, and hides afterwards. – Davesoft Aug 03 '18 at 11:30
  • Nice repro code. You found out that Show() was necessary because winforms tries to be lazy about creating the native window. It only does so when it must, standard .net style. Only when the native window is created can you get the message. Other ways to do it is to call CreateHandle() in the form class constructor, retrieve the Handle property, and using [this code](https://stackoverflow.com/questions/3742709/this-visible-is-not-working-in-windows-forms/3742980#3742980) to ensure that the Show() call doesn't actually make the window visible. – Hans Passant Aug 03 '18 at 12:54
  • Thanks Hans, this seems to be the correct explanation, and just adding a form constructor that calls CreateHandle() fixed the problem in this example – Adrian Savage Aug 03 '18 at 14:02

1 Answers1

1

To create a form that remains hidden until a broadcast message is received, CreateHandle() is called from the form's constructor, in order to create the underlying window so that broadcast messages from other instances of the program are received.

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace HiddenProgram
{
public class Program : ApplicationContext
{
    [DllImport("user32")]
    static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

    [DllImport("user32")]
    static extern int RegisterWindowMessage(string message);

    const int HWND_BROADCAST = 0xffff;

    public static readonly int WM_SHOWME = Program.RegisterWindowMessage("com.holroyd.Gateway.Show");

    static Mutex mutex = new Mutex(true, "{9BFB3652-CCE9-42A2-8CDE-BBC40A0F5213}");

    MyForm form;

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        if (!mutex.WaitOne(TimeSpan.Zero, true))
        {
            // Show the single instance window
            PostMessage(
                (IntPtr)HWND_BROADCAST,
                WM_SHOWME,
                IntPtr.Zero,
                IntPtr.Zero);
        }
        else
        {
            // Create the hidden window
            Application.Run(new Program());
            mutex.ReleaseMutex();
        }
    }

    Program()
    {
        form = new MyForm();
        form.FormClosing += form_FormClosing;
    }

    void form_FormClosing(object sender, FormClosingEventArgs e)
    {
        ExitThread();
    }
}

public class MyForm : Form
{
    public MyForm()
    {
        CreateHandle();
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == Program.WM_SHOWME)
            Visible = !Visible;
        else
            base.WndProc(ref m);
    }
}
}