Need to call DisposeLocalCopyOfClientHandle () after establishing a connection

The step following the connection to the anonymous pipe requires the server to call DisposeLocalCopyOfClientHandle . MSDN explains:

The DisposeLocalCopyOfClientHandle method should be called after the client handle is passed to the client. If this method is not called, the AnonymousPipeServerStream object will not receive a notification when the client disposes of its PipeStream object.

Trying to understand why the server will not be noticed when closing the client, I continued to look at DisposeLocalCopyOfClientHandle at the source of the link:

 // 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 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 handles to be different.

Is the parent descriptor and child descriptor (i.e. the m_handle server and the m_clientHandle server that is passed to the child) different from the first place? "different" here means "links to different objects" (as I understand it) or does it have a different meaning?

+5
source share
2 answers

Your confusion stems from the fact that the server and client are also parent and child processes. Pipe handles - server or client, but may be present in the parent and child. For a short moment after the server spawned a client, but before calling DisposeLocalCopyOfClientHandle , three handles are played in the game:

  • Server handler for the channel in the server (parent) process.
  • The client channel descriptor in the server (parent) process.
  • The client channel descriptor in the client process (child), inherited from the parent process.

The second descriptor must be closed after the child is started and started, because, as the comment explains, the pipe remains operational until all client descriptors are closed. If the second descriptor gets up, this will not allow the server to detect that the child process has completed.

Instead of using inheritance, an implementation can also spawn a child process and use a DuplicateHandle , which eliminates the need to use this helper method since the original handle can be closed immediately. This seems to mean that "mak [ing] passes handles between first-class processes."

+3
source

An obscure detail that is hard to see in .NET is the bInheritHandles CreateProcess () argument, a nasty little unixism that sneaked into winapi. Determining the correct value for this argument is very difficult to understand, you need to learn a lot about the process that you are starting, and it scales very poorly, this is an all-or-nothing option. Raymond Chen has a blog post that talks about ugly corner cases and what they did in Windows version 6.0 to solve the problem.

Otherwise, it is a solution that can be used in .NET. First of all, because it still supports older versions of Windows up to .NET 4.5. And it would be quite difficult to use. Accordingly, the ProcessStartInfo class does not have a property that allows you to explicitly control the value of the bInheritHandles argument, Process.Start () always passes TRUE. This is what "as long as we do not pass through the first-class handlers," refers to.

Another detail is that the descriptor that the child process inherits is a separate descriptor different from the descriptor of the parent process. Therefore, a total of two CloseHandle calls are required to destroy a system object. Or, to put it another way, both the parent and the child must stop using the object. This means that the "OS considers the parent and child descriptors to be different" comment refers to.

The main function of CreatePipe () winapi, which is used to create an anonymous channel, returns two descriptors, one for reading and one for writing. Depending on the direction of the pipe, the parent (aka server) and one child process (aka client) should be used. These descriptors are inherited handles, so after starting the child process, four CloseHandle calls are required to destroy the pipe object.

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

The same trick, however, cannot work for the channel handle that the child process should use. In the end, the intention is to inherit the handle so that it can return to the server. This is why you should do this explicitly after learning that the child process has been started properly. After calling the DisposeLocalCopyOfClientHandle () method, now only two calls to CloseHandle are required.

Calling CloseHandle on the client side is quite simple; it does this by calling Close or Dispose on AnonymousPipeClientStream. Or, flipping over with an unhandled exception that causes the process to crash, the OS then takes care of closing the handle. Now there is only one call to CloseHandle.

One to go, it is harder on the server side. He only knows to close / delete his anonymous protocol when he receives a notification that the child process is no longer using it. Scary quotes around the "notification", there is no event that reports this. The correct way is for the child process to send an explicit "goodbye" message so that the server knows that you need to call Close. Not quite correct, but not unusual way is that the child did not say goodbye beautifully, then the server can only know that he no longer exists from the exception that he receives when he continues to use this channel.

Which key, you get only an exception when the OS sees that the server is trying to use this channel and there are no other handles on the other side. Or, in other words, if you forget to call DisposeLocalCopyOfClientHandle (), then you will not get an exception. Not good.

+8
source

All Articles