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