0

I am new to C# and IPC, trying to make an app that is single instance and when another process tries to start it wake up already running app instance and pass the args to it. Everything is working according to my needs. But one problem is that I don't know how to set destination handle in program.cs so it only sends the message to the destination app. currently it is broadcasting global and another software (miniget) which also listens to HWND_BROADCAST receive it and wakeup as well. My already running instance is also listens to it. I want to set target handle for (IntPtr)NativeMethods.HWND_BROADCAST

static Mutex mutex = new Mutex(true, "mutex-unique-string-id");
[STAThread]
static void Main(string[] args)
{
    if (mutex.WaitOne(TimeSpan.Zero, true))
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        if (args.Length == 0)
        {
            Application.Run(new MainForm(null));
        }
        else
        {
            Application.Run(new MainForm(args[0]));
        }
        mutex.ReleaseMutex();
    }
    else
    {
        if (args.Length == 0)
        {
            SendMessageClass.SendMessageWithDataUsingHGlobal((IntPtr)NativeMethods.HWND_BROADCAST, "", IntPtr.Zero);
        }
        else
        {
            SendMessageClass.SendMessageWithDataUsingHGlobal((IntPtr)NativeMethods.HWND_BROADCAST, args[0], IntPtr.Zero);
        }
    }
}

SendMessageClass

    public class SendMessageClass
    {

        [System.Runtime.InteropServices.DllImport("user32.dll",CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(System.IntPtr hwnd, int msg,
        IntPtr wparam, IntPtr lparam);

        const int WM_COPYDATA = 0x4A;
        const int WM_CLOSE = 0x0010;

    public static void SendMessageWithDataUsingHGlobal(IntPtr destHandle, string str, IntPtr srcHandle)
    {
        COPYDATASTRUCT cds;

        cds.dwData = srcHandle;
        str = str + '\0';

        cds.cbData = str.Length + 1;
        cds.lpData = Marshal.AllocHGlobal(str.Length);
        cds.lpData = Marshal.StringToHGlobalAnsi(str);
        IntPtr iPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds));
        Marshal.StructureToPtr(cds, iPtr, true);

        // send to the MFC app
        SendMessage(destHandle, WM_COPYDATA, srcHandle, iPtr);

        // Don't forget to free the allocatted memory 
        Marshal.FreeCoTaskMem(cds.lpData);
        Marshal.FreeCoTaskMem(iPtr);
    }
}

WndProc

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            // program receives WM_COPYDATA Message from target app
            case WM_COPYDATA:
                if (m.Msg == WM_COPYDATA)
                {
                    // get the data
                    COPYDATASTRUCT cds = new COPYDATASTRUCT();
                    cds = (COPYDATASTRUCT)Marshal.PtrToStructure(m.LParam, typeof(COPYDATASTRUCT));
                    if (cds.cbData > 0)
                    {
                        byte[] data = new byte[cds.cbData];
                        Marshal.Copy(cds.lpData, data, 0, cds.cbData);
                        Encoding unicodeStr = Encoding.ASCII;
                        MessageBox.Show(new string(unicodeStr.GetChars(data)), "Received");
                    }
                }
                break;
        }
        base.WndProc(ref m);
    }
aadi1295
  • 982
  • 3
  • 19
  • 47
  • You are supposed to register a system-wide unique message ID with [RegisterWindowMessage](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerwindowmessagew). That way, a process can listen to messages specifically with that ID, and ignore all other IDs that it doesn't know. If some other program reacts anyway when receiving an unknown message ID, it's that program's fault. Other than that, you could try to use [EnumWindows](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows) and manually filter relevant target windows. – dialer Apr 17 '20 at 18:09
  • @dialer Thanks for the reply, I am sending data between different instances using `WM_COPYDATA` I have updated the questions with `SendMessageClass` and `WndProc` code. Can you please check and help me. Thanks – aadi1295 Apr 17 '20 at 18:31
  • 2
    https://stackoverflow.com/questions/11923785/run-one-instance-of-program – Hans Passant Apr 17 '20 at 18:38
  • @aadi1295 Better check first if Hans' answer works for you. That would be way easier. And it would get rid of the nasty race condition in your current code too. – dialer Apr 17 '20 at 18:48
  • @HansPassant, Thanks for your comment. I am looking at your answer and I have a question. How to send `args` to `Form1`? – aadi1295 Apr 17 '20 at 18:58
  • 1
    Use `eventArgs.CommandLine`, pass it to a public method of Form1. – Hans Passant Apr 17 '20 at 19:04
  • @HansPassant I made a public method on Form1 which accepts the args and when I try to add in `OnStartupNextInstance` I am getting error `'Form' does not contain a definition for 'Form1_New_Instance_Method' and no accessible extension method 'Form1_New_Instance_Method' accepting a first argument of type 'Form' could be found (are you missing a using directive or an assembly reference?) ` – aadi1295 Apr 17 '20 at 19:36
  • 1
    Cast required, ((Form1)this.MainForm).Foo(eventArgs.CommandLine); – Hans Passant Apr 17 '20 at 19:38
  • @HansPassant Thank you so much for your help and kindness. I was using a messy approach and you changed everything for me. Please post the answer so I can accept it. Thanks. You can simply copy your example from https://stackoverflow.com/questions/11923785/run-one-instance-of-program – aadi1295 Apr 17 '20 at 20:18

0 Answers0