Interprocess Communication using Named Pipes in C#

Skill

Interprocess Communication using Named Pipes in C#

Posted in:

Named pipes are a great way to communicate between multiple processes. What's makes them even better is that the processes don't have to share the same language. If you'd like detailed information about named pipes, MSDN has a fairly in depth article. This tutorial is going to demonstrate how to implement communication over named pipes using C# and Interop Services.

In Windows, named pipes work almost identically to files. In C++, the functions CreateFile(), ReadFile(), and WriteFile() are all used to communicate over named pipes. In C#, the process got a little more difficult. The first thing we need to understand is that named pipes work on a client/server basis. One process will work as the server and other processes will connect to it. That being said, we'll need code to do both. Let's start with the server.

Instead of jumping through hoops getting C# to natively support named pipes, why don't we just call the Windows API functions directly? Thanks to Interop services, that's exactly what we're going to do. In order to call functions from the Windows API, you'll need to include System.Runtime.InteropServices.

The first function we'll need to call is CreateNamedPipe(). This function will be creating an instance of our named pipe and return a handle to the file for subsequent operations. So, using DLLImport, let's get this function into our C# application.

[DllImport("kernel32.dll", SetLastError = true)]
public static extern SafeFileHandle CreateNamedPipe(
   String pipeName,
   uint dwOpenMode,
   uint dwPipeMode,
   uint nMaxInstances,
   uint nOutBufferSize,
   uint nInBufferSize,
   uint nDefaultTimeOut,
   IntPtr lpSecurityAttributes);

It's not important to understand all the parameters just yet. When we start calling the function, I'll explain what each one does. CreateNamedPipe only initializes the named pipe. Now we need a way to listen for client connections. That's where ConnectNamedPipe() comes in. This function will wait until a client connection has been established.

[DllImport("kernel32.dll", SetLastError = true)]
public static extern int ConnectNamedPipe(
   SafeFileHandle hNamedPipe,
   IntPtr lpOverlapped);

Now that we have our basic functions defined, we can start building our server. Let's start by creating a function that listens for client connections.

public const uint DUPLEX = (0x00000003);
public const uint FILE_FLAG_OVERLAPPED = (0x40000000);

public const string PIPE_NAME = "\\\\.\\pipe\\myNamedPipe";
public const uint BUFFER_SIZE = 4096;

private void Listen()
{
   SafeFileHandle clientPipeHandle;
   while (true)
   {
      clientPipeHandle = CreateNamedPipe(
         PIPE_NAME,
         DUPLEX | FILE_FLAG_OVERLAPPED,
         0,
         255,
         BUFFER_SIZE,
         BUFFER_SIZE,
         0,
         IntPtr.Zero);

      //failed to create named pipe
      if (clientPipeHandle.IsInvalid)
         break;

      int success = ConnectNamedPipe(
         clientPipeHandle,
         IntPtr.Zero);

      //failed to connect client pipe
      if (success != 1)
         break;

      //client connection successfull
      //handle client communication
   }
}

This function constantly waits for clients to connect to our named pipe. It does this by first calling CreateNamedPipe to create an instance of the named pipe. You can refer to MSDN's documentation on CreateNamedPipe for detailed information on the parameters. Let's go through the parameters and the values I chose for each.

pipeName: The name of the named pipe we want to create. In this case, I set it to a constant I defined called PIPE_NAME.

dwOpenMode: This tells the named pipe how we want to communicate over it. You can set this to duplex (both read and write), read only, or write only. I want the server to both read and write to the named pipe, so I set it to PIPE_ACCESS_DUPLEX (3). I also had to set the mode to overlapped, so the pipe could be both read from and written to at the same time.

dwPipeMode: This parameter describes how data will be written to the named pipe. Data can be written as either a stream of bytes or a stream of messages. I chose to write it as a stream of bytes - PIPE_TYPE_BYTE (0).

nMaxInstances: This parameter limits the number of clients that can be simultaneously connected to our named pipe. I didn't want any limit, so I set this value to PIPE_UNLIMITED_INSTANCES (255).

nOutBufferSize and nInBufferSize: These two parameters set the number of bytes to reserve for the input and output buffers. I set them to BUFFER_SIZE which is 4096 bytes.

nDefaultTimeOut: This is how long the code should wait while attempting to create the named pipe. Setting this to 0 means it will wait indefinitely.

lpSecurityAttributes: This parameters defines various security settings for the named pipe. I set this to IntPtr.Zero, which to the Windows API is NULL, so the default security attributes will be used.

CreateNamedPipe returns a handle to the named pipe instance. We need to check and make sure the handle was successfully created. If clientPipeHandle is invalid, then the server failed to create the named pipe. If the handle is anything else, we go on and wait for the client connection. ConnectNamedPipe blocks until a client connection has been established or an error has occurred.

Now that we have client connections, let's see how to read and write to the named pipe. Since named pipes are nearly identical to files, you'd think you could use .NET's FileStream object to communicate over it. If you were to try to create a FileStream object by passing the name of the named pipe as a parameter to the constructor, you'd receive the following exception:

FileStream will not open Win32 devices such as disk partitions and tape drives. Avoid use of "\\.\" in the path.

This is because the .NET framework explicitly restricts access to physical disks through device names using the FileStream object. We can, however, use the SafeFileHandle returned from the CreateNamedPipe call to create our FileStream.

FileStream fStream =
   new FileStream(clientPipeHandle, FileAccess.ReadWrite, BUFFER_SIZE, true);

The first three arguments to the FileStream constructor should be obvious. The last boolean is there to allow asynchronous communication over the pipe. If this were set to false, you would not be able to read and write to the named pipe at the same time. Once you have the FileStream object, you can read and write to the named pipe like you would any other file. Let's see how this is done.

private void Read()
{
   byte[] buffer = new byte[BUFFER_SIZE];
   ASCIIEncoding encoder = new ASCIIEncoding();

   while (true)
   {
      int bytesRead = fStream.Read(buffer, 0, BUFFER_SIZE);

      //could not read from file stream
      if (bytesRead == 0)
         break;

      System.Diagnostics.Debug.WriteLine(
         encoder.GetString(buffer));
    }
}

private void Write(string message)
{
   ASCIIEncoding encoder = new ASCIIEncoding();
   byte[] sendBuffer = encoder.GetBytes(message);
   fStream.Write(sendBuffer, 0, sendBuffer.Length);
   fStream.Flush();
}

The Read function sits in a loop waiting for messages from the client - ideally in its own thread. When a message is received, it simply prints the message to the debug console. The Write function takes a string and converts it to a byte array which is sent to the client.

That's all the code required for the server portion, so how does a client form a connection to the named pipe? Again, we're going to create a FileStream from the named pipe. We're going to need to use another Windows API function, CreateFile, to get our file handle.

[DllImport("kernel32.dll", SetLastError = true)]
public static extern SafeFileHandle CreateFile(
   String pipeName,
   uint dwDesiredAccess,
   uint dwShareMode,
   IntPtr lpSecurityAttributes,
   uint dwCreationDisposition,
   uint dwFlagsAndAttributes,
   IntPtr hTemplate);

Just like ConnectNamedPipe, this function will return a SafeFileHandle that can be passed into the constructor of our FileStream.

uint GENERIC_READ = (0x80000000);
uint GENERIC_WRITE = (0x40000000);
uint OPEN_EXISTING = 3;
uint FILE_FLAG_OVERLAPPED = (0x40000000);

SafeFileHandle pipeHandle =
   CreateFile(
      PIPE_NAME,
      GENERIC_READ | GENERIC_WRITE,
      0,
      IntPtr.Zero,
      OPEN_EXISTING,
      FILE_FLAG_OVERLAPPED,
      IntPtr.Zero);

//could not get a handle to the named pipe
if (pipeHandle.IsInvalid)
   return;

FileStream fStream =
   new FileStream(pipeHandle, FileAccess.ReadWrite, BUFFER_SIZE, true);
Write("Hello Server!");

The above code is all that's required to connect a client to the server and send the message "Hello Server!". You can refer to the MSDN documentation on CreateFile for details on the arguments.

If you have any questions regarding this tutorial, please feel free to leave them in a comment and I will answer them as best I can.

Update: November 11th, 2007

Quite a few people have found the bugs in the original post. It's nice to see an active community finding and fixing each others' code. I left out some pretty important flags that are required for overlapped IO, which many people have discovered causes the program not to run. I have fixed the code snippets above and provided complete working code which you can download and view.

PipeClient.zip and PipeServer.zip

These zip files each contain a Visual Studio 2005 solution with working code and a GUI for testing communication between the client and the server. Hopefully this will help everyone out there and we'll try our hardest not to let broken code slip out like this again.

Michael
09/04/2007 - 19:08

Thanks for publishing this. But I have a question.

Should the CreateFile call be made from both the client and server? With the same parameters? When I make the call on the client it works fine. But when I try the same call, after the blocking return, on the server it returns an invalid pipehandle (ie, pipeHandle.IsInvalid == true). Because of that I started playing around with Win32 Reads and Writes, but FileStream objects are much easier to deal with. But without a valid handle I cannot get a filestream object.

Thanks again.

reply

The Reddest
09/04/2007 - 19:55

On the server, you want to pass the file handle returned from ConnectNamedPipe to the FileStream. I've modified the DLLImports and the code snippets in this tutorial to illustrate that. I apologize for the errors.

reply

Shil
09/05/2007 - 14:30

Can you please upload the entire code? I have typed in the exact same code, but at the client side, it gives a "Access to the path denied" exception (at read). I have no idea whats going on. Please help

reply

Strzala
09/08/2007 - 04:39

I have implemented and consulted with many people, and I have the same problem as shil.

reply

Nico
09/12/2007 - 11:19

Me too ...

reply

Nico
09/12/2007 - 12:08

Ok, It works with:

const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const uint GENERIC_EXECUTE = 0x20000000;
const uint GENERIC_ALL = 0x10000000;
const uint OPEN_EXISTING = 0x00000003;
const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;


m_pipe = CreateFile(PIPE_NAME,
    GENERIC_READ | GENERIC_WRITE, 0,
    IntPtr.Zero, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

reply

The Reddest
11/11/2007 - 19:49

I have posted an update, fixed the code snippets, and provided links to working code that everyone can download. Thanks everyone for helping me find the mistakes!

reply

Pieter Breed
11/07/2007 - 22:53

Hi Brandon,

This article is very informative, thank you very much for writing it.

I have a question about the behavior of the FileStream: Can you write TO the filestream before you've read out all of the data? IE, do you run the risk of overwriting incoming data when you write to the stream when there is unread data still in it?

Regards,
Pieter

reply

The Reddest
11/11/2007 - 20:03

I believe the read and write buffers are separate for a FileStream object, so there's no worry of overwriting the read buffer by writing to it.

reply

Anonymous
04/29/2009 - 04:23

U R Great

reply

AJ
11/08/2007 - 14:25

I tried the code as it is, and the only thing that doesn't work is the FileStream with the given handle. I get the following when it tries to read/write (whichever comes first):

"Handle does not support asynchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened synchronously (that is, it was not opened for overlapped I/O)."

What's going on?

reply

Keith Hutchison
11/22/2007 - 21:33

Many thanks.

reply

Patrick
11/26/2007 - 05:05

It seems, that you forgot to call the DisconnectNamedPipe function. Cause when you close your server window, the listen Thread won't end.

reply

The Reddest
11/26/2007 - 09:16

You're correct. The example apps are just to provide the basic functionality of connecting and sending data - so some error handling and cleanup was left out. I'll modify them soon so the threads shut down when the app is closed.

reply

Nair
12/06/2007 - 16:13

Quick question, I ran both client and server, both connected and was able to send messages, everything is ok. But when I close the client (VS didn't actually close, it kept showing running) and try to send the message from server, VS Studio threw exception at client side. 2 questions
1. Is there a way server would know if a client is diconnected? How can we do that?
2. Why client code doesn't close?

Thanks and it is a very useful article.

Thanks again
nair

reply

The Reddest
12/06/2007 - 19:23

Yep, as I said in a previous comment, the threads don't shut down on the example apps. You can tell when a client disconnects because the server's fStream.Read call will unblock and read 0 bytes.

The client app doesn't close down because simply clicking the X doesn't stop the read thread. You just need to call fStream.Close() when you close the app - then catch the exception that is thrown when fStream attempts to read.

The server doesn't close because the thread listening for client connections isn't stopped.

I haven't gotten around to fixing the example apps, but I'll try to do it soon. Thanks for the comment.

reply

Nair
12/07/2007 - 14:42

Thank you very much for the answer. It make sense.

reply

Nair
12/07/2007 - 15:01

Based on my understanding this is what I did and tell me if this is correct
Client:

internal void Disconnect()
{
  try
  {
    this.stream.Close();
  }
  catch (Exception e1)
  {
    Console.WriteLine(e1.ToString());
  }
}

Questiion, is there a better way of handling this?

Server:

internal void Disconnect()
{
  Server.DisconnectNamedPipe(clientHandle);
}

This comment alone fixed my close process to close properly. Do I still need to stop the thread?

reply

Nair
12/07/2007 - 15:12

Sorry for overposting, but, why do we need to close fstream when it is already closed in Read method at client?
I changed the code at client to

internal void Disconnect()
{
  if (stream != null)
    this.stream.Close();
}

this also worked. When I stepped through, I noticed the stream was null and it didn't execute the close. So I am little confused on why would you need this statement.
Thanks again for the help.

reply

Anonymous
06/20/2008 - 17:41

Just set IsBackground property at true on all threads.

reply

Christoph
01/10/2008 - 09:31

Thx for this simple tutorial.

But how can the client determine that the pipe is empty.
Eg.
The server puts some data every 2 seconds in the pipe.
But the Client polls the type every second.
So there are polls without data.

How can the client see this?

reply

Christoph
01/10/2008 - 10:00

Forget my last question. It was stupid

reply

The Reddest
01/10/2008 - 10:45

Consider it forgotten, but I do want to respond to it just in case other people have the same issue.

First off, the client doesn't poll for data. The pipe's read call is blocking, which means execution will remain there until data is present. The read call returns the number of bytes read after data is received. If the number of bytes returned is zero, then a pipe error has occurred and the connection is finished.

reply

Peter F
01/23/2008 - 16:56

You've got a nice explanation of interprocess communication for C#.

My goal is to do this in .net 2.0 compact framework on CE 5.0. Any suggestions or comments?

reply

The Reddest
01/24/2008 - 10:41

Unfortunately, I haven't programmed under the .NET Compact Framework in a couple of years now and I don't have any experience using CE 5.0. I would assume there is named pipe support on CE, but I can't offer any insight into how to access it.

reply

Dave
04/15/2008 - 11:36

Do you know how to use named pipes over a network? This would be extremely useful for a project I'm working on.

reply

The Reddest
04/15/2008 - 11:44

Named pipes over a network is very simple. Just modify the pipe name to include the computer name or ip address of the computer you wish to connect to.

"\\\\.\\pipe\\myNamedPipe" connects to a pipe on the same machine.

"\\\\remoteComputer\\pipe\\myNamedPipe" connects to a pipe on the computer with the name 'remoteComputer'.

"\\\\192.168.1.101\\pipe\\myNamedPipe" connect to a pipe on the computer with the ip address '192.168.1.101'.

reply

Felipe Roos
05/21/2008 - 15:54

Does anyone know if this runs in .NET CF?

reply

Chris
06/05/2008 - 07:38

I'm getting a strange IOException on the FileStream.Read operation in the try/catch block of the client code. If the amount of data the server sends to the client is exactly the same size of the buffer in the client or less, the FileStream.Read operation works without issue. If I change the server code to send 1 byte or more data to the client than the client's buffer size, the FileStream.Read operation in the client code throws a IOException. Does anybody know why this is? I would expect to get a filled buffer on the first read, then loop and get the rest of the data on the second read.

reply

Frode
06/18/2008 - 15:34

Hi,

I'm having trouble to get the communication to work between machines. Have tried both using IP and machine name, but the handle is invalid so no connection.

Are there any special services or something like that which have to be turned on?

Anybody who have gotten this to work?

reply

The Reddest
06/18/2008 - 15:49

I've successfully done this without any special setup. I don't have Window's firewall turned on, so you might want to check that.

reply

Noodles
06/19/2008 - 01:50

Hi all,
I got the same problem to get the communication between two machines. I allready deactivated the firewall, tried to use IP or MachineName but nothing works.

I'm using XP SP3 on both machines, .NET 3.5

Any help is welcome.

thx

reply

Stephen S
06/23/2008 - 05:41

Interested to hear how you got on Noodles - did you get the machine-machine communication working in the end? Also, I'm curious about how pipes equate with port numbers - how do they get assigned etc... 'spose I can be less lazt and just Google further... ;)

reply

Anona
08/01/2008 - 02:50

Some modification is indeed needed for termination. Hope to see it soon.

reply

VDev
08/18/2008 - 06:52

Don't forget .NET Framework 3.0 supports pipes directly. Use namespace System.IO.Pipes for it.

reply

Greg
09/03/2008 - 10:37

How can one be notified (using the excellent code example you've provided) for when a client 'leaves' or disconnects?

thanks

Greg

reply

The Reddest
09/03/2008 - 12:07

When a client leaves or disconnects, the fStream.Read call will unblock with either an exception (which isn't caught in the post's code) or it will read 0 bytes (which is handled in the post's code). So you just have to watch for those two scenarios, and when one occurs, the client has been disconnected from the server.

reply

Alexey
09/09/2008 - 04:31

THANKTH!!!!!!!!!!

reply

Warlord0
09/10/2008 - 04:05

After successfully connecting and sending/receiving messages I found I couldn't close the windows application. The form disappeared but the debugger stayed active.

I eventually added a Disconnect method with this.handle.close() and this.stream.close() which then allowed the program to exit when I called Disconnect during formclosing.

I even got the demo client/servers to behave the same way. Once the client was connected it wouldn't exit.

PipeName was \\.\pipe\doofus\mypipetest

reply

Swamy
09/22/2008 - 05:12

I want to change the ACLs of the Pipe which is already created in the system. How can I do this?

reply

Tamas
10/08/2008 - 20:21

Threads should be background threads. In that case they will be aborted if all nonbackground thread aborts. This is the solution for Warlords problem, since ThreadAbortException does not seem to work, probably because of the API call.

f.e:
this.listenThread.IsBackground = true;

reply

Tamas
10/08/2008 - 20:29

Btw I don't understand why do you convert binary to string... This could be done in the upper abstraction layer(s).

reply

Max
10/13/2008 - 22:59

This seems to work, while the new .NET named pipes API provided by Microsoft does not. Thanks a lot!

reply

Robert
10/20/2008 - 13:40

I am trying to use the sample code and build my client as a console app but, for some reason unknown to me, the Invoke method does not exist for this type of app. Invoke is supposedly part of System.Windows.Form but even though I use that it is nowhere to be found.

If a create a Window Form app it shows up but not in a console app or a service app (because I would like to make the server a service app).

Any ideas?

BTW this sample code rocks!

reply

Robert
10/20/2008 - 14:55

Nevermind. Once I actually got that one brain cell working again I saw that I did not need the Invoke.

The pipeClient_MessageReceived method in your code is all I needed to capture a reply from the server.

reply

Robert
10/24/2008 - 08:59

I am having difficulty with one part of the code when I run it on a machine with just rights stripped down to a minimum. Otherwise the code works works fine.

This obviously would be the client piece. But the method SendMessage as problem with this.stream being initialized prior to the execution of the this.stream.Write() command.

When this.stream.Write() is executed it generates an error. When I look at the value of this.stream it is null. The Read() event apparent does not get triggered because that is where this.stream is initialized. I cannot force the issue because that generates an error in the Read() event.

BTW I am using .NET 2.0 framwork because the client machines are all Windows 2000. We have those machines locked down fairly tight as far as user rights.

My version of the client has been written as a console app. and I am not expecting a return message from the server. The server is a Windows Form and it works flawlessly.

Any thoughts on this?

reply

The Reddest
10/24/2008 - 13:37

My best guess is that you're having a synchronization problem. From what I gather, the stream is being created in the Read() method, which I assume is threaded off. My guess is that you're issuing a write call in another thread before the read thread has started. You can fix this problem a couple of different ways. First, create the stream before you start the read thread and pass it into the thread. Second, add some thread safety to the write method. Lock access to it using a Mutex until the stream is created.

That's all I've got. Hopefully it will at least point you in the right direction.

reply

Robert
10/25/2008 - 09:43

I narrowed it down even further and wished that I had done that before I posted my last message. Because my client will only be sending a message to the server without expecting a response I took the Read method out and the threading out after I posted my message.

I have narrowed the problem down to what I think is more likely a permissions issue on the network. When I run my client logged in as an Administrator it works great. When I log in as one of our agents (with very limited user rights) the Handle that is returned from the CreateFile() function is invalid and therefore cannot send a message to the server.

I am not quite sure where I need to go at this point. Our network guy is baffled as well and we don't want to raise the permission level on our agent machines.

reply

The Reddest
10/27/2008 - 07:56

You might want to check into setting some specific Security Attributes on the server side. By default, full access is granted to administrators and the owner of the pipe. Read-only access is granted to everyone else. It looks like that might be the problem. I would recommend granting the user account connecting the server full access to the pipe as well. This is done in the CreateNamedPipe function.

reply

Big Maybe
10/28/2008 - 13:18

Thank you for this article. I have a question about the server code. It creates and connects the pipe in an infinite loop - why does it do that? Isn't once enough?

reply

Big Maybe
10/28/2008 - 13:23

OK, I get it, it's to enable multiple clients. However, when I try running multiple PipeClient instances, every Connect seems to disconnect the previous client.

reply

The Reddest
10/28/2008 - 13:24

It creates a new handle to the same pipe for every client that connects to the server. The ConnectNamedPipe call will block until a client makes a connection. It then restarts the loop waiting on another connection. The server code in this tutorial will accept an infinite number of connections. A real server would probably have a limit on that number.

reply

Sourav
11/17/2008 - 12:22

On this NamedPipe example I made the server as a Windows Service and PipeClient as a class. I need to return the processed data received in client side to be sent to another class proxy class in C#. I’m not able to do it as the thread is a void delegate. Can you pl provide how I need to modify at Client application so that I can return a value to the class?
Appreciate if you could response quickly as this is urgent.

reply

The Reddest
11/17/2008 - 14:35

I don't know if I 100% understand your question, but here's a stab at an answer.

Personally, I like the event mechanism to get data out of the client class. Just hook the MessageReceived event wherever it is you're creating the clients, then forward the message to your other class.

If you want the read to be synchronous, i.e. when you call read, it blocks until it gets something then returns what it gets, then you'll need to remove the threading aspect from the client all together.

Get rid of the thread. Make Read() return what it reads. Get rid of the while(true) loop. Get rid of the event. And just call read directly from somewhere outside of the class.

You could also just pass the other C# class into the client itself. Then whenever it reads data, just pass it along.

reply

Sourav
11/18/2008 - 08:47

The changes you suggested needs to be implemented in the PipeClient only or Server class needs to be modified anyway?

reply

Sourav
11/18/2008 - 08:51

The suggested changes needs to be implemented on the Client class only correct?
Threading and while loop can be removed. But where I need to create the FileStreams at Client Side for Read and Write?

Does the Server code needs to be changed/it can stay as it is?

reply

The Reddest
11/18/2008 - 08:58

The server code would stay as is. I would then move the FileStream creation into the constructor of the Client object.

reply

J D
12/18/2008 - 21:57

Hi,
so i run into this when using your example. I start the server and then the client.

i modified the app so that the server and client start without user interaction. What i noticed is that with the same pipe name things stop communicating and only work when i change the pipe name.anyway to avoid this situation?

reply

Starbuck
01/06/2009 - 01:36

In response to Peter F and anyone else interested in Compact Framework (WinCE/PDA) development, I believe the answer is “no”, we cannot use this information for Windows Mobile. The reason is that CF (even v3.5) does not support the SafeFileHandle class that gets returned by CreateNamedPipe, CreateFile, and other unmanaged functions. HTH

reply

The Reddest
01/06/2009 - 16:23

The use of SafeFileHandle is optional. It can be directly replaced with IntPtr and it will still work. Some of the code will then have to be changed for checking for invalid handles.

reply

dhavleak
02/15/2009 - 05:37

Brandon - thanks for the excellent tutorial and code sample. Even with the .Net 3.5 support for named pipes this is still very useful considering 3.5's current lack of ubiquity.

One question. A very minor point: When we're listening for clients to connect, would it make sense to 'continue' instead of 'break' when a client connection fails. i.e. in the snippet below, would it make more sense to replace break with continue? My thinking is, just because one client failed to connect, doesn't mean that others will fail as well, so perhaps we should go back into the while loop to listen for the next client. Having said that, I don't know what would cause a client to fail to connect, and I certainly can't simulate such a failure to test the theory, so this really becomes a corner case that will rarely be exercised.

private void Listen()
{
   while (true)
   {

   //CreateNamedPipe, etc.

      int success = ConnectNamedPipe(
         clientPipeHandle,
         IntPtr.Zero);


      //failed to connect client pipe
      if (success != 1)
         break; // should we replace this with continue?

   }
}

reply

The Reddest
02/15/2009 - 15:04

I guess that's really up to the specific application. Looking at it again, I would probably continue like you suggest and log the failure. Thanks for the input!

reply

Arley
03/09/2009 - 08:45

Thanks for the excellent article.

I have just once question, when connecting to a named pipe over a network, can you control which port is used? For example, when connecting using "\\\\server\\pipe\\name" is there any way to specify the port on which the client should connect to the server?

reply

Mark
06/09/2009 - 12:59

I am trying to use A C# server and win32 C++ client. I get the pipe connection but the data is garbled. It seems like the issue would be the interpretation of the stream as opposed to a text string in win32.

The reason for the win32 is because I need to create a dll that I will need to inject into another process.

How can I get around this issue?

reply

The Reddest
06/09/2009 - 14:00

So if I understand correctly, you're sending a string over the pipe from C# to C++? How are you converting the C# string into a byte[]? C# characters are wide by default, do when you're reading them back in C++, you'll have to make sure you're also using wide characters. If you don't want to use unicode, convert the C# to a byte[] using an ASCIIEncoding object.

reply

Mark
06/10/2009 - 07:32

Thanks for the quick response! You pointed me in the right direction... I am using your server code. The changes I needed were to switch your code to use Encoding.Unicode.

Problem fixed.

Thanks again for the article and the help.
Mark

reply

chuckd
06/16/2009 - 13:32

I'm using this example for some interprocessing between a VB6.exe and C#.exe.

I have compiled the server app into C# interop .dll so it can be utilized in VB6. VB6 then starts the server through this .dll.

I then a have a C# client that is waiting for messages.

However, in the SendMessage portion of the C# server .dll sometimes my clients are null. Even though I can confirm that a client is added during the 'ListenForClients'.

What am I missing here.

        private void ListenForClients()
        {
            bool clientConnected = false;
            while (!clientConnected)
                {
                    SafeFileHandle clientHandle =
                    CreateNamedPipe(
                         this.pipeName,
                         DUPLEX | FILE_FLAG_OVERLAPPED,
                         0,
                         255,
                         BUFFER_SIZE,
                         BUFFER_SIZE,
                         0,
                         IntPtr.Zero);

                    //could not create named pipe
                    if (clientHandle.IsInvalid)
                        return;

                    int success = ConnectNamedPipe(clientHandle, IntPtr.Zero);

                    //could connect client
                    if (success == 1)
                    {
                        clientConnected = true;
                        //handling multiple clients
                        Client client = new Client();
                        client.handle = clientHandle;

                        lock (clients)
                        this.clients.Add(client);

                        this.readThread = new Thread(new ParameterizedThreadStart(Read));
                        readThread.IsBackground = true;
                        readThread.Start(client);
                    }
                    else
                    {
                        clientConnected = false;
                    }
                }
         
        }

        /// <summary>
        /// Reads incoming data from connected clients
        /// </summary>
        /// <param name="clientObj"></param>
        ///


        private void Read(object clientObj)
        {
            client = (Client)clientObj;
            client.stream = new FileStream(client.handle, FileAccess.ReadWrite, BUFFER_SIZE, true);
            byte[] buffer = new byte[BUFFER_SIZE];
            ASCIIEncoding encoder = new ASCIIEncoding();

            while (!_shutdown)
            {
                int bytesRead = 0;
                try
                {
                    bytesRead = client.stream.Read(buffer, 0, BUFFER_SIZE);
                }
                catch
                {
                    //read error has occurred
                    break;
                }

                //client has disconnected
                if (bytesRead == 0)
                    break;

                //fire message received event
                Console.WriteLine(encoder.GetString(buffer, 0, bytesRead));
               
            }

            //clean up resources
            client.stream.Close();
            client.handle.Close();
            lock (this.clients)
            this.clients.Remove(client);
        }

        /// <summary>
        /// Sends a message to all connected clients
        /// </summary>
        /// <param name="message">the message to send</param>
        public void SendMessage(string message)
        {
            lock (this.clients)
            {
                ASCIIEncoding encoder = new ASCIIEncoding();
                byte[] messageBuffer = encoder.GetBytes(message);
                foreach (Client client in this.clients)
                {
                    client.stream.Write(messageBuffer, 0, messageBuffer.Length);
                    client.stream.Flush();
                }
            }
        }

    }
}

reply

chuckd
06/16/2009 - 16:06

got it...use 3.5

reply

kerrywales
08/26/2009 - 04:07

This has been great in starting to get to grips with ipc.
I am trying to implement the code but the app running the server side remains in the process table after being closed and I have to use the TaskManager to destroy the app.

How to I ensure a clean close down of the pipeServer?

Thanks

reply

The Reddest
08/26/2009 - 07:53

You have to make sure you end the server's read thread. Convert the while(true), to while(enabled), and set enabled to false when you close the app.

reply

kalpesh
09/10/2009 - 07:54

First of all great artcle!

I am giving the IP Address of the to connect over the net work but its not working. My server is XP and the client is win2003 server. Can you please advise what would be the problem?

reply

Abhishek Shukla
12/23/2009 - 04:48

Good Article. This sample works fine if both client and server are on same machine. I placed server and client on different machines and changed pipename to "\\machinename\pipe\myNamedPipe" or "\\ipaddress\pipe\myNamedPipe".
In this case i get an exception objec reference not set to an instance of the object. After looking into the code i found that clientHandle is invalid. Do you have any idea?

reply

The Reddest
12/23/2009 - 09:26

From an earlier comment:

"You might want to check into setting some specific Security Attributes on the server side. By default, full access is granted to administrators and the owner of the pipe. Read-only access is granted to everyone else. It looks like that might be the problem. I would recommend granting the user account connecting the server full access to the pipe as well. This is done in the CreateNamedPipe function."

reply

kmorwood
02/11/2010 - 00:22

Having a problem with this code and need some other input...

First, everything works perfectly when the applications (client & server) are running within Visual Studio...in debug mode.

As soon as I run the server from the shell the client can not see the named pipe created by the server.

The client fails on CreateFile with windows error code==2 (file not found).

reply

banupriya
03/11/2010 - 06:06

Thanks for the article. But I need a solution to establish IPC between 2 Clients, of which one is a classic asp application and the other is the .NET application. From the ASP application, how do I create Named pipes and achieve communication? Is it possible to do IPC from the javascripts written on the ASP page???? If so please explain how to do it? Will interopservices help in javascript?

reply

finderme
03/24/2010 - 09:34

I've got problem with connection between C# server (from this article) & VB6 client. Any idea?

client:

Option Explicit
   Private Const BUFFSIZE = 10
   Private Declare Function CallNamedPipe Lib "kernel32" Alias _
      "CallNamedPipeA" ( _
      ByVal lpNamedPipeName As String, _
      lpInBuffer As Any, _
      ByVal nInBufferSize As Long, _
      lpOutBuffer As Any, _
      ByVal nOutBufferSize As Long, _
      lpBytesRead As Long, _
      ByVal nTimeOut As Long) As Long

   Private Sub cmdCallNamedPipe_Click()
      Dim res As Long, myStr As String, i As Long, cbRead As Long
      Dim numBytes As Long, bArray() As Byte, temp As String, szPipeName As String
     
      numBytes = BUFFSIZE
     
      szPipeName = serverName.Text
           
      ReDim bArray(numBytes)  'Build the return buffer

      'Call CallNamedPipe to do the transaction all at once
      res = CallNamedPipe(szPipeName, numBytes, LenB(numBytes), _
         bArray(0), numBytes, _
         cbRead, 30000) 'Wait up to 30 seconds for a response

      If res > 0 Then
         temp = Format(bArray(0), " 000")
         For i = 1 To cbRead - 1
            If (i Mod 16) = 0 Then temp = temp & vbCrLf
            temp = temp & " " & Format(bArray(i), "000")
         Next i
         txtReceive.Text = temp
      Else
         MsgBox "Error number " & Err.LastDllError & _
                " attempting to call CallNamedPipe.", vbOKOnly
      End If
   End Sub

returns error code 87 - ERROR_INVALID_PARAMETER :(

Pipe names of both apps are "\\.\pipe\myNamedPipe"

reply

Anonymous
03/28/2010 - 01:14

It looks like there's a bug in your Listen() function. I looked at the MSDN documentation for the ConnectNamedPipe() function, and regarding the second parameter, it states:

"If hNamedPipe was opened with FILE_FLAG_OVERLAPPED, the lpOverlapped parameter must not be NULL. It must point to a valid OVERLAPPED structure. If hNamedPipe was opened with FILE_FLAG_OVERLAPPED and lpOverlapped is NULL, the function can incorrectly report that the connect operation is complete."

reply

tosse
06/09/2010 - 06:18

Hi there,

i need to make my legacy app(.Net 1.1) able to communicate to a newer app using pipes. Does anybody know if it is possible to use pipes in .Net 1.1 and can teach me how to do this?

Thanks a lot
Thorsten

reply

The Reddest
06/09/2010 - 08:24

The only object that is not supported by .NET 1.1 is the SafeFileHandle, which was added in 2.0. You can simply replace all instances of that with an IntPtr and it will work.

reply

tosse
06/17/2010 - 02:09

That works.
Thanks a lot :)

reply

Gregory
07/04/2010 - 01:07

I'm very new to pipes. For a project I'm working on. I made a plugin for a game called RFactor, which makes a pipe called PipeToRFactorPlugin. I wrote the plugin in C++, and when I write a program to connect to the pipe in C++, it works fine. Now I'm trying to connect to it in C# (because I like the GUI) using your program and when I type in "\\\\.\\pipe\\PipeToRFactorPlugin" it doesn't connect. I just want it to connect. Any ideas what I could be doing wrong? I know the pipes their because my other program can connect to it, but I can't using this one. Thanks for the help.

reply

Anandhakrishnan
12/14/2010 - 00:44

how to set timeout for Listen() method.....

reply

The Reddest
12/14/2010 - 09:55

You'll have to change ConnectNamedPipe to be asynchronous. This can be achieved by passing an OVERLAPPED structure as the third argument (it's passing NULL in this tutorial).

Then you'll have to pull HasOverlappedIoCompleted to see when it's finished. To timeout, simply keep track of the amount of time you've been pulling and stop when you've reached the limit.

reply

Pravin
02/08/2011 - 03:41

Very Nice Center

reply

hosien
12/08/2011 - 05:55

yoe

reply

guy
12/28/2011 - 09:42

hi,

i tried the code, it worked fine except one thing, if i got a client server and the client stops reading from the pipe, i finally get stack in the server, in the client.stream.Write, it just get blocked, this means that the server gets stack by the client action (or should i say lake of action), i tried defining client.stream.WriteTimeout but an exception is raised, saying the stream does not support this option.

any idea ?

reply

Anonymous
01/01/2012 - 12:41

Thanks for the 1st paragraph!..

I remember i coded something whit pipes long time ago in C++ and it was easy cake (unlike your example in C#)

Time to go back to C++...

Have a nice day :)

PS: If you sum the ammount of time each humman being has spent on solving captachas you would find that would cost like 1000000 times less time and money, just to paying some dude to code a shit that avoids the need of implementing them... And maybe the answers would get better... but at least your captcha In fact, proves that one is a humman being capable of reading english...

The captchas from google... they cant grant that one is a humman being... they just grant that one must support their shit just because some people think 'google is no evil' ...

!!!!!!!

PS: Your captcha should trim spaces...

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.