using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting; using System.Runtime.Remoting.Messaging; using System.Text.RegularExpressions; using System.Runtime.Remoting.Services; using System.Runtime.Remoting.Proxies; using System.Reflection; namespace RemotingChannelWrapper { public class RemotingCompositeChannel : IChannelSender, ITrackingHandler { UriComparisonMode uriComparisonMode; Dictionary channelCache; static FieldInfo identityFI = typeof(RealProxy).GetField("_identity", BindingFlags.Instance | BindingFlags.NonPublic); static Type identityType = Type.GetType("System.Runtime.Remoting.Identity", false, true); static FieldInfo channelSinkFI = identityType.GetField("_channelSink", BindingFlags.Instance | BindingFlags.NonPublic); public RemotingCompositeChannel() : this(UriComparisonMode.Exact) { } public RemotingCompositeChannel(UriComparisonMode mode) { this.uriComparisonMode = mode; switch (mode) { case UriComparisonMode.Exact: case UriComparisonMode.WildcardMatch: channelCache = new Dictionary(); break; default: throw new ArgumentException("mode"); } channelCache = new Dictionary(); //Register yourself as a TrackingHandler so you can be notified when an remote object is unmarshalled TrackingServices.RegisterTrackingHandler(this); } public Dictionary ChannelCache { get { return channelCache; } } public UriComparisonMode UriComparisonMode { get { return uriComparisonMode; } } #region ITrackingHandler Members public void DisconnectedObject(object obj) { //No-op } public void MarshaledObject(object obj, ObjRef or) { //Noop } public void UnmarshaledObject(object obj, ObjRef or) { //We got an ObjRef from remote sevrer and unmarshalling it. Console.WriteLine("Unmarshalled object " + obj); if (RemotingServices.IsTransparentProxy(obj)) { Console.WriteLine("IS transparent Proxy"); ActivatedClientTypeEntry entry = RemotingConfiguration.IsRemotelyActivatedClientType(obj.GetType()); if (entry != null) { string objectUri; IMessageSink sink = this.GetMessageSink(entry.ApplicationUrl, null, out objectUri); if (sink != null) { //Set it on the TransparentProxy's sink RealProxy rp = RemotingServices.GetRealProxy(obj); object identity = identityFI.GetValue(rp); channelSinkFI.SetValue(identity, sink); } } } } #endregion #region IChannelSinkBase Members public IDictionary Properties { get { return null; } } #endregion #region IChannelSender Members public IMessageSink CreateMessageSink(string url, object remoteChannelData, out string objectURI) { objectURI = null; if (url == null && remoteChannelData != null) { //Return and when the object is unmarshalled, TrackingServices.UnmarshalledObject will be called. Set the envoy sink at that point //This is a ugly workaround as Remoting does not pass the ObjRef's type/activating Url when calling CreateMessageSink return null; } return this.GetMessageSink(url, remoteChannelData, out objectURI); } #endregion #region IChannel Members public string ChannelName { get { return "RemotingCompositeChannel"; } } public int ChannelPriority { get { //Always return highest priority so we get at the top of the list //IPCChannel priority is 20 and CrossAppdomainChannels priority is 64. //Regular tcp, http channels are of priority 1. return 25; } } public string Parse(string url, out string objectURI) { //No one calls this method of this class so ignore this. objectURI = null; return null; } #endregion public void Add(String url, IChannel channel) { if (url == null) { throw new ArgumentException("url"); } if (channel == null) { throw new ArgumentException("channel"); } channelCache.Add(url, channel); } public void Remove(String url) { if (url == null) { throw new ArgumentException("url"); } if (channelCache.ContainsKey(url)) { channelCache.Remove(url); } } IMessageSink GetMessageSink(string urlToMatch, object remoteChannelData, out string objectURI) { objectURI = null; IMessageSink messageSink = null; foreach (String currentUri in this.channelCache.Keys) { IChannelSender sender = this.channelCache[currentUri] as IChannelSender; if (sender != null && this.IsUrlAMatch(currentUri, urlToMatch)) { messageSink = sender.CreateMessageSink(urlToMatch, remoteChannelData, out objectURI); if (messageSink != null) { break; } } } return messageSink; } bool IsUrlAMatch(String uriPattern, String urlToMatch) { if (uriPattern == null || urlToMatch == null) { return false; } if (this.uriComparisonMode == UriComparisonMode.Exact) { return uriPattern.Equals(urlToMatch, StringComparison.OrdinalIgnoreCase); } else { Regex regex = new Regex(uriPattern); return regex.IsMatch(urlToMatch); } } } public enum UriComparisonMode : int { Exact, WildcardMatch, } }