#region Copyright 2010-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0 /* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #endregion using System; using System.Collections.Generic; using System.IO; using CSharpTest.Net.Synchronization; using CSharpTest.Net.Threading; using Microsoft.Win32; namespace CSharpTest.Net.IpcChannel { /// /// Provides a default implementation of the channel registrar usuing the system registry as the /// storage facility. /// public class IpcChannelRegistrar : IIpcChannelRegistrar { private static readonly string[] EmptyList = new string[0]; const string LockFormat = "{0}.RegistrarLock"; readonly RegistryKey _rootHive; readonly string _rootKey; /// /// Creates a ChannelRegistrar baseed in the hive specified at the allChannelsRoot path provided /// /// One of the registry hives ex: Registry.CurrentUser /// The path to store the ex: @"Software\YourProduct\IpcChannels" public IpcChannelRegistrar(RegistryKey rootHive, string allChannelsRoot) { _rootHive = Check.NotNull(rootHive); _rootKey = Check.NotEmpty(allChannelsRoot); using (RegistryKey key = OpenKey(true)) Check.Assert(key != null); } RegistryKey OpenKey(bool writable, params string[] paths) { string path = _rootKey; foreach (string part in paths) path = Path.Combine(path, part); return writable ? _rootHive.CreateSubKey(path) : _rootHive.OpenSubKey(path, false); } /// Registers a member (instanceId) for the provided channel name public void RegisterInstance(string channelName, string instanceId, string name) { using (new MutexLock(LockFormat, channelName)) using (RegistryKey key = OpenKey(true, channelName, instanceId)) { if (key != null && !String.IsNullOrEmpty(name)) key.SetValue(null, name); } } /// Unregisters a member (instanceId) from the provided channel name public void UnregisterInstance(string channelName, string instanceId) { using (new MutexLock(LockFormat, channelName)) using (RegistryKey key = OpenKey(true, channelName)) { try { if (key != null) { RegistryKey test = key.OpenSubKey(instanceId, false); if (test != null) { test.Close(); key.DeleteSubKeyTree(instanceId); } } } catch (ArgumentException) { } catch (IOException) { } } } /// Enumerates the registered instanceIds for the provided channel name public IEnumerable GetRegisteredInstances(string channelName) { return GetRegisteredInstances(channelName, (IEnumerable)null); } /// Enumerates the registered instanceIds who's name is instanceName for the provided channel name public IEnumerable GetRegisteredInstances(string channelName, string nameFilter) { IEnumerable filter = String.IsNullOrEmpty(nameFilter) ? (IEnumerable)null : new string[] { nameFilter }; return GetRegisteredInstances(channelName, filter); } /// Enumerates the registered instanceIds who's name is instanceName for the provided channel name public IEnumerable GetRegisteredInstances(string channelName, IEnumerable nameFilterIn) { List nameFilter = new List(); if (nameFilterIn != null) foreach (string name in nameFilterIn) if (!String.IsNullOrEmpty(name)) nameFilter.Add(name); nameFilter.Sort(); string[] instances; using (new MutexLock(LockFormat, channelName)) { using (RegistryKey key = OpenKey(false, channelName)) instances = key != null ? key.GetSubKeyNames() : new string[0]; } List found = new List(); foreach (string instance in instances) { if (nameFilter.Count == 0 || nameFilter.BinarySearch(instance, StringComparer.Ordinal) >= 0) found.Add(instance); else { try { using (RegistryKey key = OpenKey(false, channelName, instance)) { string tmpName = key.GetValue(null, null) as string; if (!String.IsNullOrEmpty(tmpName) && nameFilter.BinarySearch(tmpName, StringComparer.OrdinalIgnoreCase) >= 0) found.Add(instance); } } catch (IOException) { } } } return found; } /// Serializes the arguments for the event being sent to the specified instance public bool WriteParameters(string channelName, string instanceId, string eventName, string[] arguments) { int attempt = 0; while (true) { try { using (RegistryKey key = OpenKey(true, channelName, instanceId)) { if (key == null) return false; if (arguments != null && arguments.Length > 0) key.SetValue(eventName, arguments, RegistryValueKind.MultiString); else key.DeleteValue(eventName, false); } return true; } catch (IOException) { if(++attempt > 5) return false; } } } /// Retreives the arguments for the event being sent to the specified instance public string[] ReadParameters(string channelName, string instanceId, string eventName) { string[] args; using (RegistryKey key = OpenKey(true, channelName, instanceId)) { if (key == null) return null; try { args = key.GetValue(eventName, null, RegistryValueOptions.DoNotExpandEnvironmentNames) as string[]; if (args != null) key.DeleteValue(eventName, false); } catch (IOException) { return null; } } return args ?? EmptyList; } } }