During a recent work project, I encountered a fascinating challenge related to Windows Services and WCF Named Pipes. I’m documenting this information to share what I learned and how we solved the problem.
The scenario involved a Windows Service needing to communicate with other processes (Activities) on the same machine. We chose named pipes for this inter-process communication (IPC) task, and since we were using .Net, WCF with NamedPipesBinding seemed like the ideal solution.
We envisioned the Activity processes as servers and the Windows Service as the client. Each Activity process would host a WCF server (System.ServiceModel.ServiceHost) with an Endpoint using NetNamedPipeBinding:
|
|
However, the Windows Service couldn’t open a connection to the named pipe. Research led us to several online resources:
- http://stackoverflow.com/questions/12072617/client-on-non-admin-user-cant-communicate-using-net-pipe-with-services?lq=1
- http://msdn.microsoft.com/en-us/library/windows/desktop/ms717797%28v=vs.85%29.aspx
- http://support.microsoft.com/?kbid=100843
- http://blogs.msdn.com/b/patricka/archive/2010/05/13/if-i-m-an-administrator-why-do-i-get-access-denied.aspx
- http://stackoverflow.com/questions/4959417/how-to-cross-a-session-boundary-using-a-wcf-named-pipe-binding?lq=1
These resources all suggested the same solution: to use a Callback Contract. This solution leverages WCF’s Callback Contract feature, which enables bidirectional communication between client and server, even if the initial request comes from the server.
Instead of the client always initiating requests to the server, the server can initiate a connection to the client and provide a callback object. This allows both the client and server to send requests to each other. We need two contracts: one for server-to-client calls and one for client-to-server calls.
While a detailed explanation of WCF Callback Contracts is outside the scope of this documentation, you can easily find code examples online.
After implementing Callback Contracts, our Windows Service and the other processes could communicate effectively. However, we needed to understand why the original approach failed.
The root cause lies in the way Windows manages kernel object namespaces. Starting with Windows Vista, the namespace for named kernel objects was split into Global and Local namespaces to prevent conflicts, particularly with Remote Desktop Services.
This separation affects named pipes and shared memory sections. When a WCF service using NetNamedPipeBinding is created, it doesn’t use the .Net pipe name directly for the underlying operating system pipe. Instead, it uses a GUID. The server publishes this GUID in a Named Shared Object (a Memory Mapped File Object), the name of which is derived from the .NetNamedPipeBinding endpoint address. The client uses the same algorithm to locate the shared memory object and retrieve the GUID to connect to the correct pipe.
The problem is that a regular process running in a user session (not Session 0) cannot create a global named file mapping object. It can only create one in its local session namespace. When the Windows Service (running in Session 0) attempts to access this file mapping object, it looks in the global namespace and fails to find it.
This is where Callback Contracts provide a workaround. The Windows Service, running in Session 0, can create a global file mapping object. The client process, even though it can’t create a global file mapping object, can open one. The client can then read the GUID, connect to the pipe, and establish the callback channel for the Windows Service to communicate back.
This understanding is supported by information found in MSDN article, which states:
“The creation of a file-mapping object in the global namespace, by using CreateFileMapping, from a session other than session zero is a privileged operation. … The privilege check is limited to the creation of file-mapping objects, and does not apply to opening existing ones.”
In summary, the separation of kernel object namespaces in Windows impacts how WCF services using named pipes communicate. Callback Contracts provide a solution by enabling the Windows Service to create a global file mapping object that client processes can access to retrieve the necessary pipe information.