Click here to Skip to main content
15,889,833 members
Articles / Programming Languages / C#

.NET Interprocess Communication Revisited

Rate me:
Please Sign up or sign in to vote.
4.96/5 (30 votes)
4 Jan 2010CPOL4 min read 121.8K   2.3K   111  
The XDMessaging 2.0 library provides an easy-to-use, zero configuration alternative to existing IPC implementations.
/*=============================================================================
*
*	(C) Copyright 2007, Michael Carlisle (mike.carlisle@thecodeking.co.uk)
*
*   http://www.TheCodeKing.co.uk
*  
*	All rights reserved.
*	The code and information is provided "as-is" without waranty of any kind,
*	either expresed or implied. Please do not use commerically without permission.
*
*-----------------------------------------------------------------------------
*	History:
*		11/02/2007	Michael Carlisle				Version 1.0
*		12/12/2009	Michael Carlisle				Version 2.0
*=============================================================================
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using TheCodeKing.Net.Messaging;

namespace TheCodeKing.Demo
{
    /// <summary>
    /// A demo messaging application which demostrates the cross AppDomain Messaging API.
    /// This independent instances of the application to receive and send messages between
    /// each other.
    /// </summary>
    public partial class Messenger : Form
    {
        /// <summary>
        /// The instance used to listen to broadcast messages.
        /// </summary>
        private IXDListener listener;

        /// <summary>
        /// The instance used to broadcast messages on a particular channel.
        /// </summary>
        private IXDBroadcast broadcast;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public Messenger()
        {
            InitializeComponent();
        }
        /// <summary>
        /// The onload event which initializes the messaging API by registering
        /// for the Status and Message channels. This also assigns a delegate for
        /// processing messages received. 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            UpdateDisplayText("Launch multiple instances of this application to demo interprocess communication.\r\n", Color.Gray);

            // set the handle id in the form title
            this.Text += string.Format(" - Window {0}", this.Handle);

            InitializeMode(XDTransportMode.WindowsMessaging);

            // broadcast on the status channel that we have loaded
            broadcast.SendToChannel("Status", string.Format("Window {0} created!", this.Handle));
       }

        /// <summary>
        /// The closing overrride used to broadcast on the status channel that the window is
        /// closing.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);
            broadcast.SendToChannel("Status", string.Format("Window {0} closing!", this.Handle));
        }
        /// <summary>
        /// The delegate which processes all cross AppDomain messages and writes them to screen.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnMessageReceived(object sender, XDMessageEventArgs e)
        {
            if (e.DataGram.Channel == "ChangeMode")
            {
                InitializeMode((XDTransportMode)Enum.Parse(typeof(XDTransportMode), e.DataGram.Message), false);
            }
            else
            {
                // If called from a seperate thread, rejoin so that be can update form elements.
                if (InvokeRequired)
                {
                    try
                    {
                        // onClosing messages may fail if the form is being disposed.
                        Invoke((MethodInvoker)delegate() { UpdateDisplayText(e.DataGram); });
                    }
                    catch { }
                }
                else
                {
                    UpdateDisplayText(e.DataGram);
                }
            }
        }

        /// <summary>
        /// A helper method used to update the Windows Form.
        /// </summary>
        /// <param name="dataGram">dataGram</param>
        private void UpdateDisplayText(DataGram dataGram)
        {
            Color textColor;
            switch (dataGram.Channel.ToLower())
            {
                case "status":
                    textColor = Color.Green;
                    break;
                default:
                    textColor = Color.Blue;
                    break;
            }
            string msg = string.Format("{0}: {1}\r\n", dataGram.Channel, dataGram.Message);
            UpdateDisplayText(msg, textColor);
        }

        /// <summary>
        /// A helper method used to update the Windows Form.
        /// </summary>
        /// <param name="message">The message to be displayed on the form.</param>
        /// <param name="textColor">The colour text to use for the message.</param>
        private void UpdateDisplayText(string message, Color textColor)
        {
            this.displayTextBox.AppendText(message);
            this.displayTextBox.Select(this.displayTextBox.Text.Length - message.Length + 1, this.displayTextBox.Text.Length);
            this.displayTextBox.SelectionColor = textColor;
            this.displayTextBox.Select(this.displayTextBox.Text.Length, this.displayTextBox.Text.Length);
            this.displayTextBox.ScrollToCaret();
        }

        /// <summary>
        /// Sends a user input string on the Message channel. A message is not sent if
        /// the string is empty.
        /// </summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event args.</param>
        private void sendBtn_Click(object sender, EventArgs e)
        {
            SendMessage();
        }

        /// <summary>
        /// Wire up the enter key to submit a message.
        /// </summary>
        /// <param name="m"></param>
        /// <param name="k"></param>
        /// <returns></returns>
        protected override bool ProcessCmdKey(ref Message m, Keys k)
        {
            // allow enter to send message
            if (m.Msg == 256 && k == Keys.Enter)
            {
                SendMessage();
                return true;
            }
            return base.ProcessCmdKey(ref m, k);
        }

        /// <summary>
        /// Helper method for sending message.
        /// </summary>
        private void SendMessage()
        {
            if (this.inputTextBox.Text.Length > 0)
            {
                broadcast.SendToChannel("UserMessage", string.Format("{0}: {1}", this.Handle, this.inputTextBox.Text));
                this.inputTextBox.Text = "";
            }
        }

        /// <summary>
        /// Adds or removes the Message channel from the messaging API. This effects whether messages 
        /// sent on this channel will be received by the application. Status messages are broadcast 
        /// on the Status channel whenever this setting is changed. 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void msgCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if (msgCheckBox.Checked)
            {
                listener.RegisterChannel("UserMessage");
                broadcast.SendToChannel("Status", string.Format("{0}: Registering for UserMessage.", this.Handle));
            }
            else
            {
                listener.UnRegisterChannel("UserMessage");
                broadcast.SendToChannel("Status", string.Format("{0}: UnRegistering for UserMessage.", this.Handle));
            }
        }

        /// <summary>
        /// Adds or removes the Status channel from the messaging API. This effects whether messages 
        /// sent on this channel will be received by the application. Status messages are broadcast 
        /// on the Status channel whenever this setting is changed. 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void statusCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if (statusCheckBox.Checked)
            {
                listener.RegisterChannel("Status");
                broadcast.SendToChannel("Status", string.Format("{0}: Registering for Status.", this.Handle));
            }
            else
            {
                listener.UnRegisterChannel("Status");
                broadcast.SendToChannel("Status", string.Format("{0}: UnRegistering for Status.", this.Handle));
            }
        }

        /// <summary>
        /// Initialize the broadcast and listener mode.
        /// </summary>
        /// <param name="mode">The new mode.</param>
        private void InitializeMode(XDTransportMode mode)
        {
            InitializeMode(mode, true);
        }

        private void InitializeMode(XDTransportMode mode, bool notify)
        {
            if (listener != null)
            {
                // ensure we dispose any previous listeners, dispose should aways be
                // called on IDisposable objects when we are done with it to avoid leaks
                listener.Dispose();
            }

            // creates an instance of the IXDListener object using the given implementation  
            listener = XDListener.CreateListener(mode);

            // attach the message handler
            listener.MessageReceived += new XDListener.XDMessageHandler(OnMessageReceived);

            // register the channels we want to listen on
            if (statusCheckBox.Checked)
            {
                listener.RegisterChannel("Status");
            }
            if (msgCheckBox.Checked)
            {
                listener.RegisterChannel("UserMessage");
            }

            if (broadcast != null)
            {
                broadcast.SendToChannel("Status", string.Format("{0}: Mode changing to {1}", this.Handle, mode));
            }

            // create an instance of IXDBroadcast using the given mode, 
            // note IXDBroadcast does not implement IDisposable
            broadcast = XDBroadcast.CreateBroadcast(mode);
        }

        /// <summary>
        /// On form changed mode.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mode_CheckedChanged(object sender, EventArgs e)
        {
            if (wmRadio.Checked)
            {
                InitializeMode(XDTransportMode.WindowsMessaging);
            }
            else
            {
                InitializeMode(XDTransportMode.IOStream);
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United Kingdom United Kingdom
Mike Carlisle - Technical Architect with over 20 years experience in a wide range of technologies.

@TheCodeKing

Comments and Discussions