As those who have read my earlier post will be aware, when a WCF client wishes to establish communication with a WCF service over the NetNamedPipe transport binding, the client-side stack has to execute an undocumented algorithm, based on incomplete information, in order to locate the actual named pipe on which the service is listening. This is because the WCF service-side stack dynamically creates a differently named pipe each time it starts up, rather than using a fixed name based on the service endpoint URL.
All that the client knows is the service URL with which it has been configured to communicate. This may be identical to the URL on which the service endpoint is configured to listen, but is not necessarily so. This is because the WCF mechanism for dispatching incoming messages to particular service endpoints supports wildcard matching in addition to exact matching of URL. The HostNameComparisonMode property, which most WCF bindings support, determines the URL matching rules which will be applied for a particular service endpoint. A post by Kenny Wolf some time ago explains this mechanism in detail. The WCF client doesn’t know whether its configured service URL is an exact match of the service endpoint’s listening URL, or whether it is matched to the endpoint at a related but not identical URL as a consequence of wildcard matching rules.
The client code therefore has to “search” for its rendezvous point with the service, essentially by enumerating the possible endpoint listener URLs implied by the matching rules until it finds one which maps to the rendezvous point – which is a shared memory object with a distinctive name derived from the endpoint listener URL, as explained in my earlier post.
This searching algorithm is implemented in the GetPipeName method of the internal WCF type System.ServiceModel.Channels.PipeConnectionInitiator. One way of finding the actual name of the pipe being used by a service is to call this internal method using reflection. But is not too difficult, and much faster, to implement the search oneself.
There are three name components which define the scope of the search:
- the kernel object namespace, which is either “Local” or “Global”
- the host part of the URL, which is either a proper host name (e.g. localhost) or one of the hostname wildcard specifiers + or *
- the URL path prefix
Suppose a client wants to call a service for which it has been given the URL “net.pipe://localhost/one/two/three”. To locate the named pipe on which the service is actually listening, the client must look for the existence of a named shared memory object with a name based on one of the following URLs, and the search must occur in this order:
The actual names to be searched for are derived from these URLs by applying an algorithm which is easier to show in code than to describe in words (see below). The full list is searched for in the “Global” kernel namespace first, and then in the “Local” namespace if there is no match in “Global”.
Here is a function implementing the name algorithm:
private static string DeriveSharedMemoryName(string hostName, string path)
StringBuilder builder = new StringBuilder();
byte uriBytes = Encoding.UTF8.GetBytes(builder.ToString());
if (uriBytes.Length >= 0x80)
using (HashAlgorithm algorithm = new SHA1Managed())
encodedNameRoot = ":H" + Convert.ToBase64String(algorithm.ComputeHash(uriBytes));
encodedNameRoot = ":E" + Convert.ToBase64String(uriBytes);
return Uri.UriSchemeNetPipe + encodedNameRoot;
The result is a name which looks something like this:
Remember, what this algorithm generates is not the name of the pipe itself, but the name of a kernel shared memory FileMapping object which gives access to a shared memory structure containing the name of the pipe currently in use. To complete the search we need to try to open a view on the shared memory mapping and if successful, read the pipe name from it. I’ll save that for next time.