File: system\runtime\remoting\channelservices.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
using System.Diagnostics.Contracts;
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
//* File:    Channel.cs
//*
//* <EMAIL>Author:  Tarun Anand (Microsoft)</EMAIL>
//*
//* Purpose: Defines the general purpose remoting proxy
//*
//* Date:    May 27, 1999
//*
namespace System.Runtime.Remoting.Channels {
    using System;
    using System.Collections;
    using System.IO;
    using System.Reflection;  
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;   
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Activation;
    using System.Runtime.Remoting.Messaging;
    using System.Runtime.Remoting.Metadata; 
    using System.Runtime.Remoting.Proxies;
    using System.Runtime.Versioning;
    using System.Threading;
    using System.Security;
    using System.Security.Permissions;
    using System.Globalization;
 
    // ChannelServices
    
    [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
    internal struct Perf_Contexts {
        internal volatile int cRemoteCalls;
        internal volatile int cChannels;
    };
    
[System.Runtime.InteropServices.ComVisible(true)]
    public sealed class ChannelServices
    {
        // This gets refreshed when a channel is registered/unregistered.
        private static volatile Object[] s_currentChannelData = null;
 
        [System.Security.SecuritySafeCritical] // static constructors should be safe to call
        static ChannelServices()
        { 
        }
 
        internal static Object[] CurrentChannelData
        {
            [System.Security.SecurityCritical]  // auto-generated
            get 
            {
                if (s_currentChannelData == null)
                    RefreshChannelData();
 
                return s_currentChannelData; 
            }
        } // CurrentChannelData
 
 
        // hide the default constructor
        private ChannelServices()
        {
        }
 
        // list of registered channels and a lock to take when adding or removing channels
        // Note that the channel list is read outside of the lock, which is why it's marked volatile.
        private static Object s_channelLock = new Object();
        private static volatile RegisteredChannelList s_registeredChannels = new RegisteredChannelList();
        
    
        // Private member variables        
        // These have all been converted to getters and setters to get the effect of
        // per-AppDomain statics (note: statics are per-AppDomain now, so these members
        // could just be declared as statics on ChannelServices).
 
        private static long remoteCalls
        { 
            get { return Thread.GetDomain().RemotingData.ChannelServicesData.remoteCalls; }
            set { Thread.GetDomain().RemotingData.ChannelServicesData.remoteCalls = value; }
        }
        
        private static volatile IMessageSink xCtxChannel;
        
 
        [System.Security.SecurityCritical]  // auto-generated
        [MethodImplAttribute(MethodImplOptions.InternalCall)]      
        [ResourceExposure(ResourceScope.None)]
        static unsafe extern Perf_Contexts* GetPrivateContextsPerfCounters();
    
        [SecurityCritical]
        unsafe private static volatile Perf_Contexts *perf_Contexts = GetPrivateContextsPerfCounters(); 
    
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
        public static void RegisterChannel(IChannel chnl, bool ensureSecurity)
        {
            RegisterChannelInternal(chnl, ensureSecurity);
        }
        
        [System.Security.SecuritySafeCritical]  // auto-generated
        [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
        [Obsolete("Use System.Runtime.Remoting.ChannelServices.RegisterChannel(IChannel chnl, bool ensureSecurity) instead.", false)]
        public static void RegisterChannel(IChannel chnl)
        {
            RegisterChannelInternal(chnl, false/*ensureSecurity*/);
        }
 
        
        static bool unloadHandlerRegistered = false;
        [System.Security.SecurityCritical]  // auto-generated
        unsafe internal static void RegisterChannelInternal(IChannel chnl, bool ensureSecurity)
        {
            // Validate arguments
            if(null == chnl)
            {
                throw new ArgumentNullException("chnl");
            }
            Contract.EndContractBlock();
        
            bool fLocked = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                Monitor.Enter(s_channelLock, ref fLocked);
                String chnlName = chnl.ChannelName;
 
                RegisteredChannelList regChnlList = s_registeredChannels;
        
                // Check to make sure that the channel has not been registered
                if((chnlName == null) ||
                   (chnlName.Length == 0) ||
                   (-1 == regChnlList.FindChannelIndex(chnl.ChannelName)))
                {
                    if (ensureSecurity)
                    {
                        ISecurableChannel securableChannel = chnl as ISecurableChannel;
                        if (securableChannel != null)
                            securableChannel.IsSecured = ensureSecurity;
                        else
                            throw new RemotingException(Environment.GetResourceString("Remoting_Channel_CannotBeSecured", chnl.ChannelName??chnl.ToString()));
                            
                    }
                    RegisteredChannel[] oldList = regChnlList.RegisteredChannels;
                    RegisteredChannel[] newList = null;
                    if (oldList == null)
                    {                                            
                        newList = new RegisteredChannel[1];
                    }
                    else
                        newList = new RegisteredChannel[oldList.Length + 1];
 
                    if (!unloadHandlerRegistered && !(chnl is CrossAppDomainChannel))
                    {
                        // Register a unload handler only once and if the channel being registered
                        // is not the x-domain channel. x-domain channel does nothing inside its 
                        // StopListening implementation
                        AppDomain.CurrentDomain.DomainUnload += new EventHandler(UnloadHandler);
                        unloadHandlerRegistered = true;
                    }
 
                    // Add the interface to the array in priority order
                    int priority = chnl.ChannelPriority;
                    int current = 0;
    
                    // Find the place in the array to insert
                    while (current < oldList.Length)
                    {
                        RegisteredChannel oldChannel = oldList[current];
                        if (priority > oldChannel.Channel.ChannelPriority)
                        {
                            newList[current] = new RegisteredChannel(chnl);
                            break;
                        }
                        else
                        {
                            newList[current] = oldChannel;
                            current++;
                        }
                    }
 
                    if (current == oldList.Length)
                    {
                        // chnl has lower priority than all old channels, so we insert
                        //   it at the end of the list.
                        newList[oldList.Length] = new RegisteredChannel(chnl);
                    }
                    else
                    {
                        // finish copying rest of the old channels
                        while (current < oldList.Length)
                        {
                            newList[current + 1] = oldList[current];
                            current++;
                        }
                    }
 
                    if (perf_Contexts != null) {
                        perf_Contexts->cChannels++;
                    }
 
                    s_registeredChannels = new RegisteredChannelList(newList);
                }
                else
                {
                    throw new RemotingException(Environment.GetResourceString("Remoting_ChannelNameAlreadyRegistered", chnl.ChannelName));
                }
 
                RefreshChannelData();
            } // lock (s_channelLock)
            finally
            {
                if (fLocked)
                {
                    Monitor.Exit(s_channelLock);
                }
            }
        } // RegisterChannelInternal
    
    
        [System.Security.SecuritySafeCritical]  // auto-generated
        [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
        unsafe public static void UnregisterChannel(IChannel chnl)
        {
            // we allow null to be passed in, so we can use this api to trigger the
            //   refresh of the channel data <
 
            
            bool fLocked = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                Monitor.Enter(s_channelLock, ref fLocked);
                if (chnl != null)
                {
                    RegisteredChannelList regChnlList = s_registeredChannels;
                
                    // Check to make sure that the channel has been registered
                    int matchingIdx = regChnlList.FindChannelIndex(chnl);
                    if(-1 == matchingIdx)
                    {
                        throw new RemotingException(Environment.GetResourceString("Remoting_ChannelNotRegistered", chnl.ChannelName));
                    }
 
                    RegisteredChannel[] oldList = regChnlList.RegisteredChannels;
                    RegisteredChannel[] newList = null;
                    Contract.Assert((oldList != null) && (oldList.Length != 0), "channel list should not be empty");
 
                    newList = new RegisteredChannel[oldList.Length - 1];
 
                    // Call stop listening on the channel if it is a receiver.
                    IChannelReceiver srvChannel = chnl as IChannelReceiver;
                    if (srvChannel != null)
                        srvChannel.StopListening(null);
 
                    int current = 0;
                    int oldPos = 0;
                    while (oldPos < oldList.Length)
                    {
                        if (oldPos == matchingIdx)
                        {
                            oldPos++;
                        }
                        else
                        {
                            newList[current] = oldList[oldPos];
                            current++;
                            oldPos++;
                        }
                    }
        
                    if (perf_Contexts != null) {
                        perf_Contexts->cChannels--;
                    }
 
                    s_registeredChannels = new RegisteredChannelList(newList);
                } 
 
                RefreshChannelData();
            } // lock (s_channelLock)
            finally
            {
                if (fLocked)
                {
                    Monitor.Exit(s_channelLock);
                }
            }
        } // UnregisterChannel
 
    
        public static IChannel[] RegisteredChannels
        {       
            [System.Security.SecurityCritical]  // auto-generated_required
            get 
            {
                RegisteredChannelList regChnlList = s_registeredChannels;
                int count = regChnlList.Count;
            
                if (0 == count)
                {
                    return new IChannel[0];
                }
                else 
                {
                    // we hide the CrossAppDomainChannel, so the number of visible
                    //   channels is one less than the number of registered channels.
                    int visibleChannels = count - 1;
 
                    // Copy the array of visible channels into a new array
                    // and return
                    int co = 0;
                    IChannel[] temp = new IChannel[visibleChannels];
                    for (int i = 0; i < count; i++)
                    {
                        IChannel channel = regChnlList.GetChannel(i);
                        // add the channel to the array if it is not the CrossAppDomainChannel
                        if (!(channel is CrossAppDomainChannel))
                            temp[co++] = channel;
                    }
                    return temp;
                }
            }
        } // RegisteredChannels
        
        [System.Security.SecurityCritical]  // auto-generated
        internal static IMessageSink CreateMessageSink(String url, Object data, out String objectURI) 
        {
            BCLDebug.Trace("REMOTE", "ChannelServices::CreateMessageSink for url " + url + "\n");
            IMessageSink msgSink = null;
            objectURI = null;
 
            RegisteredChannelList regChnlList = s_registeredChannels;
            int count = regChnlList.Count;
            
            for(int i = 0; i < count; i++)
            {
                if(regChnlList.IsSender(i))
                {
                    IChannelSender chnl = (IChannelSender)regChnlList.GetChannel(i);
                    msgSink = chnl.CreateMessageSink(url, data, out objectURI);
                    
                    if(msgSink != null)
                        break;
                }
            }
            
            // If the object uri has not been set, set it to the url as 
            // default value
            if(null == objectURI)
            {
                objectURI = url;
            }
            
            return msgSink;
        } // CreateMessageSink
    
        [System.Security.SecurityCritical]  // auto-generated
        internal static IMessageSink CreateMessageSink(Object data)
        {
            String objectUri;
            return CreateMessageSink(null, data, out objectUri);
        } // CreateMessageSink
    
    
        [System.Security.SecurityCritical]  // auto-generated_required
        public static IChannel GetChannel(String name)
        {
            RegisteredChannelList regChnlList = s_registeredChannels;
        
            int matchingIdx = regChnlList.FindChannelIndex(name);
            if(0 <= matchingIdx)
            {
                IChannel channel = regChnlList.GetChannel(matchingIdx);
                if ((channel is CrossAppDomainChannel) || (channel is CrossContextChannel))
                    return null;
                else
                    return channel;
            }
            else
            {
                return null;
            }
        } // GetChannel
        
        
        [System.Security.SecurityCritical]  // auto-generated_required
        public static String[] GetUrlsForObject(MarshalByRefObject obj)
        {        
            if(null == obj)
            {
                return null;
            }
 
            RegisteredChannelList regChnlList = s_registeredChannels;
            int count = regChnlList.Count;
            
            Hashtable table = new Hashtable();
            bool fServer;
            Identity id = MarshalByRefObject.GetIdentity(obj, out fServer);
 
            if(null != id) 
            {
                String uri = id.ObjURI;
 
                if (null != uri)
                {
                    for(int i = 0; i < count; i++)
                    {
                        if(regChnlList.IsReceiver(i))
                        {
                            try
                            {
                                String[] urls = ((IChannelReceiver)regChnlList.GetChannel(i)).GetUrlsForUri(uri);
                                // Add the strings to the table
                                for(int j = 0; j < urls.Length; j++)
                                {
                                    table.Add(urls[j], urls[j]);
                                }
                            }
                            catch(NotSupportedException )
                            {
                                // We do not count the channels that do not 
                                // support this method
                            }
                        }
                    }
                }
            }            
 
            // copy url's into string array
            ICollection keys = table.Keys;
            String[] urlList = new String[keys.Count];
            int co = 0;
            foreach (String key in keys)
            {
                urlList[co++] = key;
            }
            return urlList;
        }
 
       // Find the channel message sink associated with a given proxy
        // <
        [System.Security.SecurityCritical]  // auto-generated
        internal static IMessageSink GetChannelSinkForProxy(Object obj)
        {
            IMessageSink sink = null;
            if (RemotingServices.IsTransparentProxy(obj))
            {
                RealProxy rp = RemotingServices.GetRealProxy(obj);
                RemotingProxy remProxy = rp as RemotingProxy;
                if (null != remProxy)
                {
                    Identity idObj = remProxy.IdentityObject;
                    Contract.Assert(null != idObj,"null != idObj");
                    sink = idObj.ChannelSink;
                }
            }
 
            return sink;
        } // GetChannelSinkForProxy
        
 
        //  Get the message sink dictionary of properties for a given proxy
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
        public static IDictionary GetChannelSinkProperties(Object obj)
        {
            IMessageSink sink = GetChannelSinkForProxy(obj);
            IClientChannelSink chnlSink = sink as IClientChannelSink;
            if (null != chnlSink)
            {
                // collect dictionaries for all channel sinks and return
                //   aggregate dictionary
                ArrayList dictionaries = new ArrayList();
 
                do                
                { 
                    IDictionary dict = chnlSink.Properties;
                    if (dict != null)
                        dictionaries.Add(dict);
                
                    chnlSink = chnlSink.NextChannelSink;
                } while (chnlSink != null);
                
                return new AggregateDictionary(dictionaries);
            }
            else
            {
                IDictionary dict = sink as IDictionary;
                if(null != dict)    
                {
                    return dict;
                }
                else
                {
                    return null;
                }
            }
        } // GetChannelSinkProperties
 
    
        internal static IMessageSink GetCrossContextChannelSink()
        {
            if(null == xCtxChannel)
            {
                xCtxChannel = CrossContextChannel.MessageSink;
            }
    
            return xCtxChannel;
        } // GetCrossContextChannelSink
               
    
#if DEBUG
        // A few methods to count the number of calls made across appdomains,
        // processes and machines
        internal static long GetNumberOfRemoteCalls()
        {
            return remoteCalls;
        } // GetNumberOfRemoteCalls
#endif //DEBUG
    
        [System.Security.SecurityCritical]  // auto-generated
        unsafe internal static void IncrementRemoteCalls(long cCalls)
        {
            remoteCalls += cCalls;
            if (perf_Contexts != null)
              perf_Contexts->cRemoteCalls += (int)cCalls;
        } // IncrementRemoteCalls
        
        [System.Security.SecurityCritical]  // auto-generated
        internal static void IncrementRemoteCalls()
        {
            IncrementRemoteCalls( 1 );
        } // IncrementRemoteCalls
 
 
        [System.Security.SecurityCritical]  // auto-generated
        internal static void RefreshChannelData()
        {
            bool fLocked = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                Monitor.Enter(s_channelLock, ref fLocked);
                s_currentChannelData = CollectChannelDataFromChannels();
            }
            finally
            {
                if (fLocked)
                {
                    Monitor.Exit(s_channelLock);
                }
            }
        } // RefreshChannelData
 
        [System.Security.SecurityCritical]  // auto-generated
        private static Object[] CollectChannelDataFromChannels()
        {
            // Ensure that our native cross-context & cross-domain channels
            // are registered
            RemotingServices.RegisterWellKnownChannels();
 
            RegisteredChannelList regChnlList = s_registeredChannels;
            int count = regChnlList.Count;            
 
            // Compute the number of channels that implement IChannelReceiver
            int numChnls = regChnlList.ReceiverCount;
 
            // Allocate array for channel data
            Object[] data = new Object[numChnls];
 
            // we need to remove null entries
            int nonNullDataCount = 0;                        
 
            // Set the channel data, names and mime types
            for (int i = 0, j = 0; i < count; i++)
            {
 
                IChannel chnl = regChnlList.GetChannel(i);
 
                if (null == chnl)
                {
                    throw new RemotingException(Environment.GetResourceString("Remoting_ChannelNotRegistered", ""));
                }
 
                if (regChnlList.IsReceiver(i))
                {
                    BCLDebug.Trace("REMOTE", "Setting info for receiver " + j.ToString(CultureInfo.InvariantCulture) + "\n");
                    // Extract the data
                    Object channelData = ((IChannelReceiver)chnl).ChannelData;                    
                    data[j] = channelData;
                    if (channelData != null)
                        nonNullDataCount++;
 
                    // Increment the counter
                    j++;
                }
            }
 
            if (nonNullDataCount != numChnls)
            {
                // there were null entries, so remove them.
                Object[] nonNullData = new Object[nonNullDataCount];
                int nonNullCounter = 0;
                for (int co = 0; co < numChnls; co++)
                {
                    Object channelData = data[co];
                    if (channelData != null)
                        nonNullData[nonNullCounter++] = channelData;
                }
 
                data = nonNullData;
            }
            
            return data;
        } // CollectChannelDataFromChannels
 
        // Checks to make sure the remote method being invoked is callable
        static bool IsMethodReallyPublic(MethodInfo mi)
        {
            if (!mi.IsPublic || mi.IsStatic)
                return false;
     
            if (!mi.IsGenericMethod)
                return true;
     
            foreach (Type t in mi.GetGenericArguments())
                if (!t.IsVisible)
                    return false;
     
            return true;
        }
 
        //--------------------------------------------------------------------
        //-----------------------  Dispatch Support   ------------------------
        //--------------------------------------------------------------------
 
        [System.Security.SecurityCritical]  // auto-generated_required
        public static ServerProcessing DispatchMessage(
            IServerChannelSinkStack sinkStack,
            IMessage msg, 
            out IMessage replyMsg)
        {
            ServerProcessing processing = ServerProcessing.Complete;
            replyMsg = null;
            
            try
            {            
                if(null == msg)
                {
                    throw new ArgumentNullException("msg");
                }
 
                BCLDebug.Trace("REMOTE", "Dispatching for URI " + InternalSink.GetURI(msg));
 
                // we must switch to the target context of the object and call the context chains etc...
                // Currenly XContextChannel does exactly so. So this method is just a wrapper..
    
                // <
 
                
                // Make sure that incoming calls are counted as a remote call. This way it 
                // makes more sense on a server.
                IncrementRemoteCalls();
        
                // Check if the object has been disconnected or if it is 
                // a well known object then we have to create it lazily.
                ServerIdentity srvId = CheckDisconnectedOrCreateWellKnownObject(msg);
 
                // Make sure that this isn't an AppDomain object since we don't allow
                //   calls to the AppDomain from out of process (and x-process calls
                //   are always dispatched through this method)
                if (srvId.ServerType == typeof(System.AppDomain))
                {
                    throw new RemotingException(
                        Environment.GetResourceString(
                            "Remoting_AppDomainsCantBeCalledRemotely"));
                }
                
 
                IMethodCallMessage mcm = msg as IMethodCallMessage;
 
                if (mcm == null)
                {
                    // It's a plain IMessage, so just check to make sure that the
                    //   target object implements IMessageSink and dispatch synchronously.
 
                    if (!typeof(IMessageSink).IsAssignableFrom(srvId.ServerType))
                    {
                        throw new RemotingException(
                            Environment.GetResourceString(
                                "Remoting_AppDomainsCantBeCalledRemotely"));
                    }
 
                    processing = ServerProcessing.Complete;
                    replyMsg = ChannelServices.GetCrossContextChannelSink().SyncProcessMessage(msg);
                }
                else
                {
                    // It's an IMethodCallMessage.
                
                    // Check if the method is one way. Dispatch one way calls in 
                    // an asynchronous manner
                    MethodInfo method = (MethodInfo)mcm.MethodBase;                                  
    
                    // X-process / X-machine calls should be to non-static
                    // public methods only! Non-public or static methods can't
                    // be called remotely.
                    if (!IsMethodReallyPublic(method) && 
                          !RemotingServices.IsMethodAllowedRemotely(method))
                    {
                        throw new RemotingException(
                            Environment.GetResourceString(
                                "Remoting_NonPublicOrStaticCantBeCalledRemotely"));
                    }
 
                    RemotingMethodCachedData cache = (RemotingMethodCachedData)
                        InternalRemotingServices.GetReflectionCachedData(method);
                        
                    /*
                        
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
*/                  
                    if(RemotingServices.IsOneWay(method))                    
                    {
                        processing = ServerProcessing.OneWay;
                        ChannelServices.GetCrossContextChannelSink().AsyncProcessMessage(msg, null);
                    }
                    else
                    {                    
                        // regular processing
                        processing = ServerProcessing.Complete;
                        if (!srvId.ServerType.IsContextful)
                        {
                            Object[] args = new Object[]{msg, srvId.ServerContext};
                            replyMsg = (IMessage) CrossContextChannel.SyncProcessMessageCallback(args);                            
                        }
                        else 
                            replyMsg = ChannelServices.GetCrossContextChannelSink().SyncProcessMessage(msg);
                    }
                } // end of case for IMethodCallMessage
            }
            catch(Exception e)
            {
                if(processing != ServerProcessing.OneWay)
                {
                    try
                    {                    
                        IMethodCallMessage mcm = 
                            (IMethodCallMessage) ((msg!=null)?msg:new ErrorMessage());
                        replyMsg = (IMessage)new ReturnMessage(e, mcm);
                        if (msg != null)
                        {
                            ((ReturnMessage)replyMsg).SetLogicalCallContext(
                                    (LogicalCallContext)
                                        msg.Properties[Message.CallContextKey]);
                        }
                    }
                    catch(Exception )
                    {
                        // Fatal exception .. ignore
                    }
                }
            }               
 
            return processing;
        } // DispatchMessage
        
       // This method is used by the channel to dispatch the incoming messages
       // to the server side chain(s) based on the URI embedded in the message.
       // The URI uniquely identifies the receiving object.
       // 
        [System.Security.SecurityCritical]  // auto-generated_required
        public static IMessage SyncDispatchMessage(IMessage msg)
        {            
            IMessage msgRet = null;
            bool fIsOneWay = false;
            
            try
            {            
                if(null == msg)
                {
                    throw new ArgumentNullException("msg");
                }
 
 
 
                // For ContextBoundObject's,
                // we must switch to the target context of the object and call the context chains etc...
                // Currenly XContextChannel does exactly so. So this method is just a wrapper..
    
                
                // Make sure that incoming calls are counted as a remote call. This way it 
                // makes more sense on a server.
                IncrementRemoteCalls();
 
                // <
                if (!(msg is TransitionCall))
                {
                    // Check if the object has been disconnected or if it is 
                    // a well known object then we have to create it lazily.
                    CheckDisconnectedOrCreateWellKnownObject(msg);
 
                    MethodBase method = ((IMethodMessage)msg).MethodBase;
 
                    // Check if the method is one way. Dispatch one way calls in 
                    // an asynchronous manner                    
                    fIsOneWay = RemotingServices.IsOneWay(method);
                }
 
                // <
                IMessageSink nextSink = ChannelServices.GetCrossContextChannelSink();
                
                if(!fIsOneWay)
                {                    
                    msgRet = nextSink.SyncProcessMessage(msg);  
                }
                else
                {
                    nextSink.AsyncProcessMessage(msg, null);
                }
            }
            catch(Exception e)
            {
                if(!fIsOneWay)
                {
                    try
                    {                    
                        IMethodCallMessage mcm = 
                            (IMethodCallMessage) ((msg!=null)?msg:new ErrorMessage());
                        msgRet = (IMessage)new ReturnMessage(e, mcm);
                        if (msg!=null)
                        {
                            ((ReturnMessage)msgRet).SetLogicalCallContext(
                                mcm.LogicalCallContext);
                        }
                    }
                    catch(Exception )
                    {
                        // Fatal exception .. ignore
                    }
                }
            }               
 
            return msgRet;
        }
 
       // This method is used by the channel to dispatch the incoming messages
       // to the server side chain(s) based on the URI embedded in the message.
       // The URI uniquely identifies the receiving object.
       // 
        [System.Security.SecurityCritical]  // auto-generated_required
        public static IMessageCtrl AsyncDispatchMessage(IMessage msg, IMessageSink replySink)
        {
            IMessageCtrl ctrl = null;
 
            try
            {
                if(null == msg)
                {
                    throw new ArgumentNullException("msg");
                }
            
                // we must switch to the target context of the object and call the context chains etc...
                // Currenly XContextChannel does exactly so. So this method is just a wrapper..
    
                // Make sure that incoming calls are counted as a remote call. This way it 
                // makes more sense on a server.
                IncrementRemoteCalls();
                
                if (!(msg is TransitionCall))
                {
                    // Check if the object has been disconnected or if it is 
                    // a well known object then we have to create it lazily.
                    CheckDisconnectedOrCreateWellKnownObject(msg);    
                }
    
                // <
 
                ctrl = ChannelServices.GetCrossContextChannelSink().AsyncProcessMessage(msg, replySink);
            }
            catch(Exception e)
            {
                if(null != replySink)
                {
                    try
                    {
                        IMethodCallMessage mcm = (IMethodCallMessage)msg;
                        ReturnMessage retMsg = new ReturnMessage(e, (IMethodCallMessage)msg);
                        if (msg!=null)
                        {
                            retMsg.SetLogicalCallContext(mcm.LogicalCallContext);
                        }
                        replySink.SyncProcessMessage(retMsg);
                    }
                    catch(Exception )
                    {
                        // Fatal exception... ignore
                    }                    
                }
            }
 
            return ctrl;
        } // AsyncDispatchMessage
 
 
        // Creates a channel sink chain (adds special dispatch sink to the end of the chain)
        [System.Security.SecurityCritical]  // auto-generated_required
        public static IServerChannelSink CreateServerChannelSinkChain(
            IServerChannelSinkProvider provider, IChannelReceiver channel)
        {
            if (provider == null)
                return new DispatchChannelSink();       
            
            // add dispatch provider to end (first find last provider)
            IServerChannelSinkProvider lastProvider = provider;
            while (lastProvider.Next != null)
                lastProvider = lastProvider.Next;
            lastProvider.Next = new DispatchChannelSinkProvider();
 
            IServerChannelSink sinkChain = provider.CreateSink(channel);
 
            // remove dispatch provider from end
            lastProvider.Next = null;            
 
            return sinkChain;
        } // CreateServerChannelSinkChain
        
        
 
        // Check if the object has been disconnected or if it is 
        // a well known object then we have to create it lazily.
        [System.Security.SecurityCritical]  // auto-generated
        internal static ServerIdentity CheckDisconnectedOrCreateWellKnownObject(IMessage msg)
        {
            ServerIdentity ident = InternalSink.GetServerIdentity(msg);
            
            BCLDebug.Trace("REMOTE", "Identity found = " + (ident == null ? "null" : "ServerIdentity"));
 
            // If the identity is null, then we should check whether the 
            // request if for a well known object. If yes, then we should 
            // create the well known object lazily and marshal it.
            if ((ident == null) || ident.IsRemoteDisconnected())
            {
                String uri = InternalSink.GetURI(msg);
                BCLDebug.Trace("REMOTE", "URI " + uri);
                if (uri != null)
                {
                    ServerIdentity newIdent = RemotingConfigHandler.CreateWellKnownObject(uri);
                    if (newIdent != null)
                    {
                        // The uri was a registered wellknown object.
                        ident = newIdent;
                        BCLDebug.Trace("REMOTE", "Identity created = " + (ident == null ? "null" : "ServerIdentity"));
                    }
                }  
 
            }
 
 
            if ((ident == null) || (ident.IsRemoteDisconnected()))
            {
                String uri = InternalSink.GetURI(msg);
                throw new RemotingException(Environment.GetResourceString("Remoting_Disconnected",uri));                
            }
            return ident;
        }
        
        // Channel Services AppDomain Unload Event Handler
        [System.Security.SecurityCritical]  // auto-generated
        internal static void UnloadHandler(Object sender, EventArgs e)
        {
            StopListeningOnAllChannels();
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        private static void StopListeningOnAllChannels()
        {
            try
            {
                RegisteredChannelList regChnlList = s_registeredChannels;
                int count = regChnlList.Count;    
            
                for(int i = 0; i < count; i++)
                {
                    if(regChnlList.IsReceiver(i))
                    {
                        IChannelReceiver chnl = (IChannelReceiver)regChnlList.GetChannel(i);
                        chnl.StopListening(null);
                    }
                }
            }
            catch (Exception)
            {
                // Ignore ... appdomain is shutting down..
            }
        }
 
 
 
 
        //
        // INTERNAL PROFILER NOTIFICATION SERVICES
        //
 
        [System.Security.SecurityCritical]  // auto-generated
        internal static void NotifyProfiler(IMessage msg, RemotingProfilerEvent profilerEvent)
        {
            switch (profilerEvent)
            {
            
            case RemotingProfilerEvent.ClientSend:
            {
                if (RemotingServices.CORProfilerTrackRemoting())
                {
                    Guid g;
 
                    RemotingServices.CORProfilerRemotingClientSendingMessage(out g, false);
 
                    if (RemotingServices.CORProfilerTrackRemotingCookie())
                        msg.Properties["CORProfilerCookie"] = g;
                }
                break;
            } // case RemotingProfilerEvent.ClientSend
 
            case RemotingProfilerEvent.ClientReceive:
            {
                if (RemotingServices.CORProfilerTrackRemoting())
                {
                    Guid g = Guid.Empty;
 
                    if (RemotingServices.CORProfilerTrackRemotingCookie())
                    {
                        Object obj = msg.Properties["CORProfilerCookie"];
 
                        if (obj != null)
                        {
                            g = (Guid) obj;
                        }
                    }
 
                    RemotingServices.CORProfilerRemotingClientReceivingReply(g, false);
                }
                break;
            } // case RemotingProfilerEvent.ClientReceive
            
            } // switch (event)
        } // NotifyProfiler        
 
 
 
        // This is a helper used by UrlObjRef's.
        // Finds an http channel and returns first url for this object.
        [System.Security.SecurityCritical]  // auto-generated
        internal static String FindFirstHttpUrlForObject(String objectUri)
        {                    
            if (objectUri == null)
                return null;       
 
            RegisteredChannelList regChnlList = s_registeredChannels;
            int count = regChnlList.Count;    
 
            for (int i = 0; i < count; i++)
            {
                if(regChnlList.IsReceiver(i))
                {       
                    IChannelReceiver chnl = (IChannelReceiver)regChnlList.GetChannel(i);
                    String chnlType = chnl.GetType().FullName;
                    if ((String.CompareOrdinal(chnlType, "System.Runtime.Remoting.Channels.Http.HttpChannel") == 0) ||
                        (String.CompareOrdinal(chnlType, "System.Runtime.Remoting.Channels.Http.HttpServerChannel") == 0))
                    {                                            
                        String[] urls = chnl.GetUrlsForUri(objectUri);
                        if ((urls != null) && (urls.Length > 0))
                            return urls[0];
                    }
                }                               
            }      
 
            return null;
        } // FindFirstHttpUrlForObject
 
 
        //
        // DEBUG Helpers
        //   Note: These methods should be included even in retail builds so that 
        //     they can be called from the debugger.
        //
#if DEBUG
        internal static void DumpRegisteredChannels()
        {
            // To use from cordbg: 
            //   f System.Runtime.Remoting.Channels.ChannelServices::DumpRegisteredChannels
 
            RegisteredChannelList regChnlList = s_registeredChannels;
            int count = regChnlList.Count; 
        
            Console.Error.WriteLine("Registered Channels:");            
        
            for (int i = 0; i < count; i++)
            {
                IChannel chnl = regChnlList.GetChannel(i);
                Console.Error.WriteLine(chnl);
            }
        } // DumpRegisteredChannels
#endif // DEBUG
 
 
    } // class ChannelServices
 
 
    // used by ChannelServices.NotifyProfiler
    [Serializable]
    internal enum RemotingProfilerEvent
    {
        ClientSend,
        ClientReceive
    } // RemotingProfilerEvent
 
    
    
    
    internal class RegisteredChannel
    {
        // private member variables
        private IChannel channel;
        private byte flags;
        private const byte SENDER      = 0x1;
        private const byte RECEIVER    = 0x2;
    
        internal RegisteredChannel(IChannel chnl)
        {
            channel = chnl;
            flags = 0;
            if(chnl is IChannelSender)
            {
                flags |= SENDER;
            }
            if(chnl is IChannelReceiver)
            {
                flags |= RECEIVER;
            }
        }
    
        internal virtual IChannel Channel
        {
            get { return channel; }
        }
    
        internal virtual bool IsSender()
        {
            return ((flags & SENDER) != 0);
        }
    
        internal virtual bool IsReceiver()
        {
            return ((flags & RECEIVER) != 0);
        }
    }// class RegisteredChannel
 
 
 
    // This list should be considered immutable once created.
    //   <
 
 
    internal class RegisteredChannelList
    {
        private RegisteredChannel[] _channels;
 
        internal RegisteredChannelList()
        {
            _channels = new RegisteredChannel[0];
        } // RegisteredChannelList
 
        internal RegisteredChannelList(RegisteredChannel[] channels)
        {
            _channels = channels;
        } // RegisteredChannelList
 
        internal RegisteredChannel[] RegisteredChannels
        {
            get { return _channels; }
        } // RegisteredChannels
 
        internal int Count
        {
            get 
            {
                if (_channels == null)
                    return 0;
 
                return _channels.Length;
            }
        } // Count
 
        internal IChannel GetChannel(int index)
        {                
            return _channels[index].Channel;
        } // GetChannel
 
        internal bool IsSender(int index)
        {
            return _channels[index].IsSender();
        } // IsSender
 
        internal bool IsReceiver(int index)
        {
            return _channels[index].IsReceiver();
        } // IsReceiver        
 
        internal int ReceiverCount
        {
            get 
            {
                if (_channels == null)
                    return 0;
                
                int total = 0;
                for (int i = 0; i < _channels.Length; i++)
                {
                    if (IsReceiver(i))
                        total++;
                }
                
                return total;
            }
        } // ReceiverCount
    
        internal int FindChannelIndex(IChannel channel)
        {
            Object chnlAsObject = (Object)channel;
        
            for (int i = 0; i < _channels.Length; i++)
            {
                if (chnlAsObject == (Object)GetChannel(i))
                    return i;                    
            }
 
            return -1;
        } // FindChannelIndex
 
        [System.Security.SecurityCritical]  // auto-generated
        internal int FindChannelIndex(String name)
        {        
            for (int i = 0; i < _channels.Length; i++)
            {
                if(String.Compare(name, GetChannel(i).ChannelName, StringComparison.OrdinalIgnoreCase) == 0)
                    return i;                
            }
 
            return -1;
        } // FindChannelIndex
        
        
    } // class RegisteredChannelList
    
 
 
 
    internal class ChannelServicesData
    {        
        internal long remoteCalls = 0;
        internal CrossContextChannel xctxmessageSink = null;
        internal CrossAppDomainChannel xadmessageSink = null;
        internal bool fRegisterWellKnownChannels = false;
    }
 
   //
   // Terminator sink used for profiling so that we can intercept asynchronous
   // replies on the server side.
   //  
    
    /* package scope */
    internal class ServerAsyncReplyTerminatorSink : IMessageSink
    {
        internal IMessageSink _nextSink;
 
        internal ServerAsyncReplyTerminatorSink(IMessageSink nextSink)
        {
            Contract.Assert(nextSink != null,
                            "null IMessageSink passed to ServerAsyncReplyTerminatorSink ctor.");
            _nextSink = nextSink;
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        public virtual IMessage SyncProcessMessage(IMessage replyMsg)
        {
            // If this class has been brought into the picture, then the following must be true.
            Contract.Assert(RemotingServices.CORProfilerTrackRemoting(),
                            "CORProfilerTrackRemoting returned false, but we're in AsyncProcessMessage!");
            Contract.Assert(RemotingServices.CORProfilerTrackRemotingAsync(),
                            "CORProfilerTrackRemoting returned false, but we're in AsyncProcessMessage!");
 
            Guid g;
 
            // Notify the profiler that we are receiving an async reply from the server-side
            RemotingServices.CORProfilerRemotingServerSendingReply(out g, true);
 
            // If GUID cookies are active, then we save it for the other end of the channel
            if (RemotingServices.CORProfilerTrackRemotingCookie())
                replyMsg.Properties["CORProfilerCookie"] = g;
 
            // Now that we've done the intercepting, pass the message on to the regular chain
            return _nextSink.SyncProcessMessage(replyMsg);
        }
    
        [System.Security.SecurityCritical]  // auto-generated
        public virtual IMessageCtrl AsyncProcessMessage(IMessage replyMsg, IMessageSink replySink)
        {
            // Since this class is only used for intercepting async replies, this function should
            // never get called. (Async replies are synchronous, ironically)
            Contract.Assert(false, "ServerAsyncReplyTerminatorSink.AsyncProcessMessage called!");
 
            return null;
        }
    
        public IMessageSink NextSink
        {
            [System.Security.SecurityCritical]  // auto-generated
            get
            {
                return _nextSink;
            }
        }
 
        // Do I need a finalize here?
    }
}