Check if WCF(namedpipes) host is available?

廉价感情. 提交于 2019-12-24 18:02:24

问题


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 developer 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))
            {

回答1:


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".




回答2:


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?




回答3:


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



来源:https://stackoverflow.com/questions/7552502/check-if-wcfnamedpipes-host-is-available

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!