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.
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.
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 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.
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.
{
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.
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_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.
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.
09/04/2007 - 19:55
On the server, you want to pass the file handle returned from
ConnectNamedPipeto theFileStream. I've modified the DLLImports and the code snippets in this tutorial to illustrate that. I apologize for the errors.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
09/08/2007 - 04:39
I have implemented and consulted with many people, and I have the same problem as shil.
09/12/2007 - 11:19
Me too ...
09/12/2007 - 12:08
Ok, It works with:
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);
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!
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
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.
04/29/2009 - 04:23
U R Great
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?
11/22/2007 - 21:33
Many thanks.
11/26/2007 - 05:05
It seems, that you forgot to call the
DisconnectNamedPipefunction. Cause when you close your server window, the listen Thread won't end.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.
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
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.Readcall 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.
12/07/2007 - 14:42
Thank you very much for the answer. It make sense.
12/07/2007 - 15:01
Based on my understanding this is what I did and tell me if this is correct
Client:
{
try
{
this.stream.Close();
}
catch (Exception e1)
{
Console.WriteLine(e1.ToString());
}
}
Questiion, is there a better way of handling this?
Server:
{
Server.DisconnectNamedPipe(clientHandle);
}
This comment alone fixed my close process to close properly. Do I still need to stop the thread?
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
{
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.
06/20/2008 - 17:41
Just set IsBackground property at true on all threads.
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?
01/10/2008 - 10:00
Forget my last question. It was stupid
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.
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?
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.
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.
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'.
05/21/2008 - 15:54
Does anyone know if this runs in .NET CF?
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.
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?
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.
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
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... ;)
08/01/2008 - 02:50
Some modification is indeed needed for termination. Hope to see it soon.
08/18/2008 - 06:52
Don't forget .NET Framework 3.0 supports pipes directly. Use namespace System.IO.Pipes for it.
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
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.
09/09/2008 - 04:31
THANKTH!!!!!!!!!!
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
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?
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;
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).
10/13/2008 - 22:59
This seems to work, while the new .NET named pipes API provided by Microsoft does not. Thanks a lot!
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!
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.
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?
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.
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.
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.
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?
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.
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.
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.
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.
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?
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?
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.
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?
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
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.
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.
{
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?
}
}
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!
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?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?
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.
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
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.
{
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();
}
}
}
}
}
06/16/2009 - 16:06
got it...use 3.5
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
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.
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?
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?
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."
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).
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?
03/24/2010 - 09:34
I've got problem with connection between C# server (from this article) & VB6 client. Any idea?
client:
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"
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."
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
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.
06/17/2010 - 02:09
That works.
Thanks a lot :)
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.
12/14/2010 - 00:44
how to set timeout for Listen() method.....
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.
02/08/2011 - 03:41
Very Nice Center
12/08/2011 - 05:55
yoe
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 ?
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...
Add Comment
[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.