To finish where I left off last time, a quick look at the P/Invoke code we need in order to find the shared memory location where the pipe name is published, and read the pipe name from it.
Shared memory is implemented by Windows using the same mechanism used for memory mapped files, but where the file concerned is the the system paging file. Reading from shared memory set up by another process (here, the service) is a two-stage operation: first we obtain a handle to the named kernel File Mapping object using the Windows API OpenFileMapping; then we request a view of the mapped file, from which we can read the data, using the MapViewOfFile API.
When working with kernel handles, it is important that we release them after use even in the face of exceptions, so it is a good idea to use the SafeHandle patterns provided by the .NET class library. The Microsoft.Win32.SafeHandles namespace does actually define safe handle classes specifically for FileMapping and ViewOfFile handles, but unfortunately these have been declared internal and so we can’t use them. However, the base abstract class appropriate for both types of handle is public (SafeHandleZeroOrNullIsInvalid), making it quite trivial to declare our own versions of the safe handle types we need, derived from this abstract base. The P/Invoke declarations for the Windows API functions required are also straightforward.
Armed with these, and a structure definition for the contents of the shared memory, viz: static string GetPipeNameFromSharedMemory(string sharedMemoryName)
private struct PipeEndpointMetaData
public bool IsInitialised;
public Guid PipeGuid;
the core method looks like this:
const uint FILE_MAP_READ = 0x00000004;
const int ERROR_FILE_NOT_FOUND = 2;
using (SafeFileMappingHandle fileMappingHandle = OpenFileMapping(FILE_MAP_READ, false, sharedMemoryName))
int errorCode = Marshal.GetLastWin32Error();
if (ERROR_FILE_NOT_FOUND == errorCode) return null; //File not found - this isn't the right name variant
throw new Win32Exception(errorCode); // The name matched, but something went wrong opening it
} // If we get here, we have found and opened the shared memory file mapping
// Now we need to map a view and read the pipe metadata from it
using (SafeViewOfFileHandle viewBase = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 20))
throw new Win32Exception();
Guid pipeGuid = ((PipeEndpointMetaData)Marshal.PtrToStructure(viewBase.DangerousGetHandle(), typeof(PipeEndpointMetaData))).PipeGuid;
So, locating the pipe name involves calling this method for each of the possible name variants until it returns something other than null. If it throws, the most likely reason is an Access Denied error, which you get if the service has restricted access to the pipe (for instance by using my AclSecuredNamedPipeBinding), or if you try to locate a standard WCF NetNamedPipe endpoint from the security context of a remote user.