Проверить, доступен ли хост WCF (namedpipes)? - PullRequest
1 голос
/ 26 сентября 2011

Привет

У нас есть приложение winform, которое должно выполняться только как одиночный, если второй экземпляр попытается запустить этот новый экземпляр, он подключится к текущему и передаст параметры по именованным каналам.

Проблема в том, что при запуске первого экземпляра будет попытка подключиться к существующему хосту. Если хост не существует (как в этом случае), будет выдано исключение. Нет проблем с обработкой этого исключения, но наши разработчики часто используют «Break on Exception», и это означает, что каждый раз, когда мы запускаем приложение, разработчик получит два (в данном случае) перерыва об исключении. Тэй должен будет дважды нажать F5 за каждый старт.

Есть ли способ проверить, доступна ли служба без исключения броска, если ее нет?

BestRegards

Редактировать1:

[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);

Следующий код говорит: Ошибка 152 Не удается неявно преобразовать тип 'System.IntPtr' в 'Orbit.Client.Main.Classes.Controllers.MyClientController.SafeFileMappingHandle'

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

Ответы [ 3 ]

3 голосов
/ 03 октября 2011

Если уже есть WCF-сервер, прослушивающий конечную точку именованного канала, будет создан объект общей памяти, через который сервер публикует фактическое имя канала. Подробнее об этом .

Вы можете проверить существование этого объекта общей памяти с помощью кода, подобного следующему, который не будет выдавать, просто вернет false, если сервер еще не запущен. (Я извлек это из кода, который у меня уже работает, а затем отредактировал его, чтобы сделать то, что вы хотите - но без тестирования отредактированной версии, поэтому извиняюсь, если вам нужно исправить сборку / ссылки на пространство имен и т. Д., Чтобы запустить его.) 1005 *

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);
        }
    }

}

Имя хоста и путь, по которому вы переходите, получены из URL-адреса службы WCF. Имя хоста - это либо конкретное имя хоста (например, localhost), либо +, либо *, в зависимости от настройки HostNameComparisonMode.

РЕДАКТИРОВАТЬ: Вам также понадобится пара объявлений P / Invoke для функций Win API:

[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: нам нужно настроить возвращаемое значение DeriveSharedMemoryName, чтобы указать пространство имен локального ядра, предполагая, что ваше приложение не запускается с повышенными привилегиями. Измените последнюю строку этой функции следующим образом:

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

Вам также необходимо правильно указать параметр hostname, чтобы он соответствовал настройке hostNameComparisonMode, используемой в вашей привязке. Насколько я помню, по умолчанию это сопоставление StrongWildcard в NetNamedPipeBinding, поэтому вам, вероятно, нужно передать "+", а не "localhost".

0 голосов
/ 06 октября 2011

Мое решение следующее:

if (Debugger.IsAttached) 
return true;

Это гарантирует, что код для проверки службы никогда не запускается во время отладки.

BestRegards

0 голосов
/ 27 сентября 2011

Можете ли вы попытаться перечислить именованные каналы, используя

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

, а затем определить, входит ли ваш именованный канал в их число?

...