Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C#

Anonymous Pipes Made Easy

Rate me:
Please Sign up or sign in to vote.
4.97/5 (19 votes)
28 Feb 2022CPOL5 min read 50.5K   2.2K   34   35
A simple and convenient wrapper class for Anonymous Pipes in C# using serialization.
In this example project, you will see how to make using AnonymousPipes class as easy and intuitive as I believe they should be in the first place, and use serialization to pass data between your client and server applications.

 

Introduction

Sometimes, you just need to be able to talk to your application.

It should be that easy, shouldn't it? Let's say you have a few applications running as part of your solution, and you just want your main application to be able to tell the others to shut down. Or maybe you want to start one up, have it run in the background, and then pass back some useful information. Or maybe you have a Windows Service running on the same machine as a desktop application, and you need them to be able to communicate with each other, and you really don't want to (and shouldn't) resort to using TCP/IP.

Anonymous pipes is the solution to these problems. Implementing them can be a little tricky though. The aim of the AnonymousPipes class in this example project is to make using them as easy and intuitive as I believe they should be in the first place.

Background

I've built several TCP/IP communications libraries, and I find myself going to them a lot to solve problems with communication between different applications in my solutions. I sometimes find myself tempted to communicate with different applications on the same machine through these libraries when I shouldn't, because it would be easy... But there really is no reason to go through the network card and drop your message on the network, when it will only be picked up by the same network card and computer but for another application. It's a network security problem as well as needless network bandwidth usage.

The right way to go here is IPC (Interprocess Communication) - Anonymous Pipes in particular (as opposed to NAMED pipes, which I won't discuss here), because we only want communication between running applications on the same PC or server.

There is a bit of setup involved in using Anonymous Pipes, and there is also the niggling limitation that an Anonymous Pipe is a one way communication channel... so if you want TWO way communication between your main and child applications, you need to set up two pipes.

This seemed like a perfect opportunity to write a wrapper class that handles all this setup and detail for me... and when I was done, it occurred to me that I could probably have found what I was looking for here on CodeProject. I was surprised to discover that there isn't (or my search didn't bring one up) an example of Anonymous Pipe usage here for C#.

Using the Code

In the example project, I am simply checking to see if there are any command line arguments when the Windows Forms application starts. If there aren't, then I am assuming that this instance is going to be the pipe SERVER, so I immediately start another instance of the application and pass the two pipe handles (input and output pipes) to the new instance as command line args.

The second instance starts up and discovers that it does indeed have some command line args, so it assumes it is a pipe CLIENT. It reads the first argument which is the two handles of the pipes created by our first instance, and connects to the two pipes.

Below is an example of instantiating the AnonymousPipes wrapper class. I think that may be a little confusing to look at, so I'm going to go into a little detail about this instantiation:

C#
pipe = new AnonymousPipes("Server end of the pipe.", (object msg) => {
   // Text in:
   UI(() => lbTextIn.Items.Add("Object received: " + msg.GetType().ToString() + " (" + msg.ToString() + ")")));
}, ()=> {
   // A client has connected!
   UI(() => tsbStatus.Text = "Client Connected");
   UI(() => tsbStartClient.Enabled = false);
}, () => {
   // A client disconnected!
   UI(() => lbTextIn.Items.Add("Client disconnected at " + DateTime.Now.ToString()));

   // Shut down this AnonymousPipes server:
   pipe.Close();

   // And start it listening again
   // after the disconnection:
   if(!tsbStartServer.Visible) StartAnonymousPipesServer();
}, out handles);

The AnonymousPipes class is instantiated differently depending on who is instantiating it: the pipe SERVER, or the pipe CLIENT.

If the pipe SERVER is instantiating it, then the AnonymousPipes wrapper class has some initial setup to do - including starting the child application and passing it the command line argument containing the handles of the two pipes.

Because it's a wrapper class, and I wanted to make it as reusable as possible, I'm using delegates to get incoming text from the wrapper class.

The AnonymousPipes constructor that is used by the pipe server application looks like this:

C#
public AnonymousPipes(String pipeName, CallBack callback, 
  ConnectionEvent connectionEvent, ConnectionEvent disconnectEvent, out Handles handles)
{
}

The parameters are fairly obvious, I think: pipeName is a name you give this pipe so you can keep track of it if you were to add the AnonymousPipes object to a list. You get the name of this object by calling .GetPipeName().

The callback delegate has the following signature:

C#
public delegate void CallBack(object msg);

This is used to pass incoming text from the pipe to YOU. Any incoming text will arrive this way. You should use a function with the proper signature (something like private void YourFunction(String msg) { }), or an anonymous inline delegate the way I did. In this example project, I'm simply shoving everything that comes in into a listbox so you can see it.

connectionEvent and disconnectEvent are also delegates. They will trigger if your child application disconnect and connection events from the wrapper class. Use this to do any clean up you need to in the eventuality that your child disconnects or shuts down.

In the client application, the wrapper class is instantiated like this:

C#
pipe = new AnonymousPipes("Client end of the pipe.");

if (!pipe.ConnectToPipe(startArgs, (object msg) => {
   UI(() => lbTextIn.Items.Add("Object received:" + msg.GetType().ToString() + " (" + msg.ToString() + ")"));
}, () => {
   // We're disconnected!
   UI(() => tsbStatus.Text = "Disconnected");
   UI(() => tsbConnectToPipe.Enabled = false);

   pipe.Close();
})) {
   // Connection failed!
   UI(() => tsbConnectToPipe.Enabled = false);
   UI(() => tsbStatus.Text = "Connection failed: The current handles are invalid!");
   return;
}

When instantiating the wrapper class in the client, just pass the name. Then call ConnectToPipe(), as seen above. The delegates should be familiar, and work exactly the same way they did in the pipe server instantiation.

Serialization

In this exaple project, I'm once again using the binary formatter for serialization. Using it is easy and streight forward - just drop any .net serializable object into the Send() function. To send your own classes / objects, make sure they exist in both your server and client applications, that they are identical, and that they are both marked as [Serializable].

History

  • 24th March, 2016: Initial version
  • 2/27/22 - Serialization added.

License

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


Written By
President Doxtader Industries LLC
United States United States
I've been in IT for the last 25 years in one capacity or another - always as either a network engineer or a developer... or both. At the moment I have an IT consultancy in Long Island, NY offering software development and network engineer services.

Comments and Discussions

 
QuestionOS? Pin
pggcoding16-Jul-22 22:18
pggcoding16-Jul-22 22:18 
AnswerRe: OS? Pin
pdoxtader27-Jul-22 9:18
professionalpdoxtader27-Jul-22 9:18 
QuestionPerformance Pin
aeastham1-Mar-22 2:54
aeastham1-Mar-22 2:54 
AnswerRe: Performance Pin
pdoxtader1-Mar-22 4:56
professionalpdoxtader1-Mar-22 4:56 
QuestionCool stuff, thank you for! Any chance to exchange objects? Pin
LightTempler25-Feb-22 5:29
LightTempler25-Feb-22 5:29 
AnswerRe: Cool stuff, thank you for! Any chance to exchange objects? Pin
pdoxtader25-Feb-22 7:40
professionalpdoxtader25-Feb-22 7:40 
GeneralRe: Cool stuff, thank you for! Any chance to exchange objects? Pin
LightTempler25-Feb-22 11:38
LightTempler25-Feb-22 11:38 
GeneralRe: Cool stuff, thank you for! Any chance to exchange objects? Pin
pdoxtader27-Feb-22 8:56
professionalpdoxtader27-Feb-22 8:56 
This latest version is using object serialization.
  • Pete

GeneralRe: Cool stuff, thank you for! Any chance to exchange objects? Pin
LightTempler28-Feb-22 4:30
LightTempler28-Feb-22 4:30 
QuestionAnother limitation I just found Pin
Dave S24-Feb-22 2:40
Dave S24-Feb-22 2:40 
AnswerRe: Another limitation I just found Pin
pdoxtader24-Feb-22 3:32
professionalpdoxtader24-Feb-22 3:32 
QuestionCommunication between a Windows Service and a Winforms app Pin
Jaime Stuardo - Chile21-Feb-22 3:03
Jaime Stuardo - Chile21-Feb-22 3:03 
AnswerRe: Communication between a Windows Service and a Winforms app Pin
pdoxtader22-Feb-22 15:54
professionalpdoxtader22-Feb-22 15:54 
QuestionGreat Work! Pin
marjaan30-Jun-20 2:44
marjaan30-Jun-20 2:44 
AnswerRe: Great Work! Pin
pdoxtader22-Jul-20 6:36
professionalpdoxtader22-Jul-20 6:36 
PraiseSimple and straightforward Pin
Z-User17-Oct-18 1:36
Z-User17-Oct-18 1:36 
GeneralRe: Simple and straightforward Pin
pdoxtader28-May-19 4:59
professionalpdoxtader28-May-19 4:59 
QuestionClient/Server Projects Pin
opti9991-Jun-16 7:43
opti9991-Jun-16 7:43 
AnswerRe: Client/Server Projects Pin
pdoxtader1-Jun-16 7:52
professionalpdoxtader1-Jun-16 7:52 
GeneralRe: Client/Server Projects Pin
opti9991-Jun-16 8:05
opti9991-Jun-16 8:05 
GeneralRe: Client/Server Projects Pin
pdoxtader1-Jun-16 9:05
professionalpdoxtader1-Jun-16 9:05 
GeneralRe: Client/Server Projects Pin
pdoxtader1-Jun-16 9:17
professionalpdoxtader1-Jun-16 9:17 
Questionreacting to text received messages Pin
opti9991-Jun-16 2:44
opti9991-Jun-16 2:44 
AnswerRe: reacting to text received messages Pin
pdoxtader1-Jun-16 2:49
professionalpdoxtader1-Jun-16 2:49 
GeneralRe: reacting to text received messages Pin
opti9991-Jun-16 3:12
opti9991-Jun-16 3:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.