// // 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 private int _ContextID; private int _DomainID; private string _processGuid; internal CrossAppDomainData(int domainId) { _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 const String _strBaseURI = "MONOCADURI"; 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; } 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; } 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(); } object threadStatus = Thread.ResetDataStoreStatus (); 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); Thread.RestoreDataStoreStatus (threadStatus); } 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 (SendAsyncMessage), 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 (); BinaryFormatter serializer = new BinaryFormatter (); serializer.SurrogateSelector = new RemotingSurrogateSelector (); serializer.Serialize (mem, msg); mem.Position = 0; return mem; } internal static MemoryStream SerializeObject(object obj) { MemoryStream mem = new MemoryStream (); BinaryFormatter serializer = new BinaryFormatter (); serializer.SurrogateSelector = new RemotingSurrogateSelector (); serializer.Serialize (mem, obj); mem.Position = 0; return mem; } internal static object DeserializeObject(MemoryStream mem) { BinaryFormatter serializer = new BinaryFormatter(); serializer.SurrogateSelector = null; mem.Position = 0; return serializer.Deserialize (mem); } } internal class AsyncRequest { internal IMessageSink ReplySink; internal IMessage MsgRequest; public AsyncRequest (IMessage msgRequest, IMessageSink replySink) { ReplySink = replySink; MsgRequest = msgRequest; } } }