Inter process communication using C# without named pipes
Inter process communication sometimes becomes important. Specially when one has to implement single instance application which can take request at runtime. There are different ways to implement inter process communication. Each options comes with its one limitation and restriction. In this article I have discussed 3 different ways to achieve inter process communication.
1. Using Web API or Self hosted web service
In this technique API is hosted in separate thread. Which can communication with UI or main application using static method exposed. This example is of single instance application, where application check if instance is already running than share parameters with already running instance and close it self.
Limitation: Need process to run as ADMIN or SYSTEM to host API.
Program.cs
static class Program { [STAThread] static void Main() { string[] args = Environment.GetCommandLineArgs(); Process thisProc = Process.GetCurrentProcess(); // Check how many total processes have the same name as the current one if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1) { WebRequest req = WebRequest.Create("http://localhost:19002/Communication/AddItem/" + args[1]); req.Method = "GET"; HttpWebResponse resp = req.GetResponse() as HttpWebResponse; } else { var config = new HttpSelfHostConfiguration("http://localhost:19002"); config.Routes.MapHttpRoute( name: "API", routeTemplate: "{Controller}/{action}/{Item}", defaults: new { item = RouteParameter.Optional }); using(HttpSelfHostServer server = new HttpSelfHostServer(config)) { server.OpenAsync().Wait(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Form1 frm1 = new Form1(); CommonObjects.objRef = frm1; Application.Run(frm1); } } } }
CommunicationController.cs
public class CommunicationController: ApiController { [HttpGet] public string AddItem(string Item) { CommonObjects.objRef.AddItemMethod(Item); return "OK"; } } public class CommonObjects { public static Form1 objRef; }
Form1.cs
public partial class Form1 : Form { public Form1() { InitializeComponent(); } public void AddItemMethod(string ItemName) { this.BeginInvoke((Action)(() => { this.listBox1.Items.Add(ItemName); })); } }
2. Using memory mapped file
Memory mapped files are very easy to implement. Also helps to create communication between multiple processes using common memory file. Where one process write position is read position of other process on same file. In below lecture both method is implemented in same example.
Limitation: Need both process to run under same user account.
process1,cs
public partial class Process1 : Form { public Process1() { InitializeComponent(); } EventLog eventLog = new EventLog(); MemoryMappedFileCommunicator communicator; private void btnSend_Click(object sender, EventArgs e) { var data = System.Text.Encoding.UTF8.GetBytes(txtSendMessage.Text); communicator.Write(data); } private void Process1_Load(object sender, EventArgs e) { communicator = new MemoryMappedFileCommunicator("MemoryMappedShare", 4096); communicator.ReadPosition = 2000; communicator.WritePosition = 0; communicator.DataReceived += new EventHandler<MemoryMappedDataReceivedEventArgs>(communicator_DataReceived); communicator.StartReader(); if (!EventLog.SourceExists("EventShare")) { EventLog.CreateEventSource("EventShare", "EventShare"); } eventLog.Source = "EventShare"; eventLog.EntryWritten += new EntryWrittenEventHandler(LogListner); eventLog.EnableRaisingEvents = true; //communicator.Dispose(); //communicator = null; } private void LogListner(object source, EntryWrittenEventArgs e) { if (e.Entry.Message.StartsWith("Process2:")) { BeginInvoke(new Action(() => { txtReceiveMessage.Text = e.Entry.Message; })); } } private void communicator_DataReceived(object sender, MemoryMappedDataReceivedEventArgs e) { txtReceiveMessage.Text = System.Text.Encoding.UTF8.GetString(e.Data); } private void button1_Click(object sender, EventArgs e) { eventLog.WriteEntry("Process1:OPENED", EventLogEntryType.Information); } }
Process2.cs
public partial class Process2 : Form { private MemoryMappedFileCommunicator communicator; public Process2() { InitializeComponent(); } EventLog eventLog = new EventLog(); private void Process2_Load(object sender, EventArgs e) { communicator = new MemoryMappedFileCommunicator("MemoryMappedShare", 4096); // This process reads data that begins in the position 0 and writes starting from the position 2000. communicator.ReadPosition = 0; communicator.WritePosition = 2000; // Creates an handler for the event that is raised when data are available in the // MemoryMappedFile. communicator.DataReceived += new EventHandler<MemoryMappedDataReceivedEventArgs>(communicator_DataReceived); communicator.StartReader(); if (!EventLog.SourceExists("EventShare")) { EventLog.CreateEventSource("EventShare", "EventShare"); } eventLog.Source = "EventShare"; eventLog.EntryWritten += LogListner; eventLog.EnableRaisingEvents = true; } private void LogListner(object source, EntryWrittenEventArgs e) { if (e.Entry.Message.StartsWith("Process1:")) { BeginInvoke(new Action(()=> { txtReceiveMessage.Text = e.Entry.Message; })); } } private void communicator_DataReceived(object sender, MemoryMappedDataReceivedEventArgs e) { txtReceiveMessage.Text = System.Text.Encoding.UTF8.GetString(e.Data); } private void btnSend_Click(object sender, EventArgs e) { communicator.Write(txtSendMessage.Text); } private void button1_Click(object sender, EventArgs e) { eventLog.WriteEntry("Process2:OPENED", EventLogEntryType.Information); } }
3. Using event viewer log
Using event viewer is best option when processes are running under different user accounts. Also event viewer access should be given to user for use.
Limitation: Doesn't work for GUEST user.
Click to download