12

I'm working on a Mono application that will run on Linux, Mac, and Windows, and need the ability for apps (on a single os) to send simple string messages to each other.

Specifically, I want a Single Instance Application. If a second instance is attempted to be started, it will instead send a message to the single instance already running.

DBus is out, as I don't want to have that be an additional requirement. Socket communication seems to be hard, as windows seems to not allow permission to connect. Memory Mapped Files seems not to be supported in Mono. Named Pipes appears not to be supported in Mono. IPC seems not to be supported on Mono.

So, is there a simple method to send string messages on a single machine to a server app that works on each os, without requiring permissions, or additional dependencies?

Doug Blank
  • 2,031
  • 18
  • 36
  • I see that you already settled on something, but what exactly was the problem with sockets? Windows wouldn't allow a listener to be started or you couldn't connect to the open socket? Note that you can create sockets that are only open to the localhost and not external to the machine. Windows may require less rights to do that. – Fantius Mar 20 '11 at 23:23
  • One issue, one problem. The issue: I would need to know what socket to use, per user. I could use a ranged hash, but could end up with collision. NamedPipes were not yet implemented in Mono 2.6, as far as I know. The problem: I got an error on windows that the connection was rejected because the host did not accept the connection request (or something to that effect... can't find exact error message). My solution avoids both of these issues and is working fine so far. – Doug Blank Mar 21 '11 at 11:22

4 Answers4

5

On my ubuntu (10.10 mono version: 2.6.7) I've tried using WCF for interprocess communication with BasicHttpBinding, NetTcpBinding and NetNamedPipeBinding. First 2 worked fine, for NetNamedPipeBinding I got an error:

Channel type IDuplexSessionChannel is not supported

when calling ChannelFactory.CreateChannel() method.

I've also tried using Remoting (which is a legacy technology since WCF came out) with IpcChannel; example from this msdn page started and worked without problems on my machine.

I suppose you shouldn't have problems using WCF or Remoting on Windows either, not sure about Mac though, don't have any of those around to test. Let me know if you need any code examples.

hope this helps, regards

serge_gubenko
  • 20,186
  • 2
  • 61
  • 64
  • +1 for WCF basic http binding and so on. Basic http binding was the first piece of WCF implementation in Mono, so it should work fine in most scenarios (even on Mac OS X). But other bindings are still under development IMHO, so bugs should be isolated and reported back to Mono whenever possible. Remoting is obsolete by WCF, so it is not recommended to use it any more in new projects. – Lex Li Mar 20 '11 at 05:12
  • Unfortunately BasicHttpBinding creates problems on Windows with .NET. It requires the user to run netsh command on administrator console (http://social.msdn.microsoft.com/Forums/vstudio/en-US/78b1f2dd-7fa1-4ca4-87e1-d26169948603/systemservicemodeladdressaccessdeniedexception-http-could-not-register-url-http8731url?forum=wcf), which may not always be available to the users in corporate environment. – Sergiy Belozorov Feb 25 '14 at 20:49
  • NetTcpBinding worked both on Mono on Linux and .NET on Windows. Thank you. – Sergiy Belozorov Feb 25 '14 at 21:35
  • @SergiyByelozyorov, HTTP can work for low privilege users, you just have to specify the correct `HostComparisonMode`. See http://stackoverflow.com/a/27882705/138200 – Mitch Feb 21 '15 at 22:26
3

I wrote about this on the mono-dev mailing list. Several general-purpose inter-process messaging systems were considered, including DBus, System.Threading.Mutex class, WCF, Remoting, Named Pipes... The conclusions were basically mono doesn't support Mutex class (works for inter-thread, not for inter-process) and there's nothing platform agnostic available.

I have only been able to imagine three possible solutions. All have their drawbacks. Maybe there's a better solution available, or maybe just better solutions for specific purposes, or maybe there exist some cross-platform 3rd party libraries you could include in your app (I don't know.) But these are the best solutions I've been able to find so far:

  • Open or create a file in a known location, with exclusive lock. (FileShare.None). Each application tries to open the file, do its work, and close the file. If failing to open, Thread.Sleep(1) and try again. This is kind of ghetto, but it works cross-platform to provide inter-process mutex.
  • Sockets. First application listens on localhost, some high numbered port. Second application attempts to listen on that port, fails to open (because some other process already has it) so second process sends a message to the first process, which is already listening on that port.
  • If you have access to a transactional database, or message passing system (sqs, rabbitmq, etc) use it.
  • Of course, you could detect which platform you're on, and then use whatever works on that platform.
Edward Ned Harvey
  • 6,525
  • 5
  • 36
  • 45
1

For your simple reason for needing IPC, I'd look for another solution.

This code is confirmed to work on Linux and Windows. Should work on Mac as well:

    public static IList Processes()
    {
        IList<Process> processes = new List<Process>();
        foreach (System.Diagnostics.Process process in System.Diagnostics.Process.GetProcesses())
        {
            Process p = new Process();
            p.Pid = process.Id;
            p.Name = process.ProcessName;

            processes.Add(p);
        }
        return processes;
    }

Just iterate through the list and look for your own ProcessName.

To send a message to your application, just use MyProcess.StandardInput to write to the applications standard input. This only works assuming your application is a GUI application though.

If you have problems with that, then you could maybe use a specialized "lock" file. Using the FileSystemWatcher class you can check when it changes. This way the second instance could write a message in the file and then the first instance notice that it changes and can read in the contents of the file to get a message.

Earlz
  • 62,085
  • 98
  • 303
  • 499
  • I think that this is the simplest, most robust method. I will need to be to start my single instance with redirectinput enabled, but I think that this is the overall best option. Thanks! – Doug Blank Mar 20 '11 at 12:16
  • Except it seems that I can't reconnect onto the running process's standardinput. That then defeats the purpose. That means I can't use the redirection except from the original process that started the single instance. Am I right? If so, I'm still looking for a solution... – Doug Blank Mar 20 '11 at 13:29
  • @D.S. I'm not sure to be honest, I just connected a few classes. I've not tried this method myself. – Earlz Mar 20 '11 at 16:08
  • @D.S. To address your problems with the process' standard input, look at my added suggestion of using the FileSystemWatcher class to implement your own simplistic IPC – Earlz Mar 20 '11 at 23:20
1

THIS ANSWER DOESN'T WORK FOR ALL PLATFORMS

Solved my problem with two techniques: a named mutex (so that the app can be run on the same machine by different users), and a watcher on a message file. The file is opened and written to for communication. Here is a basic solution, written in IronPython 2.6:

(mutex, locked) = System.Threading.Mutex(True, "MyApp/%s" % System.Environment.UserName, None)
if locked:
    watcher = System.IO.FileSystemWatcher()
    watcher.Path = path_to_user_dir
    watcher.Filter = "messages"
    watcher.NotifyFilter = System.IO.NotifyFilters.LastWrite
    watcher.Changed += handleMessages
    watcher.EnableRaisingEvents = True
else:
    messages = os.path.join(path_to_user_dir, "messages")
    fp = file(messages, "a")
    fp.write(command)
    fp.close()
    sys.exit(0)
Doug Blank
  • 2,031
  • 18
  • 36
  • Unfortunately, this should not be considered an answer, for two reasons: System.Threading.Mutex can be used for synchronization on windows inter-process, and I don't know about linux, but on mac osx, it can only be used for inter-thread synchronization. Doesn't work for inter-process. Similarly, FileSystemWatcher works perfectly on windows and linux, but not on mac osx, solaris, or other kqueue-based unix systems. – Edward Ned Harvey Jul 02 '14 at 20:25
  • You are correct. I found that this didn't work on all platforms. Still looking for a solution. – Doug Blank Jul 03 '14 at 21:22
  • You can un-mark it as an answer, can't you? This was the page I found when I was looking for a similar answer, so the "answered" answer mis-led me for a moment until I discovered it didn't work. I continued working after that, and I'll write a new answer momentarily... – Edward Ned Harvey Jul 04 '14 at 15:34