Create a Intra-Application Named Pipe using WCF in .Net using C#; IPC Communications.

Posted by OmegaMan at August 19, 2007

Category: How To, WCF, Web Services

Tags: , , ,

Update 07/2009: Update to not use HTTP transport methodology but NetNamedPipeBinding. Updated downloadable code

Update 10/2008: Code downloadable…see comments for link.
A named pipe is a communications method between two disparate applications. This article will show how to use WCF and .Net 3+ with C# to create such named pipe(s). This article is a how-to article showing every step needed and has the code downloadable.
Premise
I have a need to have one application to provide a messages to another application (InterProcess Communication (IPC)) using a unique named pipe between them using WCF.

Named Pipe Library

We will be creating three projects. The first will be a library project (shown below as NamedPipe) and two test projects (not shown in the picture below). The test projects will be the consumers of the library which will be a receiver and a sender talking to each other. The Library will contain the guts of the operations in WCF and once done will look like this in VS2008 Solution Explorer:
image
This library will be all encompassing and contain the code necessary to perform the named pipe between applications. It handles all the core WCF functionality setup/endpoints. All one has to do is have the receiver and sender use the library to communicate by just specifying a name for the pipe.
The receiver and sender will simply consume the classes in Receiver.cs and Sender.cs respectively to achieve the named pipe communication.
Steps:
  1. Create a library project named NamedPipe.
  2. Add two references to the System namespaces Runtime.Serialization and ServiceModel.
  3. Create a folder within the project named Communication, which will be a sub-namespace to NamedPipe. We will place the low level operations and specifications there.
  4. Within the Communication folder add an interface class named IPipeService (as iPipeService.cs in the downloadable code). Within that file is where we will begin to define the service contract which the sending and receive code will adhere to. The interface code will look like this:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ServiceModel;
    
    namespace NamedPipe.Communication
    {
     [ServiceContract
      (Namespace =
      "http://www.OmegaCoder.com/NamedPipe1")]
     public interface IPipeService
     {
      [OperationContract]
      void PipeIn(List<string> data);
     }
    }
    • Line 04: We will need to add this using so our attributes will be recognized on the interface.
    • Line 08: This is the attribute which will show our interface to be a specialized WCF contract.
    • Line 10: We setup a generic namespace to avoid naming collisions with other WCF services. This namespace does not have anything to do with opening up the ports, which will have a different URL altogether. This url is kind of a throw away url, we are not publishing to this location.
    • Line 13: We will be opening up one port which we will communicate between the processes and this attribute defines that.
    • Line 14: We will be passing a list of strings between the processes and this method is the vector of the operation.
  5. Again within the Communication folder create the second part of the contract, the actual class which will be the catalyst for the data transfer on the receiving side named PipeService (as PipeService.cs in the download code). Let me show the code and explain each line:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ServiceModel;
    
    namespace NamedPipe.Communication
    {
    
    [ServiceBehavior
            (InstanceContextMode = InstanceContextMode.Single)]
     public class PipeService : IPipeService
     {
         public static string URI
             = "net.pipe://localhost/Pipe";
    
         // This is when we used the HTTP bindings.
         // = "http://localhost:8000/Pipe";
    
         #region IPipeService Members
    
         public void PipeIn(List<string> data)
         {
             if (DataReady != null)
                 DataReady(data);
         }
    
         public delegate void DataIsReady(List<string> hotData);
         public DataIsReady DataReady = null;
    
         #endregion
     }
    }
    • Line 04: This is the required using for WCF operations.
    • Line 09: This attribute is self explanatory, for this class will be generated for each endpoint we define. In our case only one endpoint for our pipe.
    • Line 10: This is not the default behavior. In standard WCF processing this object would be created through reflection. We don’t want that to happen, because we have a delegate (call back) which will transfer data to the receiver. Due to that fact, we will be creating the object by hand later and allowing the consumer to subscribe to the delegate. Because of that, we have to specify that this context will be a singleton instance.
    • Line 14: Here is the actual url and port which the operations will be operating on. The named part of the port will be added to this string to give us a unique pipe.  If we want to expand this to other machines on the intranet, we could use an HTTP protocol as shown below in Line 17.
    • Line 20: Here is where the data will be passed in/out.
    • Line 22: Check for a subscriber to the delegate.
    • Line 23: Finally pass the data on to the consumer(s) of the delegate after we have received data from the ether.
    • Line 25/26: Here is where we will transfer the data back and forth by allowing a consumer to subscribe to this delegate.

Receiver Class

  1. Now we will create the top level receiver class (as Receiver.cs in the zip) which will be consumed by the target of the named pipe. That class is created in the top level of the NamedPipe namespace. Here is the code and the line by line explanation.
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using NamedPipe.Communication;

namespace NamedPipe
{
 public class Receiver :  IDisposable
 {

     #region Constructors
     // Default constructor, use this if
     // the default name of the pipe can
     // be used instead of one provided.
     public Receiver() : this(DefaultPipeName) { }

     // Use this constructor to create
     // a pipe with a unique name.
     public Receiver(string PipeName)
     {
         _PipeName = PipeName;
     }
#endregion

     #region Public Operations
     // If the service host can be started,
     // true will be returned. If false is returned
     // another process owns the pipe.
     public bool ServiceOn()
     {
         return ( _operational = HostThisService());
     }

     public void ServiceOff()
     {
      if (_host != null)
          if (_host.State != CommunicationState.Closed)
              _host.Close();

      _operational = false;

     }
#endregion

     #region WCF Operations
    // The thread which will listen for communications
    // from the other side of the pipe.
    private bool HostThisService()
    {

     try
     {

         _host = new ServiceHost(_ps, new Uri(PipeService.URI));

         // Usage BasicHttpBinding can be used if this is
         // not going to be on the local machine.
         _host.AddServiceEndpoint( typeof( IPipeService ),
              new NetNamedPipeBinding(),
              _PipeName );
         _host.Open();

         _operational = true;
     }
     catch (Exception ex)
     {
         error = ex;
         _operational = false;
     }

     return _operational;

    }
#endregion

    #region Public Properties
    // The consumer of this class will subscribe to this delegate to
    // receive data in a callback fashion.
    public PipeService.DataIsReady Data
    {
        get
        {
           return _ps.DataReady;
        }

        set
        {
           _ps.DataReady += value;
        }
    }

    public string CurrentPipeName
    {
        get { return _PipeName; }
    }

    // Any error text will be placed here.
    public Exception error              = null;

    // See the actual name of the pipe for
    // any default operations.
    public const string DefaultPipeName = "Pipe1";
#endregion

#region Private Variables
    private PipeService _ps   = new PipeService();
    private bool _operational = false;
    private ServiceHost _host = null;
    private string _PipeName  = string.Empty;
#endregion

#region IDisposable Members

    // A basic dispose.
    public void Dispose()
    {
        this.ServiceOff();

        if (_ps != null)
            _ps = null;
    }

 #endregion
   }
}
  • Line 04: Even though we have lower level WCF definitions, we will be initiating the WCF functionality in this class.
  • Line 05: Pull in the lower level WCF contract and operations.
  • Line 16: If you know that this is the only named pipe on the system, use a default name for the pipe.
  • Line 20: Otherwise use this constructor to create a named pipe to your specifications.
  • Line 30: Call this after creating the object and after subscribing to the delegate, to start the process of listening. Don’t forget to check for a running status. (Note if the pipe is in use it will throw an exception, so catch any exceptions).
  • Line 35: Turn service off, or it will be done in the dispose automatically.
  • Line 49: This is the function which will do the magic of the WCF port processing.
  • Line 55: The service host will not be creating a generic end point. It will use our singleton pipe service.
  • Line 58: We will open up this URI which is specified in the URI static of PipeService.
  • Line 59: We will have one endpoint and we let it know what type it is.
  • Line 60: We are doing not http communications but netnamed pipe operations.
  • Line 62: Here is the uniqueness of this endpoint. The pipe name.
  • Line 62: Finally we begin listening operations.
  • Line 68: Any errors will be on the public error object for the consumer to check.
  • Line 85: Subscribe to this delegate to pass the information back to the creator of this receiver named pipe.
  • Line 99: Check here for any errors.
  • Line 103: This is the default name of the pipe, if one is not given by the consumer.
  • Line 107: Here we create our singleton WCF service for the one endpoint we will specify.
  • Line 115: Clean up any operations and member variables.

Sender Class

    1. Here is the class which will be consumed by the application which will send the information over. Note this class is not instantiated but used by simply calling the static which will send the data on over:
using System;
using System.Collections.Generic;
using System.Text;

using System.ServiceModel;
using NamedPipe.Communication;

namespace NamedPipe
{
// Create this class to send a message on a
// named pipe.
public class Sender
{
    // Use a default pipe name. Be careful not to reuse
    // this on other applications.
    public static void SendMessage(List<string> messages)
    {
        SendMessage(messages, Receiver.DefaultPipeName);

    }

    // Use this method when we have an actual pipe name.
    public static void SendMessage(List<string> messages, string PipeName)
    {
        EndpointAddress ep
            = new EndpointAddress(
                string.Format("{0}/{1}",
                   PipeService.URI,
                   PipeName));

//      IPipeService proxy = ChannelFactory<IPipeService>.CreateChannel( new BasicHttpBinding(), ep );
        IPipeService proxy = ChannelFactory<IPipeService>.CreateChannel( new NetNamedPipeBinding(), ep );
        proxy.PipeIn( messages );

    }
}
}
  • Line 05: Need to pull in the WCF information to initiate the message.
  • Line 06: We have all the connection contract information which we will need to initiate a message.
  • Line 16: The class and method is a static class so just call this static. No muss no fuss. Note this uses the default named pipe name. If our receiver has specified a name for the pipe do not call this one.
  • Line 21: Call this method with a unique name in for the pipe so a message to us can be sent.
  • Line 23: We will combine the URL and port with the name of the pipe which gives us the uniqueness.
  • Line 29: We create a proxy service to do the transfer and expose the method we need to send the info to the receiver application.
  • Line 30: We send in the list of strings and we are done!

Test Projects

Finally we will create two test projects. A winform which will be the receiver and a console application which will be the sender. All we need to do is reference the Named Pipe Library!

image

Test Receiver

  1. Create a winform project and reference the NamedPipe Project.
  2. Add a richtext box to the form. Here is the code inside:
    using NamedPipe;
    
    public partial class Form1 : Form
    {
        Receiver pipe = new Receiver();
        public Form1()
        {
            InitializeComponent();
            pipe.Data += new NamedPipe.Communication.PipeService.DataIsReady(DataBeingRecieved);
            if (pipe.ServiceOn() == false)
                MessageBox.Show(pipe.error.Message);
    
        }
    
        void DataBeingRecieved(List<string> data)
        {
            this.Invoke(new MethodInvoker(delegate()
            {
                rtb_Status.Text += string.Join(Environment.NewLine, data.ToArray());
                rtb_Status.Text += Environment.NewLine;
            }));
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            if (pipe != null)
                rtb_Status.Text += "Not Null" + Environment.NewLine;
    
            if (pipe.error != null)
                rtb_Status.Text += pipe.error + Environment.NewLine;
    
        }
    }
  • Line 01: We pull in the Named Pipe namespace.
  • Line 05: Since we will be continuously monitoring, we will instantiate the receiver class on the class, so its lifetime is the same as that of the application.
  • Line 09: Here is where we will subscribe to the delegate which will provide us with the text sent from the sender.
  • Line 11: We have gotten an error, show the exception.
  • Line 15: Here is our subscriber target which will receive the data!
  • Line 17: Since the WCF process is on another thread, we invoke back to the GUI thread to populate the richtext box.
  • Line 19: We add the data to the rich text box!
  • Line 24: I added a button, not in the steps to test out the object. This code is optional.

Test Sender

  1. Create a console application and reference the NamedPipe project.
  2. Copy this code:
    static void Main(string[] args)
    {
        List<string> messages = new List<string>();
    
        messages.Add("Line 1");
        messages.Add("Second Line");
        try
        {
            NamedPipe.Sender.SendMessage(messages);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    
    }
  • Line 09: We simply send our message. We are using the default pipe name, so we just use the single argument SendMessage. Otherwise we would use the constructor which would allow us to send data on the unique named pipe!

Summary

With that we are finished. Run the winform receiver first. Then run the console sender. If everything has been coded you should see the text on the screen. With the NamedPipe library it is now a toolbox item which we can take with us from project to project.

image

Share

31 Comments

  1. Martin Ortiz says

    Nice…

    Looking for zip of project. (can’t copy from web page)

    Reply
  2. omegaman says

    Here is the download for the projects

    Download

    WCF
    Reply
  3. Martin Ortiz says

    Sweet…got project files, very nicely done. thanks

    Reply
  4. Mike says

    Great Example! Thanks for posting this.

    There were a couple of issues with the code on the post, but everything worked great with the zipped code.

    Reply
  5. Doug says

    This doesn’t seem to be an implementation of an actual operating system named pipe. As near as I can tell, messages sent using this class will be wrapped in a SOAP envelope and transmitted using http. One of the appealing features of true OS level named pipes is that they are VERY low overhead. I write one byte to the pipe, one byte comes out the other end… no encoding, no extra formatting, etc.

    Reply
  6. omegaman says

    Your analysis is spot on. I would call this a ‘poor’ man’s named pipe. It is only in function only and not in low-level processing only. I wish .Net would make my process irrelevant with a true .net named pipe.

    Reply
  7. Snixtor says

    Couldn’t you just change the binding to NetNamedPipeBinding and the address to suit and it would be using actual named pipes instead of HTTP?

    Reply
  8. sandhya says

    hi this is really good one and helpful

    Reply
  9. Michael says

    Thanks for the great article.

    It seems code for the Receiver class is missing, while code for the Sender appears twice, in both Receiver and Sender sections…

    Reply
  10. omegaman says

    @Michael I have updated the post to use Syntax highlighter, for easier cut and paste. I fixed the missing code in the article to show the actual receiver class. Thanks for pointing that out.

    @Snixtor: You are right to use NetNamedPipeBinding. I am going to be writing a new updated article to show how and why that is better.

    Reply
  11. Roberto Madarang says

    Can I use this on a pre-compiled applications (exe)? Thanks.

    Reply
    • omegaman says

      It can be used on .Net console applications (exe’s).

      Reply
  12. newbie says

    Hi,

    I am a novice C# coder and have reviewed this example closely. It goes a long way to resolving a problem I have. However the limitation from my perspective is that I need to be able to have multiple receivers listening to a single sender. This example allows for only one receiver. (I need real-time publication to several receivers).

    I have looked around a little and the considerations quickly go over my head (transport, technology…).

    My question is, is there a way to extend this solution to enable multiple listeners to the namedpipe?

    tia

    LD

    Reply
  13. omegaman says

    LD/Newbie

    The very nature of WCF precludes a muliple one named pipe scenario because only one process can listen to one port at a time (AFAIK). Hence receiver #2 fails after 1 starts up. So I recommend this to solve your problem:

    1) Create multiple actual named pipes such as pipe1, pipe2..or mypipe41. The example provided uses a default piped named “pipe1”. Look in the code on both the sender and reciever and open up different named pipes besides the default (found on the constructor, the default one chains to another which passes in “Pipe1”). Call the other constructor, in both sender and receiver) and use a different name. Have the receivers open up their specific pipes and have the sender run through all of the pipes sending the info.

    2) Outside of this code example, create a webservice which each of your senders can poll looking for this change of information.

    GL HTH

    Reply
  14. Newbie says

    Thanks. I am working through your suggestions.

    re Webservice: this is a localized app (centic to a piece of hardware)
    and app performance is important. I understand that NamedPipes and TCP.NET are preferable in this regard.

    As I understand it, the communication is uni-directional, in that the sender is the only party that can initiate. (SOA definition?). The implication being that if I need to initiate dialog from both sides I need to set up a sender/receiver at each end. NamedPipes are state-full so I am hoping there is a switch somewhere that this functionality. Any ideas?

    tia

    Reply
  15. Vince says

    This was a great article. Much appreciated.
    About the “You are right to use NetNamedPipeBinding. I am going to be writing a new updated article to show how and why that is better”, were you able to do it ?

    Vince

    Reply
    • omegaman says

      Thanks for reminding me Vince. Look for something over this weekend. 🙂

      Reply
  16. Andy Pybus says

    Re: Intra-Application Named Pipe using WCF in .Net using C#; IPC Communications.

    Is there a zip file of this (the link appears to be broken)?

    Reply
    • OmegaMan says

      Thanks…I recently moved to a new server to host the site and the WordPress transfer utitility didn’t pick up the ancillary files such as zips. Try again.

      Reply
  17. John Maloney says

    Thanks for this, I was running around in circles trying to implement callbacks though Duplex TCP and the waters were getting muddy, I rewrote the code in VB so if you have any requests for that it is done and tested. Thanks again.

    Reply
  18. John says

    Thank you for the great example.
    I managed to make it work but I was wondering how can I make the receiver to send a response back to the sender? Is this possible through the same pipe?

    Something like the following ?
    object result = Sender.SendMessage(messages);

    Thank you

    Reply
  19. Matthew Orr says

    Perhaps I’ve messed up something in my version of your code because I’m not getting a unique pipe.

    I’m trying to send to multiple receivers and get an error:

    Cannot listen on pipe name ‘net.pipe://localhost/Pipe/’ because another pipe endpoint is already listening on that name.

    I have specified a pipe name where indicated in your comments, but it doesn’t seem to be getting passed down to line 14.

    Reply
  20. yogi says

    hi i am getting this error in your application please help…..

    There was no endpoint listening at net.pipe://localhost/Pipe/Pipe1 that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.

    Reply
  21. yogi says

    hi methew orr, try delay by applying sleep in your code which is calling the wcf.eg is shown below.

    ServiceReference5.ServiceClient sc = new Client.ServiceReference5.ServiceClient(“NetNamedPipeBinding_IService”);
    System.Threading.Thread.Sleep(2000);//delay by 2 seconds.
    Console.WriteLine(sc.GetId());//calls the wcf .
    Console.ReadLine();

    Reply
  22. mckay says

    Hi, how can I use the receiver in a console application?
    Here is my code: {Link Removed by Moderator}

    When I run the sender I get this message:
    “There was no endpoint listening at net.pipe://localhost/Pipe/Pipe1 that could accept the message.”

    The Windows Forms receiver works fine.

    Reply
  23. David Thielen says

    This is very well laid out – thank you.

    One question – is WCF now the best way to use named pipes even for the very simple case of two apps that are always on the same system?

    thanks – dave

    ps – The yellow text is very hard to read.

    Reply
  24. David Thielen says

    And I have a second question. Is there a way to have multiple receivers all using the same pipe name? In other words, to have the sender broadcast to all that are interested?

    thanks – dave

    Reply
  25. Emad Alashi says

    I promised to comment on every blog post I find valuable and and helped me solving a problem, and this one did.

    Thanks a lot

    Reply
  26. Ed Alger says

    Thank you for this treatment of WCF!
    I messed around for 2 days trying to understand the “no end point message” I was getting from many of the other samples found on the web. Your code showed me the problem in just 1 hour. Now having the lib, is a plus!
    Thanks,
    Ed…

    Reply
  27. Ed Alger says

    I’ve been able to get a one-way comm from FormA to FormB working OK. Now I’d like to get a two-way comm working, FormA->FormB and FormB->FormA. I referenced your append #14 by omegaman on October 18, 2009 – 6:11 pm. It seems like there’s more involved than simply creating an instance of Receiver pipe = new Receiver(“uniquePipeName”) for each receiver. Doing so, creates a new _hostService. Do I have to somehow preserve the _hostService while letting the new Receiver instance AddNewEndPoint??
    Ed…

    Reply
  28. David Bennington says

    Thank you for posting…this was a quick start to help me resolve a similar problem!

    Reply

Leave a comment

(required)
(required) (will not be published)

This site uses Akismet to reduce spam. Learn how your comment data is processed.