The step following the establishment of a connection with an anonymous pipe requires the server calling DisposeLocalCopyOfClientHandle
. MSDN explains:
The DisposeLocalCopyOfClientHandle method should be called after the client handle has been passed to the client. If this method is not called, the AnonymousPipeServerStream object will not receive notice when the client disposes of its PipeStream object.
Trying to understand why wouldn't the server be noticed when client is closed, I went on to look at DisposeLocalCopyOfClientHandle
on reference source:
// This method is an annoying one but it has to exist at least until we make passing handles between
// processes first class. We need this because once the child handle is inherited, the OS considers
// the parent and child's handles to be different. Therefore, if a child closes its handle, our
// Read/Write methods won't throw because the OS will think that there is still a child handle around
// that can still Write/Read to/from the other end of the pipe.
//
// Ideally, we would want the Process class to close this handle after it has been inherited. See
// the pipe spec future features section for more information.
//
// Right now, this is the best signal to set the anonymous pipe as connected; if this is called, we
// know the client has been passed the handle and so the connection is live.
[System.Security.SecurityCritical]
public void DisposeLocalCopyOfClientHandle() {
if (m_clientHandle != null && !m_clientHandle.IsClosed) {
m_clientHandle.Dispose();
}
}
This sentence confused me:
once the child handle is inherited, the OS considers the parent and child's handles to be different.
Aren't the parent's handle and the child's handle (i.e., the server's m_handle
and the server's m_clientHandle
, which is passed to the child) different in the first place? Does "different" here mean "references different objects" (this is the way I understand it), or does it have other meaning?
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:
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".