context: //
  // System.Runtime.Remoting.Channels.CrossAppDomainChannel.cs
  //
  // Author: Patrik Torstensson (totte_mono@yahoo.com)
  //         Lluis Sanchez Gual (lluis@ximian.com)
  //
  // 2003 (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.IO;
  using System.Threading;
  using System.Runtime.Remoting;
  using System.Runtime.Remoting.Messaging;   
  using System.Runtime.Remoting.Channels; 
  using System.Runtime.Remoting.Contexts; 
  using System.Runtime.Serialization;
  using System.Runtime.Serialization.Formatters.Binary;
  using System.Reflection;
  
  namespace System.Runtime.Remoting.Channels 
  {
  
  	// Holds the cross appdomain channel data (used to get/create the correct sink)
  	[Serializable]
  	internal class CrossAppDomainData 
  	{
  		// TODO: Add context support
  		// Required for .NET compatibility
  #pragma warning disable 0414
  		private object _ContextID;
  #pragma warning restore
  		private int _DomainID;
  		private string _processGuid;
  
  		internal CrossAppDomainData(int domainId) 
  		{
  			_ContextID = (int) 0;
  			_DomainID = domainId;
  			_processGuid = RemotingConfiguration.ProcessId;
  		}
  
  		internal int DomainID 
  		{  
  			get { return _DomainID;	}
  		}
  
  		internal string ProcessID
  		{
  			get { return _processGuid; }
  		}
  	}
  
  	// Responsible for marshalling objects between appdomains
  	[Serializable]
  	internal class CrossAppDomainChannel : IChannel, IChannelSender, IChannelReceiver 
  	{
  		private const String _strName = "MONOCAD";
  		
  		private static Object s_lock = new Object();
  
  		internal static void RegisterCrossAppDomainChannel() 
  		{
  			lock (s_lock) 
  			{
  				// todo: make singleton
  				CrossAppDomainChannel monocad = new CrossAppDomainChannel();
  				ChannelServices.RegisterChannel ((IChannel) monocad);
  			}
  		}		
  
  		// IChannel implementation
  		public virtual String ChannelName 
  		{
  			get { return _strName; }
  		}
      
  		public virtual int ChannelPriority 
  		{
  			get { return 100; }
  		}
  		
  		public String Parse(String url, out String objectURI) 
  		{
  			objectURI = url;
  			return null;
  		}	
  
  		// IChannelReceiver
  		public virtual Object ChannelData 
  		{
  			get { return new CrossAppDomainData(Thread.GetDomainID()); }
  		}	
  		
  		public virtual String[] GetUrlsForUri(String objectURI) 
  		{
  			throw new NotSupportedException("CrossAppdomain channel dont support UrlsForUri");
  		}	
  		
  		// Dummies
  		public virtual void StartListening(Object data) {}
  		public virtual void StopListening(Object data) {}	
  
  		// IChannelSender
  		public virtual IMessageSink CreateMessageSink(String url, Object data, out String uri) 
  		{
  			uri = null;
              
  			if (data != null) 
  			{
  				// Get the data and then get the sink
  				CrossAppDomainData cadData = data as CrossAppDomainData;
  				if (cadData != null && cadData.ProcessID == RemotingConfiguration.ProcessId)
  					// GetSink creates a new sink if we don't have any (use contexts here later)
  					return CrossAppDomainSink.GetSink(cadData.DomainID);
  			} 
  			if (url != null && url.StartsWith(_strName)) 
  				throw new NotSupportedException("Can't create a named channel via crossappdomain");
  
  			return null;
  		}
  	}
  	
  	[MonoTODO("Handle domain unloading?")]
  	internal class CrossAppDomainSink : IMessageSink 
  	{
  		private static Hashtable s_sinks = new Hashtable();
  
  		private static MethodInfo processMessageMethod =
  			typeof (CrossAppDomainSink).GetMethod ("ProcessMessageInDomain", BindingFlags.NonPublic|BindingFlags.Static);
  
  
  		private int _domainID;
  
  		internal CrossAppDomainSink(int domainID) 
  		{
  			_domainID = domainID;
  		}
  		
  		internal static CrossAppDomainSink GetSink(int domainID) 
  		{
  			// Check if we have a sink for the current domainID
  			// note, locking is not to bad here, very few class to GetSink
  			lock (s_sinks.SyncRoot) 
  			{
  				if (s_sinks.ContainsKey(domainID)) 
  					return (CrossAppDomainSink) s_sinks[domainID];
  				else 
  				{
  					CrossAppDomainSink sink = new CrossAppDomainSink(domainID);
  					s_sinks[domainID] = sink;
  
  					return sink;
  				}
  			}
  		}
  		
  		internal int TargetDomainId {
  			get { return _domainID; }
  		}
  
  		private struct ProcessMessageRes {
  			public byte[] arrResponse;
  			public CADMethodReturnMessage cadMrm;
  		}
  
  #pragma warning disable 169
  		private static ProcessMessageRes ProcessMessageInDomain (
  			byte[] arrRequest,
  			CADMethodCallMessage cadMsg)
  	    {
  			ProcessMessageRes res = new ProcessMessageRes ();
  
  			try 
  			{
  				AppDomain.CurrentDomain.ProcessMessageInDomain (arrRequest, cadMsg, out res.arrResponse, out res.cadMrm);
  			}
  			catch (Exception e) 
  			{
  				IMessage errorMsg = new MethodResponse (e, new ErrorMessage());
  				res.arrResponse = CADSerializer.SerializeMessage (errorMsg).GetBuffer(); 
  			}
  			return res;
  		}
  #pragma warning restore 169
  
  		public virtual IMessage SyncProcessMessage(IMessage msgRequest) 
  		{
  			IMessage retMessage = null;
  
  			try 
  			{
  				// Time to transit into the "our" domain
  				byte [] arrResponse = null;
  				byte [] arrRequest = null; 
  				
  				CADMethodReturnMessage cadMrm = null;
  				CADMethodCallMessage cadMsg;
  				
  				cadMsg = CADMethodCallMessage.Create (msgRequest);
  				if (null == cadMsg) {
  					// Serialize the request message
  					MemoryStream reqMsgStream = CADSerializer.SerializeMessage(msgRequest);
  					arrRequest = reqMsgStream.GetBuffer();
  				}
  
  				Context currentContext = Thread.CurrentContext;
  
  				try {
  					// InternalInvoke can't handle out arguments, this is why
  					// we return the results in a structure
  					ProcessMessageRes res = (ProcessMessageRes)AppDomain.InvokeInDomainByID (_domainID, processMessageMethod, null, new object [] { arrRequest, cadMsg });
  					arrResponse = res.arrResponse;
  					cadMrm = res.cadMrm;
  				} finally {
  					AppDomain.InternalSetContext (currentContext);
  				}					
  
  				
  				if (null != arrResponse) {
  					// Time to deserialize the message
  					MemoryStream respMsgStream = new MemoryStream(arrResponse);
  
  					// Deserialize the response message
  					retMessage = CADSerializer.DeserializeMessage(respMsgStream, msgRequest as IMethodCallMessage);
  				} else
  					retMessage = new MethodResponse (msgRequest as IMethodCallMessage, cadMrm);
  			}
  			catch (Exception e) 
  			{
  				try
  				{
  					retMessage = new ReturnMessage (e, msgRequest as IMethodCallMessage);
  				}
  				catch (Exception)
  				{
  					// this is just to be sure
  				}
  			}
  
  	    	return retMessage;
  		}
  
  		public virtual IMessageCtrl AsyncProcessMessage (IMessage reqMsg, IMessageSink replySink) 
  		{
  			AsyncRequest req = new AsyncRequest (reqMsg, replySink);
  			ThreadPool.QueueUserWorkItem (new WaitCallback ((data) => {
  				try {
  					SendAsyncMessage (data);
  				} catch {}
  				}
  				), req);
  			return null;
  		}
  		
  		public void SendAsyncMessage (object data)
  		{
  			AsyncRequest req = (AsyncRequest)data;
  			IMessage response = SyncProcessMessage (req.MsgRequest);
  			req.ReplySink.SyncProcessMessage (response);
  		}
  		
  		public IMessageSink NextSink { get { return null; } }
  	}
  
  	internal class CADSerializer 
  	{
  		internal static IMessage DeserializeMessage(MemoryStream mem, IMethodCallMessage msg)
  		{
  			BinaryFormatter serializer = new BinaryFormatter();                
  
  			serializer.SurrogateSelector = null;
  			mem.Position = 0;
  
  			if (msg == null)
  				return (IMessage) serializer.Deserialize(mem, null);
  			else
  				return (IMessage) serializer.DeserializeMethodResponse(mem, null, msg);
  		}
  		
  		internal static MemoryStream SerializeMessage(IMessage msg)
  		{
  			MemoryStream mem = new MemoryStream ();
  
gt: 			BinaryFormatter serializer = new BinaryFormatter ();                