context: //
  // System.Runtime.Remoting.Channels.ChannelServices.cs
  //
  // Author: Rodrigo Moya (rodrigo@ximian.com)
  //         Dietmar Maurer (dietmar@ximian.com)
  //         Lluis Sanchez Gual (lluis@ideary.com)
  //
  // 2002 (C) Copyright, Ximian, Inc.
  //
  
  //
  // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  using System.Collections;
  using System.Collections.Generic;
  using System.Reflection;
  using System.Runtime.Remoting;
  using System.Runtime.Remoting.Channels;
  using System.Runtime.Remoting.Messaging;
  using System.Runtime.Remoting.Contexts;
  
  namespace System.Runtime.Remoting
  {
  	[Serializable]
  	internal class ChannelInfo : IChannelInfo
  	{
  		object [] channelData = null;
  
  		public ChannelInfo ()
  		{
  			channelData = ChannelServices.GetCurrentChannelInfo ();
  		}
  
  		public ChannelInfo (object remoteChannelData)
  		{
  			channelData = new object[] { remoteChannelData };
  		}
  		
  		public object[] ChannelData 
  		{
  			get {
  				return channelData;
  			}
  			
  			set {
  				channelData = value;
  			}
  		}
  	}
  }
  
  namespace System.Runtime.Remoting.Channels
  {
  	[System.Runtime.InteropServices.ComVisible (true)]
  	public sealed class ChannelServices
  	{
  		private static ArrayList registeredChannels = new ArrayList ();
  		private static ArrayList delayedClientChannels = new ArrayList ();
  		
  		private static CrossContextChannel _crossContextSink = new CrossContextChannel();
  		
  		internal static string CrossContextUrl = "__CrossContext";
  
  		private ChannelServices ()
  		{
  		}
  
  		internal static CrossContextChannel CrossContextChannel
  		{
  			get { return _crossContextSink; }
  		}
  
  		internal static IMessageSink CreateClientChannelSinkChain(string url, object remoteChannelData, out string objectUri)
  		{
  			// Locate a channel that can parse the url. This channel will be used to
  			// create the sink chain.
  
  			object[] channelDataArray = (object[])remoteChannelData;
  
  			lock (registeredChannels.SyncRoot)
  			{
  				// First of all, try registered channels
  				foreach (IChannel c in registeredChannels) 
  				{
  					IChannelSender sender = c as IChannelSender;
  					if (sender == null) continue;
  	
  					IMessageSink sink = CreateClientChannelSinkChain (sender, url, channelDataArray, out objectUri);
  					if (sink != null) return sink;
  				}
  				
  				// Not found. Try now creation delayed channels
  				RemotingConfiguration.LoadDefaultDelayedChannels ();
  				foreach (IChannelSender sender in delayedClientChannels) 
  				{
  					IMessageSink sink = CreateClientChannelSinkChain (sender, url, channelDataArray, out objectUri);
  					if (sink != null) {
  						delayedClientChannels.Remove (sender);
  						RegisterChannel (sender);
  						return sink;
  					}
  				}
  			}
  			
  			objectUri = null;
  			return null;
  		}
  		
  		internal static IMessageSink CreateClientChannelSinkChain (IChannelSender sender, string url, object[] channelDataArray, out string objectUri)
  		{
  			objectUri = null;
  			if (channelDataArray == null) {
  				return sender.CreateMessageSink (url, null, out objectUri);
  			}
  			else {
  				foreach (object data in channelDataArray) {
  					IMessageSink sink;
  
  					if (data is IChannelDataStore) {
  						// Don't provide the url in this case, since some channels won't
  						// check the channelData parameter if the url is not null.
  						sink = sender.CreateMessageSink (null, data, out objectUri);
  					} else {
  						sink = sender.CreateMessageSink (url, data, out objectUri);
  					}
  					if (sink != null) return sink;		
  				}
  			}
  			return null;
  		}
  		
  		public static IChannel[] RegisteredChannels
  		{
  			get {
  				lock (registeredChannels.SyncRoot)
  				{
  					var list = new List<IChannel> ();
  					
  					for (int i = 0; i < registeredChannels.Count; i++) {
  						IChannel ch = (IChannel) registeredChannels[i];
  						if (ch is CrossAppDomainChannel) continue;
  						list.Add (ch);
  					}
  
  					return list.ToArray ();
  				}
  			}
  		}
  
  		public static IServerChannelSink CreateServerChannelSinkChain (
  			IServerChannelSinkProvider provider, IChannelReceiver channel)
  	    {
  			IServerChannelSinkProvider tmp = provider;
  			while (tmp.Next != null) tmp = tmp.Next;
  			tmp.Next = new ServerDispatchSinkProvider ();
  
  			// Every provider has to call CreateSink() of its next provider
  			return  provider.CreateSink (channel);
  		}
  
  		public static ServerProcessing DispatchMessage (
  			IServerChannelSinkStack sinkStack,
  			IMessage msg,
  			out IMessage replyMsg)
  		{
  			if (msg == null) throw new ArgumentNullException ("msg");
  			
  			// Async processing is not done here because there isn't any way
  			// to know if a message is to be dispatched sync or asynchronously.
  
  			replyMsg = SyncDispatchMessage (msg);
  
  			if (RemotingServices.IsOneWay (((IMethodMessage) msg).MethodBase))
  				return ServerProcessing.OneWay;
  			else
  				return ServerProcessing.Complete;
  		}
  
  		public static IChannel GetChannel (string name)
  		{
  			lock (registeredChannels.SyncRoot)
  			{
  				foreach (IChannel chnl in registeredChannels) {
  					if (chnl.ChannelName == name && !(chnl is CrossAppDomainChannel)) return chnl;
  				}
  				return null;
  			}
  		}
  
  		public static IDictionary GetChannelSinkProperties (object obj)
  		{
  			if (!RemotingServices.IsTransparentProxy (obj))
  				throw new ArgumentException ("obj must be a proxy","obj");
  				
  			ClientIdentity ident = (ClientIdentity) RemotingServices.GetRealProxy (obj).ObjectIdentity;
  			IMessageSink sink = ident.ChannelSink;
  			var dics = new List<IDictionary> ();
  			
  			while (sink != null && !(sink is IClientChannelSink))
  				sink = sink.NextSink;
  
  			if (sink == null)
  				return new Hashtable ();
  
  			IClientChannelSink csink = sink as IClientChannelSink;
  			while (csink != null)
  			{
  				dics.Add (csink.Properties);
  				csink = csink.NextChannelSink;
  			}
  
  			IDictionary[] adics = dics.ToArray ();
  			return new AggregateDictionary (adics);
  		}
  
  		public static string[] GetUrlsForObject (MarshalByRefObject obj)
  		{
  			string uri = RemotingServices.GetObjectUri (obj);
  			if (uri == null) return new string [0];
  
  			var list = new List<string> ();
  
  			lock (registeredChannels.SyncRoot)
  			{
  				foreach (object chnl_obj in registeredChannels) {
  					if (chnl_obj is CrossAppDomainChannel) continue;
  					
  					IChannelReceiver chnl = chnl_obj as IChannelReceiver;
  	
  					if (chnl != null)
  						list.AddRange (chnl.GetUrlsForUri (uri));
  				}
  			}
  			
  			return list.ToArray ();
  		}
  
  		[Obsolete ("Use RegisterChannel(IChannel,Boolean)")]
  		public static void RegisterChannel (IChannel chnl)
  		{
  			RegisterChannel (chnl, false);
  		}
  
  		public static void RegisterChannel (IChannel chnl, bool ensureSecurity)
  		{
  			if (chnl == null)
  				throw new ArgumentNullException ("chnl");
  
  			if (ensureSecurity) {
  				ISecurableChannel securable = chnl as ISecurableChannel;
  				if (securable == null)
  					throw new RemotingException (String.Format ("Channel {0} is not securable while ensureSecurity is specified as true", chnl.ChannelName));
  				securable.IsSecured = true;
  			}
  			
  			// Put the channel in the correct place according to its priority.
  			// Since there are not many channels, a linear search is ok.
  
  			lock (registeredChannels.SyncRoot)
  			{
  
gt: 				int pos = -1;