2

Hellu

The question is simple, yet the answers seem to be various and pretty unclear.

How can I, in an application A, handle events sent by an application B, and how to implements those events in B? Knowing that B has no idea of the existence of A.

Let's say B is a "batch-like" type of program, doing things in the back with no graphical display whatsoever, and A is the nice GUI (pun intended) that should write messages B sends him in a dialog or something.

I found interesting answers about this (like this one or this one), but none actually answers the issue with example for C#; at best, they redirect to some external tools.

I heard it was an issue in C#, but in the end, is there a clean way to do it? Thanks.

If that can ease the thing, let's say A launches B (with Process or something), knowing that B can also be launched alone (in which case, I don't need this whole event handling thing).

Community
  • 1
  • 1
Kilazur
  • 3,089
  • 1
  • 22
  • 48

2 Answers2

9

You could use a combination of MemoryMappedFile, Mutex and EventWaitHandle to manage this between processes.

The following example shows how it would work. In real code, the producer() method would be in a different application from the consumer() method, but this serves to illustrate an implementation:

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        void run()
        {
            Task.Run(() => consumer());
            Task.Run(() => producer());

            Console.WriteLine("Press <ENTER> to stop.");
            Console.ReadLine();
        }

        static void producer()
        {
            using (var mmf = MemoryMappedFile.CreateOrOpen("MyMapName", 1024))
            using (var view = mmf.CreateViewStream())
            {
                var writer = new BinaryWriter(view);
                var signal = new EventWaitHandle(false, EventResetMode.AutoReset, "MyEventName");
                var mutex = new Mutex(false, "MyMutex");

                for (int i = 0; i < 100; ++i)
                {
                    string message = "Message #" + i;

                    mutex.WaitOne();
                    writer.BaseStream.Position = 0;
                    writer.Write(message);
                    signal.Set();
                    mutex.ReleaseMutex();

                    Thread.Sleep(1000);
                }
            }
        }

        static void consumer()
        {
            using (var mmf = MemoryMappedFile.CreateOrOpen("MyMapName", 1024))
            using (var view = mmf.CreateViewStream())
            {
                var reader = new BinaryReader(view);
                var signal = new EventWaitHandle(false, EventResetMode.AutoReset, "MyEventName");
                var mutex = new Mutex(false, "MyMutex");

                while (true)
                {
                    signal.WaitOne();
                    mutex.WaitOne();
                    reader.BaseStream.Position = 0;
                    var message = reader.ReadString();
                    Console.WriteLine("Received message: " + message);
                    mutex.ReleaseMutex();
                }
            }
        }

        static void Main()
        {
            new Program().run();
        }
    }
}

IMPORTANT: This implementation does not use a queue, so it is possible for the consumer to miss messages if it does not process them before a new message arrives.

If you must not miss any messages, you would have to use some kind of queuing implementation instead, for example MSMQ.

However, you could also consider using Windows Communications Foundation, which allows you to call remote methods in another process - but that might require more coupling between processes than you want.

Finally, another possibility is to use a Named Pipe. I actually think this might be the best solution for you.

There's a Microsoft example showing server and client IPC using a named pipe here: http://msdn.microsoft.com/en-us/library/bb546085%28v=vs.110%29.aspx

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • At first glance, I like that, it's clever. I'm going to try it. – Kilazur May 13 '14 at 10:14
  • It is somewhat simplistic and suffers from the major drawback that you could miss events, but it might be the *simplest* solution for you if that isn't a problem. – Matthew Watson May 13 '14 at 10:16
  • My only objective is to notify a user of the state of my background action, if he launched it with the GUI app. Missing a message is, I guess, no big deal, since if the consumer didn't have enough time to display it, the user would probably not have the time to read it :p – Kilazur May 13 '14 at 10:21
  • I have a question: so if I launch my producer process without the consumer one, won't it produces unexpected issues about signal not getting received or something? – Kilazur May 13 '14 at 10:24
  • 1
    @Kilazur No, the messages will just be ignored (you can test this by commenting out the `Task.Run(() => consumer());` in my sample program.) – Matthew Watson May 13 '14 at 10:28
  • Perfect then, gonna be accepted as soon as I tested it in my program. Though I don't have any `Task.Run` method, settling with `new Task(Action).Start();` – Kilazur May 13 '14 at 10:41
  • exactly what I was looking for http://stackoverflow.com/questions/35432877/shard-memory-ipc-triggers-the-eventhandler-on-console-fails-on-wpf-application – PblicHieip Hag Ana Feb 16 '16 at 13:42
0

depends on how complex your scenario would get. You could use Interprocess communication or even some simple file (or db table) which would be written/read by the 2 apps (you'll need to sync access to it).

AndreiC
  • 1,490
  • 4
  • 16
  • 34
  • Oh, the scenario couldn't be simpler. In the ideal scenario, B has a property `public string Message` that fires a OnMessageChanged event when set'd. – Kilazur May 13 '14 at 09:44
  • ok, but how do you plan to subscribe to an event triggered by app B in app A? as far as I know, you can't. – AndreiC May 13 '14 at 11:46
  • I did, his example looks great and I'm probably gonna use it. Though it doesn't work as I wanted it to, I guess there's no way to do it this way. – Kilazur May 13 '14 at 11:49