开发者

Check if WCF(namedpipes) host is available?

Hi,

We have a winform application that is only to be executed as a singelton, If a second instance try to start this new instance will connect to the current and transmit parameters over namedpipes.

The problem is that when starting the first instance there will be a try to connect to existing host. If the host is not existing(like in this case) an exception will be thrown. There is no problem to handle this exception but our developers is often using "Break on Exception" and that means that every time we startup the application the deve开发者_开发百科loper will get two(in this case) breaks about exception. Thay will have to hit F5 twice for every start.

Is there any way to check if the service is available without throw exception if its not?

BestRegards

Edit1:

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr OpenFileMapping(uint dwDesiredAccess, bool bInheritHandle, string lpName);

The following code says : Error 152 Cannot implicitly convert type 'System.IntPtr' to 'Orbit.Client.Main.Classes.Controllers.MyClientController.SafeFileMappingHandle'

using (SafeFileMappingHandle fileMappingHandle
                = OpenFileMapping(FILE_MAP_READ, false, sharedMemoryName))
            {


If there is already a WCF server listening on the named pipe endpoint, there will be a shared memory object created, via which the server publishes the actual name of the pipe. See here for details of this.

You can check for the existence of this shared memory object with code something like the following, which will not throw, just return false, if there is no server running already. (I've extracted this from code I already have working, and then edited it to do what you want - but without testing the edited version, so apologies if you have to fix up assembly/namespace refs etc to get it running.)

public static class ServiceInstanceChecker
{

    public static bool DoesAServerExistAlready(string hostName, string path)
    {
        return IsNetNamedPipeSharedMemoryMetaDataPublished(DeriveSharedMemoryName(hostName, path));
    }


    private static string DeriveSharedMemoryName(string hostName, string path)
    {
        StringBuilder builder = new StringBuilder();
        builder.Append(Uri.UriSchemeNetPipe);
        builder.Append("://");
        builder.Append(hostName.ToUpperInvariant());
        builder.Append(path);
        byte[] uriBytes = Encoding.UTF8.GetBytes(builder.ToString());

        string encodedNameRoot;
        if (uriBytes.Length >= 0x80)
        {
            using (HashAlgorithm algorithm = new SHA1Managed())
            {
                encodedNameRoot = ":H" + Convert.ToBase64String(algorithm.ComputeHash(uriBytes));
            }
        }
        else
        {
            encodedNameRoot = ":E" + Convert.ToBase64String(uriBytes);
        }
        return Uri.UriSchemeNetPipe + encodedNameRoot;
    }

    private static bool IsNetNamePipeSharedMemoryMetaDataPublished(string sharedMemoryName)
    {
        const uint FILE_MAP_READ = 0x00000004;
        const int ERROR_FILE_NOT_FOUND = 2;
        using (SafeFileMappingHandle fileMappingHandle 
            = OpenFileMapping(FILE_MAP_READ, false, sharedMemoryName))
        {
            if (fileMappingHandle.IsInvalid)
            {
                int errorCode = Marshal.GetLastWin32Error();
                if (ERROR_FILE_NOT_FOUND == errorCode) return false; 
                throw new Win32Exception(errorCode); // The name matched, but something went wrong opening it
            }
            return true;
        }
    }

    private class SafeFileMappingHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        public SafeFileMappingHandle() : base(true) { }
        public SafeFileMappingHandle(IntPtr handle) : base(true) { base.SetHandle(handle); }

        protected override bool ReleaseHandle()
        {
            return CloseHandle(base.handle);
        }
    }

}

The host name and path you pass in are derived from the WCF service url. Hostname is either a specific hostname (e.g. localhost) or +, or *, depending on the setting for HostNameComparisonMode.

EDIT: You'll also need a couple of P/Invoke declarations for the Win API functions:

[DllImport("kernel32.dll", SetLastError = true)] 
static extern bool CloseHandle(IntPtr hObject); 

[DllImport("kernel32.dll", SetLastError = true)] 
static extern SafeFileMappingHandle OpenFileMapping(
  uint dwDesiredAccess,
  bool inheritHandle,
  string name
);

EDIT2: We need to tweak the return value of DeriveSharedMemoryName to specify the Local kernel namespace, assuming that your application is not run with elevated privileges. Change the last line of this function to read:

return @"Local\" + Uri.UriSchemeNetPipe + encodedNameRoot;

You also need to specify the hostname parameter correctly to match the hostNameComparisonMode setting used in your binding. As far as I recall, this defaults to StrongWildcard matching in the NetNamedPipeBinding, so you probably need to pass in "+" rather than "localhost".


Can you try to list the named pipes available using

String[] listOfPipes = System.IO.Directory.GetFiles(@"\.\pipe\");

and then determine is your named pipe is amongst them?


My solution is the following :

if (Debugger.IsAttached) 
return true;

This will make sure that the code for checking the service is never runned during debugging.

BestRegards

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜