0

I'm trying to do 2 things here:

  • Have only a single application instance running (I'm using the solution mentioned here for it..https://stackoverflow.com/a/522874/5159431) and

  • If another is attempted to be opened, the already open instance will show a balloon tip message or essentially any form control interaction (writing to a text box for example)

This is my modified Program.cs class:

static class Program
{
    static readonly Mutex SingleInstanceMutex = new Mutex(false, "GUID here");
    static TimerForm _mainForm;
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        if (SingleInstanceMutex.WaitOne(TimeSpan.Zero, true))
        {
            try
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                _mainForm = new TimerForm();
                Application.Run(_mainForm);
            }
            finally
            {
                SingleInstanceMutex.ReleaseMutex();
            }
        }
        else
        {
            //Below message box works
            MessageBox.Show("Already running");
            //Below line throws null reference on second instance starting up
            _mainForm.InvokeShowMinimizeBalloonTip();
        }
    }
}

What can I do call a method in the TimerFormfrom the else condition. You can assume InvokeShowMinimizeBalloonTip() simply writes text to a text box on the form.

Community
  • 1
  • 1
Ash
  • 5,786
  • 5
  • 22
  • 42
  • Why must be the existing instance the one that must warn the user? Why not the new one? – Pikoh Feb 21 '17 at 12:39
  • @Pikoh I only want one instance of the application running. Or it might contaminate DB data for another instance. – Ash Feb 21 '17 at 12:41
  • `static` won't share instance between running applications. You will have to use IPC to send messages instead. When running copy receive message it calls method of itself. – Sinatr Feb 21 '17 at 12:41
  • 1
    You have to send a message, use [RegisterWindowMessage](https://msdn.microsoft.com/en-us/library/windows/desktop/ms644947(v=vs.85).aspx) in your app, and in the else block a [PostMessage](https://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx). You have to handle it in `WndProc` of your main form. – Alessandro D'Andria Feb 21 '17 at 12:45
  • Delegates won't work across applications. See [IPC methods](https://en.wikipedia.org/wiki/Inter-process_communication), should give you an idea. I was using custom windows messages to pass mapped file handle (containing data) in past. – Sinatr Feb 21 '17 at 12:55

3 Answers3

1

As my comment this code can help you:

static class Native
{
    [DllImport(ExternDll.USER32, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool PostMessage(
        IntPtr hWnd,
        uint Msg,
        IntPtr wParam,
        IntPtr lParam);

    [DllImport(ExternDll.USER32, CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern uint RegisterWindowMessage(
        string lpString);

    const uint HWND_BROADCAST = 0xFFFFU;
}

static class Program 
{
    public static uint _id;

    static void Main()
    {
        _id = Native.RegisterWindowMessage("Something_ShowInstance");

        if (_id == 0U)
        {
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }

        if (SingleInstanceMutex.WaitOne(TimeSpan.Zero, true))
        {
            // ...
        }
        else
        {
            Native.PostMessage(
                (IntPtr)HWND_BROADCAST,
                _id,
                IntPtr.Zero,
                IntPtr.Zero));
        }
    }
}

In your main form you should implement the code to respond the message:

class MainForm : Form
{
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == Program._id)
        {
            // do something
        }

        base.WndProc(ref m);
    }
}
Alessandro D'Andria
  • 8,663
  • 2
  • 36
  • 32
0

_mainForm in your else block is null because you did not create a Form. Remember that you are still in your second program instance when you enter the else block.

You have to tell your first instance that it should do something. The easiest way would probably be to register a Window Message (RegisterWindowMessage), send the message in your second instance, and listen for this message in your first instance.

However every other IPC method will work for this. The key is to remember that you have two separate processes that need to communicate with each other.

Sylence
  • 2,975
  • 2
  • 24
  • 31
-2

What I think you should use is Singleton software design pattern. It will allow you to have only one instance of TimeForm at a time, so you wont need to check if another instance existst. Furhermore, it will allow you to acces this instance anywhere, in case you would need to call its method, just like in your else.

Read about Singleton here: https://en.wikipedia.org/wiki/Singleton_pattern

Draeggiar
  • 19
  • 7
  • Recommending a Singleton is almost always a bad idea. In this case the single instance will not be shared across the two processes so it won't solve anything. – Sylence Feb 21 '17 at 12:49