The need to call DisposeLocalCopyOfClientHandle() after establishing a connection The need to call DisposeLocalCopyOfClientHandle() after establishing a connection windows windows

The need to call DisposeLocalCopyOfClientHandle() after establishing a connection


The obscure detail that is hard to see in .NET is the bInheritHandles argument of CreateProcess(), a nasty little unixism that crept in the winapi. Determining the proper value of that argument is very difficult to reason through, you have to know a lot about the process you start and it scales really poorly, it is an all-or-nothing option. Raymond Chen has a blog post that talks about the ugly corner cases and what they did in Windows version 6.0 to solve the problem.

Not otherwise a solution that could be used in .NET. Primarily because it still supported older Windows versions up to .NET 4.5. And it would be pretty hard to use. Accordingly, the ProcessStartInfo class has no property that allows you to explicitly control the bInheritHandles argument value, Process.Start() always passes TRUE. Which is what the "until we make passing handles between processes first class" comment refers to.

A further detail is that the handle that the child process inherits is a separate handle that is distinct from the handle of the parent process. So a total of two CloseHandle calls are required to destroy the system object. Or to put it another way, both the parent and the child need to stop using the object. Which is what the "the OS considers the parent and child's handles to be different" comment refers to.

The underlying CreatePipe() winapi function that is used to create an anonymous pipe returns two handles, one to read and one to write. Depending on the pipe direction, one should be used by the parent (aka server) and one by the child process (aka client). These handles are inheritable handles so after you start the child process, a total of four CloseHandle calls are required to destroy the pipe object.

That's unpleasant. The .NET wrapper can do something about the server handle. It calls DuplicateHandle() to make a copy of the server-side handle, passing FALSE for the bInheritHandle argument. Then closes the original handle. Good, the child process will no longer inherit the server-side handle so now only three CloseHandle calls are required.

The same trick however cannot work for the pipe handle that the child process needs to use. After all, the intention is for it to inherit the handle so it can talk back to the server. Which is why you have to do it explicitly, after you know that the child process was started properly. After your DisposeLocalCopyOfClientHandle() method call now only two CloseHandle calls are required.

The CloseHandle call on the client side is easy enough, it does so by calling Close or Dispose on the AnonymousPipeClientStream. Or by keeling over by an unhandled exception that crashes the process, the OS then takes care of closing the handle. Now only one CloseHandle call is remaining.

One to go, it is harder on the server side. It only knows to close/dispose its AnonymousPipeServerStream when it gets the "notification" that the child process is no longer using it. Scary quotes around "notification", there is no event that tells you this. The proper way is for the child process to send an explicit "goodbye" message so the server knows to call Close. The not so proper but not uncommon way is that the child did not say goodbye nicely, then the server can only know it isn't around anymore from the exception it gets when it continues to use the pipe.

Which is the key, you only get the exception when the OS sees that the server tries to use the pipe and there are no remaining handles opened on the other side. Or in other words, if you forget to call DisposeLocalCopyOfClientHandle() then you don't get the exception. Not good.


Your confusion stems from the fact that the server and the client are also parent and child processes. The pipe handles are server or client, but can be present in parent and child. For a brief moment, after the server has spawned the client but before DisposeLocalCopyOfClientHandle is called, three handles are in play:

  • A server handle to the pipe, in the server (parent) process.
  • A client handle to the pipe, in the server (parent) process.
  • A client handle to the pipe, in the client (child) process, inherited from the parent process.

The second handle needs to be closed after the child is up and running because, as the comment explains, the pipe remains usable until all client handles have been closed. If the second handle sticks around, it will prevent the server from detecting that the child process is done.

Rather than using inheritance, the implementation could also spawn the child process and use DuplicateHandle, which would eliminate the need for this helper method since the original handle could be closed immediately. This is presumably what's meant by "mak[ing] passing handles between processes first class".