CrossAppDomainChannel.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / clr / src / BCL / System / Runtime / Remoting / CrossAppDomainChannel.cs / 1 / CrossAppDomainChannel.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
//
// Remoting Infrastructure Sink for making calls across context 
// boundaries. 
//
namespace System.Runtime.Remoting.Channels { 
    using System;
    using System.Collections;
    using System.IO;
    using System.Runtime.InteropServices; 
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Messaging; 
    using System.Runtime.Remoting.Contexts; 
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary; 
    using System.Security;
    using System.Security.Permissions;
    using System.Security.Policy;
    using System.Security.Principal; 
    using System.Text;
    using System.Threading; 
    using System.Runtime.ConstrainedExecution; 

 

    [Serializable]
    internal class CrossAppDomainChannel : IChannel, IChannelSender, IChannelReceiver
    { 
        private const String _channelName = "XAPPDMN";
        private const String _channelURI = "XAPPDMN_URI"; 
 

        private static CrossAppDomainChannel gAppDomainChannel 
        {
            get { return Thread.GetDomain().RemotingData.ChannelServicesData.xadmessageSink; }
            set { Thread.GetDomain().RemotingData.ChannelServicesData.xadmessageSink = value; }
        } 
        private static Object staticSyncObject = new Object();
        private static PermissionSet s_fullTrust = new PermissionSet(PermissionState.Unrestricted); 
 
        internal static CrossAppDomainChannel AppDomainChannel
        { 
            get
            {
                if (gAppDomainChannel == null)
                { 
                    CrossAppDomainChannel tmpChnl = new CrossAppDomainChannel();
 
                    lock (staticSyncObject) 
                    {
                        if (gAppDomainChannel == null) 
                        {
                            gAppDomainChannel = tmpChnl;
                        }
                    } 
                }
                return gAppDomainChannel; 
 
            }
        } 

        internal static void RegisterChannel()
        {
            CrossAppDomainChannel adc = CrossAppDomainChannel.AppDomainChannel; 
            ChannelServices.RegisterChannelInternal((IChannel)adc, false /*ensureSecurity*/);
        } 
 
        //
        // IChannel Methods 
        //
        public virtual String ChannelName
        {
            get{ return _channelName; } 
        }
 
        public virtual String ChannelURI 
        {
            get{ return _channelURI; } 
        }

        public virtual int ChannelPriority
        { 
            get{ return 100;}
        } 
 
        public String Parse(String url, out String objectURI)
        { 
            objectURI = url;
            return null;
        }
 
        public virtual Object ChannelData
        { 
            get 
            {
                return new CrossAppDomainData( 
                                    Context.DefaultContext.InternalContextID,
                                    Thread.GetDomain().GetId(),
                                    Identity.ProcessGuid);
            } 
        }
 
 
        public virtual IMessageSink CreateMessageSink(String url, Object data,
                                                      out String objectURI) 
        {
            // Set the out parameters
            objectURI = null;
            IMessageSink sink = null; 

            // < 
 

            if ((null != url) && (data == null)) 
            {
                if(url.StartsWith(_channelName, StringComparison.Ordinal))
                {
                    throw new RemotingException( 
                        Environment.GetResourceString(
                            "Remoting_AppDomains_NYI")); 
                } 
            }
            else 
            {
                Message.DebugOut("XAPPDOMAIN::Creating sink for data \n");
                CrossAppDomainData xadData = data as CrossAppDomainData;
                if (null != xadData) 
                {
                    if (xadData.ProcessGuid.Equals(Identity.ProcessGuid)) 
                    { 
                        sink = CrossAppDomainSink.FindOrCreateSink(xadData);
                    } 
                }
            }
            return sink;
        } 

        public virtual String[] GetUrlsForUri(String objectURI) 
        { 
            throw new NotSupportedException(
                Environment.GetResourceString( 
                    "NotSupported_Method"));
            //<

 
        }
 
        public virtual void StartListening(Object data) 
        {
 
        }

        public virtual void StopListening(Object data)
        { 

        } 
    } 

    [Serializable] 
    internal class CrossAppDomainData
    {
        Object _ContextID = 0;      // This is for backward compatibility
        int _DomainID;  // server appDomain ID 
        String _processGuid;    // idGuid for the process (shared static)
 
        internal virtual IntPtr ContextID { 
            get {
#if WIN32 
                    return new IntPtr((int)_ContextID);
#else
                    return new IntPtr((long)_ContextID);
#endif 
            }
        } 
        internal virtual int DomainID { 
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            get {return _DomainID;} 
        }

        internal virtual String ProcessGuid { get {return _processGuid;}}
 
        internal CrossAppDomainData(IntPtr ctxId, int domainID, String processGuid)
        { 
            _DomainID = domainID; 
            _processGuid = processGuid;
#if WIN32 
            _ContextID = ctxId.ToInt32();
#else
            _ContextID = ctxId.ToInt64();  // This would have never worked anyway
#endif 
        }
 
        internal bool IsFromThisProcess() 
        {
            return Identity.ProcessGuid.Equals(_processGuid); 
        }

        internal bool IsFromThisAppDomain()
        { 
            return  IsFromThisProcess()
                    && 
                    (Thread.GetDomain().GetId() == _DomainID); 
        }
    } 
   //    Implements the Message Sink provided by the X-AppDomain channel.
   //    We try to use one instance of the sink to make calls to all remote
   //    objects in another AppDomain from one AppDomain.
    internal class CrossAppDomainSink 
        : InternalSink, IMessageSink
    { 
        internal const int GROW_BY = 0x8; 
        internal static int[] _sinkKeys;
        internal static CrossAppDomainSink[] _sinks; 

        internal const string LCC_DATA_KEY = "__xADCall";

        private static Object staticSyncObject = new Object(); 
        private static InternalCrossContextDelegate s_xctxDel = new InternalCrossContextDelegate(DoTransitionDispatchCallback);
 
        // each sink stores the default ContextID of the server side domain 
        // and the domain ID for the domain
        internal CrossAppDomainData _xadData; 

        internal CrossAppDomainSink(CrossAppDomainData xadData)
        {
            // 
            // WARNING: xadData.ContextID may not be valid at this point.  Because
            //          CrossAppDomainData._ContextID is an IntPtr and IntPtrs are 
            //          value types, the deserializer has to wait until the very 
            //          end of deserialization to fixup value types.  However, when
            //          we unmarshal objects, we need to setup the x-AD sink and 
            //          initialize it with this data.  Fortunately, that data won't
            //          be consumed until deserialization is complete, so we just
            //          need to take care not to read _ContextID in the constructor.
            //          The xadData object ref will be finalized by the time we need 
            //          to consume its contents and everything should work properly.
            // 
            _xadData = xadData; 
        }
 
        // Note: this should be called from within a synch-block
        internal static void GrowArrays(int oldSize)
        {
            if (_sinks == null) 
            {
                _sinks = new CrossAppDomainSink[GROW_BY]; 
                _sinkKeys = new int[GROW_BY]; 
            }
            else 
            {
                CrossAppDomainSink[] tmpSinks = new CrossAppDomainSink[_sinks.Length + GROW_BY];
                int[] tmpKeys = new int[_sinkKeys.Length + GROW_BY];
                Array.Copy(_sinks, tmpSinks, _sinks.Length); 
                Array.Copy(_sinkKeys, tmpKeys, _sinkKeys.Length);
                _sinks = tmpSinks; 
                _sinkKeys = tmpKeys; 
            }
        } 
        internal static CrossAppDomainSink FindOrCreateSink(CrossAppDomainData xadData)
        {
            //
            // WARNING: Do not read any value type member of xadData in this method!! 
            //          xadData is not completely deserialized at this point.  See
            //          warning in CrossAppDomainSink::.ctor above 
            // 
            lock(staticSyncObject) {
                // Note: keep this in [....] with DomainUnloaded below 
                int key = xadData.DomainID;
                if (_sinks == null)
                {
                    GrowArrays(0); 
                }
                int i=0; 
                while (_sinks[i] != null) 
                {
                    if (_sinkKeys[i] == key) 
                    {
                        return _sinks[i];
                    }
                    i++; 
                    if (i == _sinks.Length)
                    { 
                        // could not find a sink, also need to Grow the array. 
                        GrowArrays(i);
                        break; 
                    }
                }
                // At this point we need to create a new sink and cache
                // it at location "i" 
                _sinks[i] = new CrossAppDomainSink(xadData);
                _sinkKeys[i] = key; 
                return _sinks[i]; 
            }
        } 

        internal static void DomainUnloaded(Int32 domainID)
        {
            int key = domainID; 
            lock(staticSyncObject) {
                if (_sinks == null) 
                { 
                    return;
                } 
                // Note: keep this in [....] with FindOrCreateSink
                int i = 0;
                int remove = -1;
                while (_sinks[i] != null) 
                {
                    if (_sinkKeys[i] == key) 
                    { 
                        BCLDebug.Assert(remove == -1, "multiple sinks?");
                        remove = i; 
                    }
                    i++;
                    if (i == _sinks.Length)
                    { 
                        break;
                    } 
                } 

                if (remove ==-1) //hasn't been initialized yet 
                    return;

                // The sink to remove is at index 'remove'
                // We will move the last non-null entry to this location 

                BCLDebug.Assert(remove != -1, "Bad domainId for unload?"); 
                _sinkKeys[remove] = _sinkKeys[i-1]; 
                _sinks[remove] = _sinks[i-1];
                _sinkKeys[i-1] = 0; 
                _sinks[i-1] = null;
            }

        } 

 
        internal static byte[] DoDispatch(byte[] reqStmBuff, 
                                          SmuggledMethodCallMessage smuggledMcm,
                                          out SmuggledMethodReturnMessage smuggledMrm) 
        {
            //*********************** DE-SERIALIZE REQ-MSG ********************

            IMessage desReqMsg = null; 

            if (smuggledMcm != null) 
            { 
                ArrayList deserializedArgs = smuggledMcm.FixupForNewAppDomain();
                desReqMsg = new MethodCall(smuggledMcm, deserializedArgs); 
            }
            else
            {
                MemoryStream reqStm = new MemoryStream(reqStmBuff); 
                desReqMsg = CrossAppDomainSerializer.DeserializeMessage(reqStm);
            } 
 
            LogicalCallContext lcc = CallContext.GetLogicalCallContext();
            lcc.SetData(LCC_DATA_KEY, true); 
            // now we can delegate to the DispatchMessage to do the rest

            IMessage retMsg = ChannelServices.SyncDispatchMessage(desReqMsg);
            lcc.FreeNamedDataSlot(LCC_DATA_KEY); 

            smuggledMrm = SmuggledMethodReturnMessage.SmuggleIfPossible(retMsg); 
            if (smuggledMrm != null) 
            {
                return null; 
            }
            else
            {
                if (retMsg != null) 
                {
                    // Null out the principal since we won't use it on the other side. 
                    // This is handled inside of SmuggleIfPossible for method call 
                    // messages.
                    LogicalCallContext callCtx = (LogicalCallContext) 
                        retMsg.Properties[Message.CallContextKey];
                    if (callCtx != null)
                    {
                        if (callCtx.Principal != null) 
                            callCtx.Principal = null;
                    } 
 
                    return CrossAppDomainSerializer.SerializeMessage(retMsg).GetBuffer();
                } 

                //*********************** SERIALIZE RET-MSG ********************
                return null;
            } 
        } // DoDispatch
 
        internal static Object DoTransitionDispatchCallback(Object[] args) 
        {
            byte[]                       reqStmBuff     = (byte[])args[0]; 
            SmuggledMethodCallMessage    smuggledMcm    = (SmuggledMethodCallMessage)args[1];
            SmuggledMethodReturnMessage  smuggledMrm    = null;
            byte[]                       retBuff        = null;
 
            try
            { 
                Message.DebugOut("#### : changed to Server Domain :: "+ (Thread.CurrentContext.InternalContextID).ToString("X") ); 

                retBuff = DoDispatch(reqStmBuff, smuggledMcm, out smuggledMrm); 
            }
            catch (Exception e)
            {
                // This will catch exceptions thrown by the infrastructure, 
                // Serialization/Deserialization etc
                // Those thrown by the server are already taken care of 
                // and encoded in the retMsg .. so we don't come here for 
                // that case.
 
                // We are in another appDomain, so we can't simply throw
                // the exception object across. The following marshals it
                // into a serialized return message.
                IMessage retMsg = 
                    new ReturnMessage(e, new ErrorMessage());
                //*********************** SERIALIZE RET-MSG ****************** 
                retBuff = CrossAppDomainSerializer.SerializeMessage(retMsg).GetBuffer(); 
                retMsg = null;
            } 

            args[2] = smuggledMrm;

            return retBuff; 
        }
 
        internal byte[] DoTransitionDispatch( 
            byte[] reqStmBuff,
            SmuggledMethodCallMessage smuggledMcm, 
            out SmuggledMethodReturnMessage smuggledMrm)
        {
            byte[] retBuff = null;
 
            Object[] args = new Object[] { reqStmBuff, smuggledMcm, null };
 
            retBuff = (byte[]) Thread.CurrentThread.InternalCrossContextCallback(null, 
                                                              _xadData.ContextID,
                                                              _xadData.DomainID, 
                                                              s_xctxDel,
                                                              args);

            Message.DebugOut("#### : changed back to Client Domain " + (Thread.CurrentContext.InternalContextID).ToString("X")); 

            smuggledMrm = (SmuggledMethodReturnMessage) args[2]; 
 
            // System.Diagnostics.Debugger.Break();
            return retBuff; 
        } // DoTransitionDispatch

        public virtual IMessage SyncProcessMessage(IMessage reqMsg)
        { 
            Message.DebugOut("\n::::::::::::::::::::::::: CrossAppDomain Channel: [....] call starting");
            IMessage errMsg = InternalSink.ValidateMessage(reqMsg); 
            if (errMsg != null) 
            {
                return errMsg; 
            }


            // currentPrincipal is used to save the current principal. It should be 
            //   restored on the reply message.
            IPrincipal currentPrincipal = null; 
 

            IMessage desRetMsg = null; 

            try
            {
                IMethodCallMessage mcmReqMsg = reqMsg as IMethodCallMessage; 
                if (mcmReqMsg != null)
                { 
                    LogicalCallContext lcc = mcmReqMsg.LogicalCallContext; 
                    if (lcc != null)
                    { 
                        // Special case Principal since if might not be serializable
                        currentPrincipal = lcc.RemovePrincipalIfNotSerializable();
                    }
                } 

                MemoryStream reqStm = null; 
                SmuggledMethodCallMessage smuggledMcm = SmuggledMethodCallMessage.SmuggleIfPossible(reqMsg); 

                if (smuggledMcm == null) 
                {

                    //*********************** SERIALIZE REQ-MSG ****************
                    // Deserialization of objects requires permissions that users 
                    // of remoting are not guaranteed to possess. Since remoting
                    // can guarantee that it's users can't abuse deserialization 
                    // (since it won't allow them to pass in raw blobs of 
                    // serialized data), it should assert the permissions
                    // necessary before calling the deserialization code. This 
                    // will terminate the security stackwalk caused when
                    // serialization checks for the correct permissions at the
                    // remoting stack frame so the check won't continue on to
                    // the user and fail. [from [....]] 
                    // We will hold off from doing this for x-process channels
                    // until the big picture of distributed security is finalized. 
 
                    reqStm = CrossAppDomainSerializer.SerializeMessage(reqMsg);
                } 

                // Retrieve calling caller context here, where it is safe from the view
                // of app domain checking code
                LogicalCallContext oldCallCtx = CallContext.SetLogicalCallContext(null); 

                // Call helper method here, to avoid confusion with stack frames & app domains 
                MemoryStream retStm = null; 
                byte[] responseBytes = null;
                SmuggledMethodReturnMessage smuggledMrm; 

                try
                {
                    if (smuggledMcm != null) 
                        responseBytes = DoTransitionDispatch(null, smuggledMcm, out smuggledMrm);
                    else 
                        responseBytes = DoTransitionDispatch(reqStm.GetBuffer(), null, out smuggledMrm); 
                }
                finally 
                {
                    CallContext.SetLogicalCallContext(oldCallCtx);
                }
 
                if (smuggledMrm != null)
                { 
                    ArrayList deserializedArgs = smuggledMrm.FixupForNewAppDomain(); 
                    desRetMsg = new MethodResponse((IMethodCallMessage)reqMsg,
                                                   smuggledMrm, 
                                                   deserializedArgs);
                }
                else
                { 
                    if (responseBytes != null) {
                        retStm = new MemoryStream(responseBytes); 
 
                        Message.DebugOut("::::::::::::::::::::::::::: CrossAppDomain Channel: [....] call returning!!\n");
                        //*********************** DESERIALIZE RET-MSG ************** 
                        desRetMsg = CrossAppDomainSerializer.DeserializeMessage(retStm, reqMsg as IMethodCallMessage);
                    }
                }
            } 
            catch(Exception e)
            { 
                Message.DebugOut("Arrgh.. XAppDomainSink::throwing exception " + e + "\n"); 
                try
                { 
                    desRetMsg = new ReturnMessage(e, (reqMsg as IMethodCallMessage));
                }
                catch(Exception )
                { 
                    // Fatal Error .. can't do much here
                } 
            } 

            // restore the principal if necessary. 
            if (currentPrincipal != null)
            {
                IMethodReturnMessage mrmRetMsg = desRetMsg as IMethodReturnMessage;
                if (mrmRetMsg != null) 
                {
                    LogicalCallContext lcc = mrmRetMsg.LogicalCallContext; 
                    lcc.Principal = currentPrincipal; 
                }
            } 

            return desRetMsg;
        }
 
        public virtual IMessageCtrl AsyncProcessMessage(IMessage reqMsg, IMessageSink replySink)
        { 
            // This is the case where we take care of returning the calling 
            // thread asap by using the ThreadPool for completing the call.
 
            // we use a more elaborate WorkItem and delegate the work to the thread pool
            ADAsyncWorkItem workItem = new ADAsyncWorkItem(reqMsg,
                                        (IMessageSink)this, /* nextSink */
                                        replySink); 

            WaitCallback threadFunc = new WaitCallback(workItem.FinishAsyncWork); 
            ThreadPool.QueueUserWorkItem(threadFunc); 

            return null; 
        }

        public IMessageSink NextSink
        { 
            get
            { 
                // We are a terminating sink for this chain 
                return null;
            } 
        }

    }
 
    /* package */
    internal class ADAsyncWorkItem 
    { 
        // the replySink passed in to us in AsyncProcessMsg
        private IMessageSink _replySink; 

        // the nextSink we have to call
        private IMessageSink _nextSink;
 
        private LogicalCallContext _callCtx;
 
        // the request msg passed in 
        private IMessage _reqMsg;
 
        internal ADAsyncWorkItem(IMessage reqMsg, IMessageSink nextSink, IMessageSink replySink)
        {
            _reqMsg = reqMsg;
            _nextSink = nextSink; 
            _replySink = replySink;
            _callCtx = CallContext.GetLogicalCallContext(); 
        } 

        /* package */ 
        internal virtual void FinishAsyncWork(Object stateIgnored)
        {
            // install the call context that the calling thread actually had onto
            // the threadPool thread. 
            LogicalCallContext threadPoolCallCtx = CallContext.SetLogicalCallContext(_callCtx);
 
            IMessage retMsg = _nextSink.SyncProcessMessage(_reqMsg); 

            // send the reply back to the replySink we were provided with 
            // note: replySink may be null for one-way calls.
            if (_replySink != null)
            {
                _replySink.SyncProcessMessage(retMsg); 
            }
            CallContext.SetLogicalCallContext(threadPoolCallCtx); 
        } 
    }
 

    internal static class CrossAppDomainSerializer
    {
        internal static MemoryStream SerializeMessage(IMessage msg) 
        {
            MemoryStream stm = new MemoryStream(); 
            RemotingSurrogateSelector ss = new RemotingSurrogateSelector(); 
            BinaryFormatter fmt = new BinaryFormatter();
            fmt.SurrogateSelector = ss; 
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
            fmt.Serialize(stm, msg, null, false /* No Security check */);

            // Reset the stream so that Deserialize happens correctly 
            stm.Position = 0;
 
            return stm; 
        }
 
#if false
        // called from MessageSmuggler classes
        internal static MemoryStream SerializeMessageParts(ArrayList argsToSerialize, out Object[] smuggledArgs)
        { 
            MemoryStream stm = new MemoryStream();
 
            BinaryFormatter fmt = new BinaryFormatter(); 
            RemotingSurrogateSelector ss = new RemotingSurrogateSelector();
            fmt.SurrogateSelector = ss; 
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
            fmt.Serialize(stm, argsToSerialize, null, false ); // No Security check

            smuggledArgs = fmt.CrossAppDomainArray; 
            stm.Position = 0;
            return stm; 
        } // SerializeMessageParts 
#endif
 
        internal static MemoryStream SerializeMessageParts(ArrayList argsToSerialize)
        {
            MemoryStream stm = new MemoryStream();
 
            BinaryFormatter fmt = new BinaryFormatter();
            RemotingSurrogateSelector ss = new RemotingSurrogateSelector(); 
            fmt.SurrogateSelector = ss; 
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
            fmt.Serialize(stm, argsToSerialize, null, false /* No Security check */); 

            stm.Position = 0;
            return stm;
        } // SerializeMessageParts 

        // called from MessageSmuggler classes 
        internal static void SerializeObject(Object obj, MemoryStream stm) 
        {
            BinaryFormatter fmt = new BinaryFormatter(); 
            RemotingSurrogateSelector ss = new RemotingSurrogateSelector();
            fmt.SurrogateSelector = ss;
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
            fmt.Serialize(stm, obj, null, false /* No Security check */); 
        } // SerializeMessageParts
 
        // called from MessageSmuggler classes 
        internal static MemoryStream SerializeObject(Object obj)
        { 
            MemoryStream stm = new MemoryStream();

            SerializeObject( obj, stm );
 
            stm.Position = 0;
            return stm; 
        } // SerializeMessageParts 

 
        internal static IMessage DeserializeMessage(MemoryStream stm)
        {
            return DeserializeMessage(stm, null);
        } 

        internal static IMessage DeserializeMessage( 
            MemoryStream stm, IMethodCallMessage reqMsg) 
        {
            if (stm == null) 
                throw new ArgumentNullException("stm");

            stm.Position = 0;
            BinaryFormatter fmt = new BinaryFormatter(); 
            fmt.SurrogateSelector = null;
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain); 
 
            return (IMessage) fmt.Deserialize(stm, null, false /* No Security check */, true/*isCrossAppDomain*/, reqMsg);
        } 

#if false
        // called from MessageSmuggler classes
        internal static ArrayList DeserializeMessageParts(MemoryStream stm, Object[] args) 
        {
            stm.Position = 0; 
 
            BinaryFormatter fmt = new BinaryFormatter();
            fmt.CrossAppDomainArray = args; 
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
            return (ArrayList) fmt.Deserialize(stm, null, false/*checkSEcurity*/, true/*isCrossAppDomain*/, null);
        } // DeserializeMessageParts
#endif 

        internal static ArrayList DeserializeMessageParts(MemoryStream stm) 
        { 
            return (ArrayList) DeserializeObject(stm);
 
        } // DeserializeMessageParts


        internal static Object DeserializeObject(MemoryStream stm) 
        {
            stm.Position = 0; 
 
            BinaryFormatter fmt = new BinaryFormatter();
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain); 
            return fmt.Deserialize(stm, null, false /* No Security check */, true/*isCrossAppDomain*/, null);
        } // DeserializeMessageParts
    }
 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
//
// Remoting Infrastructure Sink for making calls across context 
// boundaries. 
//
namespace System.Runtime.Remoting.Channels { 
    using System;
    using System.Collections;
    using System.IO;
    using System.Runtime.InteropServices; 
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Messaging; 
    using System.Runtime.Remoting.Contexts; 
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary; 
    using System.Security;
    using System.Security.Permissions;
    using System.Security.Policy;
    using System.Security.Principal; 
    using System.Text;
    using System.Threading; 
    using System.Runtime.ConstrainedExecution; 

 

    [Serializable]
    internal class CrossAppDomainChannel : IChannel, IChannelSender, IChannelReceiver
    { 
        private const String _channelName = "XAPPDMN";
        private const String _channelURI = "XAPPDMN_URI"; 
 

        private static CrossAppDomainChannel gAppDomainChannel 
        {
            get { return Thread.GetDomain().RemotingData.ChannelServicesData.xadmessageSink; }
            set { Thread.GetDomain().RemotingData.ChannelServicesData.xadmessageSink = value; }
        } 
        private static Object staticSyncObject = new Object();
        private static PermissionSet s_fullTrust = new PermissionSet(PermissionState.Unrestricted); 
 
        internal static CrossAppDomainChannel AppDomainChannel
        { 
            get
            {
                if (gAppDomainChannel == null)
                { 
                    CrossAppDomainChannel tmpChnl = new CrossAppDomainChannel();
 
                    lock (staticSyncObject) 
                    {
                        if (gAppDomainChannel == null) 
                        {
                            gAppDomainChannel = tmpChnl;
                        }
                    } 
                }
                return gAppDomainChannel; 
 
            }
        } 

        internal static void RegisterChannel()
        {
            CrossAppDomainChannel adc = CrossAppDomainChannel.AppDomainChannel; 
            ChannelServices.RegisterChannelInternal((IChannel)adc, false /*ensureSecurity*/);
        } 
 
        //
        // IChannel Methods 
        //
        public virtual String ChannelName
        {
            get{ return _channelName; } 
        }
 
        public virtual String ChannelURI 
        {
            get{ return _channelURI; } 
        }

        public virtual int ChannelPriority
        { 
            get{ return 100;}
        } 
 
        public String Parse(String url, out String objectURI)
        { 
            objectURI = url;
            return null;
        }
 
        public virtual Object ChannelData
        { 
            get 
            {
                return new CrossAppDomainData( 
                                    Context.DefaultContext.InternalContextID,
                                    Thread.GetDomain().GetId(),
                                    Identity.ProcessGuid);
            } 
        }
 
 
        public virtual IMessageSink CreateMessageSink(String url, Object data,
                                                      out String objectURI) 
        {
            // Set the out parameters
            objectURI = null;
            IMessageSink sink = null; 

            // < 
 

            if ((null != url) && (data == null)) 
            {
                if(url.StartsWith(_channelName, StringComparison.Ordinal))
                {
                    throw new RemotingException( 
                        Environment.GetResourceString(
                            "Remoting_AppDomains_NYI")); 
                } 
            }
            else 
            {
                Message.DebugOut("XAPPDOMAIN::Creating sink for data \n");
                CrossAppDomainData xadData = data as CrossAppDomainData;
                if (null != xadData) 
                {
                    if (xadData.ProcessGuid.Equals(Identity.ProcessGuid)) 
                    { 
                        sink = CrossAppDomainSink.FindOrCreateSink(xadData);
                    } 
                }
            }
            return sink;
        } 

        public virtual String[] GetUrlsForUri(String objectURI) 
        { 
            throw new NotSupportedException(
                Environment.GetResourceString( 
                    "NotSupported_Method"));
            //<

 
        }
 
        public virtual void StartListening(Object data) 
        {
 
        }

        public virtual void StopListening(Object data)
        { 

        } 
    } 

    [Serializable] 
    internal class CrossAppDomainData
    {
        Object _ContextID = 0;      // This is for backward compatibility
        int _DomainID;  // server appDomain ID 
        String _processGuid;    // idGuid for the process (shared static)
 
        internal virtual IntPtr ContextID { 
            get {
#if WIN32 
                    return new IntPtr((int)_ContextID);
#else
                    return new IntPtr((long)_ContextID);
#endif 
            }
        } 
        internal virtual int DomainID { 
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            get {return _DomainID;} 
        }

        internal virtual String ProcessGuid { get {return _processGuid;}}
 
        internal CrossAppDomainData(IntPtr ctxId, int domainID, String processGuid)
        { 
            _DomainID = domainID; 
            _processGuid = processGuid;
#if WIN32 
            _ContextID = ctxId.ToInt32();
#else
            _ContextID = ctxId.ToInt64();  // This would have never worked anyway
#endif 
        }
 
        internal bool IsFromThisProcess() 
        {
            return Identity.ProcessGuid.Equals(_processGuid); 
        }

        internal bool IsFromThisAppDomain()
        { 
            return  IsFromThisProcess()
                    && 
                    (Thread.GetDomain().GetId() == _DomainID); 
        }
    } 
   //    Implements the Message Sink provided by the X-AppDomain channel.
   //    We try to use one instance of the sink to make calls to all remote
   //    objects in another AppDomain from one AppDomain.
    internal class CrossAppDomainSink 
        : InternalSink, IMessageSink
    { 
        internal const int GROW_BY = 0x8; 
        internal static int[] _sinkKeys;
        internal static CrossAppDomainSink[] _sinks; 

        internal const string LCC_DATA_KEY = "__xADCall";

        private static Object staticSyncObject = new Object(); 
        private static InternalCrossContextDelegate s_xctxDel = new InternalCrossContextDelegate(DoTransitionDispatchCallback);
 
        // each sink stores the default ContextID of the server side domain 
        // and the domain ID for the domain
        internal CrossAppDomainData _xadData; 

        internal CrossAppDomainSink(CrossAppDomainData xadData)
        {
            // 
            // WARNING: xadData.ContextID may not be valid at this point.  Because
            //          CrossAppDomainData._ContextID is an IntPtr and IntPtrs are 
            //          value types, the deserializer has to wait until the very 
            //          end of deserialization to fixup value types.  However, when
            //          we unmarshal objects, we need to setup the x-AD sink and 
            //          initialize it with this data.  Fortunately, that data won't
            //          be consumed until deserialization is complete, so we just
            //          need to take care not to read _ContextID in the constructor.
            //          The xadData object ref will be finalized by the time we need 
            //          to consume its contents and everything should work properly.
            // 
            _xadData = xadData; 
        }
 
        // Note: this should be called from within a synch-block
        internal static void GrowArrays(int oldSize)
        {
            if (_sinks == null) 
            {
                _sinks = new CrossAppDomainSink[GROW_BY]; 
                _sinkKeys = new int[GROW_BY]; 
            }
            else 
            {
                CrossAppDomainSink[] tmpSinks = new CrossAppDomainSink[_sinks.Length + GROW_BY];
                int[] tmpKeys = new int[_sinkKeys.Length + GROW_BY];
                Array.Copy(_sinks, tmpSinks, _sinks.Length); 
                Array.Copy(_sinkKeys, tmpKeys, _sinkKeys.Length);
                _sinks = tmpSinks; 
                _sinkKeys = tmpKeys; 
            }
        } 
        internal static CrossAppDomainSink FindOrCreateSink(CrossAppDomainData xadData)
        {
            //
            // WARNING: Do not read any value type member of xadData in this method!! 
            //          xadData is not completely deserialized at this point.  See
            //          warning in CrossAppDomainSink::.ctor above 
            // 
            lock(staticSyncObject) {
                // Note: keep this in [....] with DomainUnloaded below 
                int key = xadData.DomainID;
                if (_sinks == null)
                {
                    GrowArrays(0); 
                }
                int i=0; 
                while (_sinks[i] != null) 
                {
                    if (_sinkKeys[i] == key) 
                    {
                        return _sinks[i];
                    }
                    i++; 
                    if (i == _sinks.Length)
                    { 
                        // could not find a sink, also need to Grow the array. 
                        GrowArrays(i);
                        break; 
                    }
                }
                // At this point we need to create a new sink and cache
                // it at location "i" 
                _sinks[i] = new CrossAppDomainSink(xadData);
                _sinkKeys[i] = key; 
                return _sinks[i]; 
            }
        } 

        internal static void DomainUnloaded(Int32 domainID)
        {
            int key = domainID; 
            lock(staticSyncObject) {
                if (_sinks == null) 
                { 
                    return;
                } 
                // Note: keep this in [....] with FindOrCreateSink
                int i = 0;
                int remove = -1;
                while (_sinks[i] != null) 
                {
                    if (_sinkKeys[i] == key) 
                    { 
                        BCLDebug.Assert(remove == -1, "multiple sinks?");
                        remove = i; 
                    }
                    i++;
                    if (i == _sinks.Length)
                    { 
                        break;
                    } 
                } 

                if (remove ==-1) //hasn't been initialized yet 
                    return;

                // The sink to remove is at index 'remove'
                // We will move the last non-null entry to this location 

                BCLDebug.Assert(remove != -1, "Bad domainId for unload?"); 
                _sinkKeys[remove] = _sinkKeys[i-1]; 
                _sinks[remove] = _sinks[i-1];
                _sinkKeys[i-1] = 0; 
                _sinks[i-1] = null;
            }

        } 

 
        internal static byte[] DoDispatch(byte[] reqStmBuff, 
                                          SmuggledMethodCallMessage smuggledMcm,
                                          out SmuggledMethodReturnMessage smuggledMrm) 
        {
            //*********************** DE-SERIALIZE REQ-MSG ********************

            IMessage desReqMsg = null; 

            if (smuggledMcm != null) 
            { 
                ArrayList deserializedArgs = smuggledMcm.FixupForNewAppDomain();
                desReqMsg = new MethodCall(smuggledMcm, deserializedArgs); 
            }
            else
            {
                MemoryStream reqStm = new MemoryStream(reqStmBuff); 
                desReqMsg = CrossAppDomainSerializer.DeserializeMessage(reqStm);
            } 
 
            LogicalCallContext lcc = CallContext.GetLogicalCallContext();
            lcc.SetData(LCC_DATA_KEY, true); 
            // now we can delegate to the DispatchMessage to do the rest

            IMessage retMsg = ChannelServices.SyncDispatchMessage(desReqMsg);
            lcc.FreeNamedDataSlot(LCC_DATA_KEY); 

            smuggledMrm = SmuggledMethodReturnMessage.SmuggleIfPossible(retMsg); 
            if (smuggledMrm != null) 
            {
                return null; 
            }
            else
            {
                if (retMsg != null) 
                {
                    // Null out the principal since we won't use it on the other side. 
                    // This is handled inside of SmuggleIfPossible for method call 
                    // messages.
                    LogicalCallContext callCtx = (LogicalCallContext) 
                        retMsg.Properties[Message.CallContextKey];
                    if (callCtx != null)
                    {
                        if (callCtx.Principal != null) 
                            callCtx.Principal = null;
                    } 
 
                    return CrossAppDomainSerializer.SerializeMessage(retMsg).GetBuffer();
                } 

                //*********************** SERIALIZE RET-MSG ********************
                return null;
            } 
        } // DoDispatch
 
        internal static Object DoTransitionDispatchCallback(Object[] args) 
        {
            byte[]                       reqStmBuff     = (byte[])args[0]; 
            SmuggledMethodCallMessage    smuggledMcm    = (SmuggledMethodCallMessage)args[1];
            SmuggledMethodReturnMessage  smuggledMrm    = null;
            byte[]                       retBuff        = null;
 
            try
            { 
                Message.DebugOut("#### : changed to Server Domain :: "+ (Thread.CurrentContext.InternalContextID).ToString("X") ); 

                retBuff = DoDispatch(reqStmBuff, smuggledMcm, out smuggledMrm); 
            }
            catch (Exception e)
            {
                // This will catch exceptions thrown by the infrastructure, 
                // Serialization/Deserialization etc
                // Those thrown by the server are already taken care of 
                // and encoded in the retMsg .. so we don't come here for 
                // that case.
 
                // We are in another appDomain, so we can't simply throw
                // the exception object across. The following marshals it
                // into a serialized return message.
                IMessage retMsg = 
                    new ReturnMessage(e, new ErrorMessage());
                //*********************** SERIALIZE RET-MSG ****************** 
                retBuff = CrossAppDomainSerializer.SerializeMessage(retMsg).GetBuffer(); 
                retMsg = null;
            } 

            args[2] = smuggledMrm;

            return retBuff; 
        }
 
        internal byte[] DoTransitionDispatch( 
            byte[] reqStmBuff,
            SmuggledMethodCallMessage smuggledMcm, 
            out SmuggledMethodReturnMessage smuggledMrm)
        {
            byte[] retBuff = null;
 
            Object[] args = new Object[] { reqStmBuff, smuggledMcm, null };
 
            retBuff = (byte[]) Thread.CurrentThread.InternalCrossContextCallback(null, 
                                                              _xadData.ContextID,
                                                              _xadData.DomainID, 
                                                              s_xctxDel,
                                                              args);

            Message.DebugOut("#### : changed back to Client Domain " + (Thread.CurrentContext.InternalContextID).ToString("X")); 

            smuggledMrm = (SmuggledMethodReturnMessage) args[2]; 
 
            // System.Diagnostics.Debugger.Break();
            return retBuff; 
        } // DoTransitionDispatch

        public virtual IMessage SyncProcessMessage(IMessage reqMsg)
        { 
            Message.DebugOut("\n::::::::::::::::::::::::: CrossAppDomain Channel: [....] call starting");
            IMessage errMsg = InternalSink.ValidateMessage(reqMsg); 
            if (errMsg != null) 
            {
                return errMsg; 
            }


            // currentPrincipal is used to save the current principal. It should be 
            //   restored on the reply message.
            IPrincipal currentPrincipal = null; 
 

            IMessage desRetMsg = null; 

            try
            {
                IMethodCallMessage mcmReqMsg = reqMsg as IMethodCallMessage; 
                if (mcmReqMsg != null)
                { 
                    LogicalCallContext lcc = mcmReqMsg.LogicalCallContext; 
                    if (lcc != null)
                    { 
                        // Special case Principal since if might not be serializable
                        currentPrincipal = lcc.RemovePrincipalIfNotSerializable();
                    }
                } 

                MemoryStream reqStm = null; 
                SmuggledMethodCallMessage smuggledMcm = SmuggledMethodCallMessage.SmuggleIfPossible(reqMsg); 

                if (smuggledMcm == null) 
                {

                    //*********************** SERIALIZE REQ-MSG ****************
                    // Deserialization of objects requires permissions that users 
                    // of remoting are not guaranteed to possess. Since remoting
                    // can guarantee that it's users can't abuse deserialization 
                    // (since it won't allow them to pass in raw blobs of 
                    // serialized data), it should assert the permissions
                    // necessary before calling the deserialization code. This 
                    // will terminate the security stackwalk caused when
                    // serialization checks for the correct permissions at the
                    // remoting stack frame so the check won't continue on to
                    // the user and fail. [from [....]] 
                    // We will hold off from doing this for x-process channels
                    // until the big picture of distributed security is finalized. 
 
                    reqStm = CrossAppDomainSerializer.SerializeMessage(reqMsg);
                } 

                // Retrieve calling caller context here, where it is safe from the view
                // of app domain checking code
                LogicalCallContext oldCallCtx = CallContext.SetLogicalCallContext(null); 

                // Call helper method here, to avoid confusion with stack frames & app domains 
                MemoryStream retStm = null; 
                byte[] responseBytes = null;
                SmuggledMethodReturnMessage smuggledMrm; 

                try
                {
                    if (smuggledMcm != null) 
                        responseBytes = DoTransitionDispatch(null, smuggledMcm, out smuggledMrm);
                    else 
                        responseBytes = DoTransitionDispatch(reqStm.GetBuffer(), null, out smuggledMrm); 
                }
                finally 
                {
                    CallContext.SetLogicalCallContext(oldCallCtx);
                }
 
                if (smuggledMrm != null)
                { 
                    ArrayList deserializedArgs = smuggledMrm.FixupForNewAppDomain(); 
                    desRetMsg = new MethodResponse((IMethodCallMessage)reqMsg,
                                                   smuggledMrm, 
                                                   deserializedArgs);
                }
                else
                { 
                    if (responseBytes != null) {
                        retStm = new MemoryStream(responseBytes); 
 
                        Message.DebugOut("::::::::::::::::::::::::::: CrossAppDomain Channel: [....] call returning!!\n");
                        //*********************** DESERIALIZE RET-MSG ************** 
                        desRetMsg = CrossAppDomainSerializer.DeserializeMessage(retStm, reqMsg as IMethodCallMessage);
                    }
                }
            } 
            catch(Exception e)
            { 
                Message.DebugOut("Arrgh.. XAppDomainSink::throwing exception " + e + "\n"); 
                try
                { 
                    desRetMsg = new ReturnMessage(e, (reqMsg as IMethodCallMessage));
                }
                catch(Exception )
                { 
                    // Fatal Error .. can't do much here
                } 
            } 

            // restore the principal if necessary. 
            if (currentPrincipal != null)
            {
                IMethodReturnMessage mrmRetMsg = desRetMsg as IMethodReturnMessage;
                if (mrmRetMsg != null) 
                {
                    LogicalCallContext lcc = mrmRetMsg.LogicalCallContext; 
                    lcc.Principal = currentPrincipal; 
                }
            } 

            return desRetMsg;
        }
 
        public virtual IMessageCtrl AsyncProcessMessage(IMessage reqMsg, IMessageSink replySink)
        { 
            // This is the case where we take care of returning the calling 
            // thread asap by using the ThreadPool for completing the call.
 
            // we use a more elaborate WorkItem and delegate the work to the thread pool
            ADAsyncWorkItem workItem = new ADAsyncWorkItem(reqMsg,
                                        (IMessageSink)this, /* nextSink */
                                        replySink); 

            WaitCallback threadFunc = new WaitCallback(workItem.FinishAsyncWork); 
            ThreadPool.QueueUserWorkItem(threadFunc); 

            return null; 
        }

        public IMessageSink NextSink
        { 
            get
            { 
                // We are a terminating sink for this chain 
                return null;
            } 
        }

    }
 
    /* package */
    internal class ADAsyncWorkItem 
    { 
        // the replySink passed in to us in AsyncProcessMsg
        private IMessageSink _replySink; 

        // the nextSink we have to call
        private IMessageSink _nextSink;
 
        private LogicalCallContext _callCtx;
 
        // the request msg passed in 
        private IMessage _reqMsg;
 
        internal ADAsyncWorkItem(IMessage reqMsg, IMessageSink nextSink, IMessageSink replySink)
        {
            _reqMsg = reqMsg;
            _nextSink = nextSink; 
            _replySink = replySink;
            _callCtx = CallContext.GetLogicalCallContext(); 
        } 

        /* package */ 
        internal virtual void FinishAsyncWork(Object stateIgnored)
        {
            // install the call context that the calling thread actually had onto
            // the threadPool thread. 
            LogicalCallContext threadPoolCallCtx = CallContext.SetLogicalCallContext(_callCtx);
 
            IMessage retMsg = _nextSink.SyncProcessMessage(_reqMsg); 

            // send the reply back to the replySink we were provided with 
            // note: replySink may be null for one-way calls.
            if (_replySink != null)
            {
                _replySink.SyncProcessMessage(retMsg); 
            }
            CallContext.SetLogicalCallContext(threadPoolCallCtx); 
        } 
    }
 

    internal static class CrossAppDomainSerializer
    {
        internal static MemoryStream SerializeMessage(IMessage msg) 
        {
            MemoryStream stm = new MemoryStream(); 
            RemotingSurrogateSelector ss = new RemotingSurrogateSelector(); 
            BinaryFormatter fmt = new BinaryFormatter();
            fmt.SurrogateSelector = ss; 
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
            fmt.Serialize(stm, msg, null, false /* No Security check */);

            // Reset the stream so that Deserialize happens correctly 
            stm.Position = 0;
 
            return stm; 
        }
 
#if false
        // called from MessageSmuggler classes
        internal static MemoryStream SerializeMessageParts(ArrayList argsToSerialize, out Object[] smuggledArgs)
        { 
            MemoryStream stm = new MemoryStream();
 
            BinaryFormatter fmt = new BinaryFormatter(); 
            RemotingSurrogateSelector ss = new RemotingSurrogateSelector();
            fmt.SurrogateSelector = ss; 
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
            fmt.Serialize(stm, argsToSerialize, null, false ); // No Security check

            smuggledArgs = fmt.CrossAppDomainArray; 
            stm.Position = 0;
            return stm; 
        } // SerializeMessageParts 
#endif
 
        internal static MemoryStream SerializeMessageParts(ArrayList argsToSerialize)
        {
            MemoryStream stm = new MemoryStream();
 
            BinaryFormatter fmt = new BinaryFormatter();
            RemotingSurrogateSelector ss = new RemotingSurrogateSelector(); 
            fmt.SurrogateSelector = ss; 
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
            fmt.Serialize(stm, argsToSerialize, null, false /* No Security check */); 

            stm.Position = 0;
            return stm;
        } // SerializeMessageParts 

        // called from MessageSmuggler classes 
        internal static void SerializeObject(Object obj, MemoryStream stm) 
        {
            BinaryFormatter fmt = new BinaryFormatter(); 
            RemotingSurrogateSelector ss = new RemotingSurrogateSelector();
            fmt.SurrogateSelector = ss;
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
            fmt.Serialize(stm, obj, null, false /* No Security check */); 
        } // SerializeMessageParts
 
        // called from MessageSmuggler classes 
        internal static MemoryStream SerializeObject(Object obj)
        { 
            MemoryStream stm = new MemoryStream();

            SerializeObject( obj, stm );
 
            stm.Position = 0;
            return stm; 
        } // SerializeMessageParts 

 
        internal static IMessage DeserializeMessage(MemoryStream stm)
        {
            return DeserializeMessage(stm, null);
        } 

        internal static IMessage DeserializeMessage( 
            MemoryStream stm, IMethodCallMessage reqMsg) 
        {
            if (stm == null) 
                throw new ArgumentNullException("stm");

            stm.Position = 0;
            BinaryFormatter fmt = new BinaryFormatter(); 
            fmt.SurrogateSelector = null;
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain); 
 
            return (IMessage) fmt.Deserialize(stm, null, false /* No Security check */, true/*isCrossAppDomain*/, reqMsg);
        } 

#if false
        // called from MessageSmuggler classes
        internal static ArrayList DeserializeMessageParts(MemoryStream stm, Object[] args) 
        {
            stm.Position = 0; 
 
            BinaryFormatter fmt = new BinaryFormatter();
            fmt.CrossAppDomainArray = args; 
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
            return (ArrayList) fmt.Deserialize(stm, null, false/*checkSEcurity*/, true/*isCrossAppDomain*/, null);
        } // DeserializeMessageParts
#endif 

        internal static ArrayList DeserializeMessageParts(MemoryStream stm) 
        { 
            return (ArrayList) DeserializeObject(stm);
 
        } // DeserializeMessageParts


        internal static Object DeserializeObject(MemoryStream stm) 
        {
            stm.Position = 0; 
 
            BinaryFormatter fmt = new BinaryFormatter();
            fmt.Context = new StreamingContext(StreamingContextStates.CrossAppDomain); 
            return fmt.Deserialize(stm, null, false /* No Security check */, true/*isCrossAppDomain*/, null);
        } // DeserializeMessageParts
    }
 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK