HOWTO: Use A Custom Proxy For Cross-AppDomain Calls
[DISCLAIMER: This article seems to be outdated; I heard that there is a better approach available. I'll update this one as soon as I get the time to do so. In the meantime, I'll leave it online nevertheless ...]
Recently, there have been some questions on how to use a custom proxy for cross AppDomain calls on [DOTNET]. The short answer seems to be: You shouldn't and therefore you can't.
Well, in fact the first one may be right but the second one needs a little bit of elaboration. The reason why you cannot place a call from a custom proxy across an AppDomain is that you don't have access to the CrossAppDomainChannel and the CrossAppDomainSink due to protection levels in RealProxy (they are marked as private).
This is somewhat different for HttpChannel and BinaryChannel which can be accessed by iterating over ChannelServices.RegisteredChannels. You will get access to those channel's sinks by calling CreateMessageSink() on each of them until one returns a non-null object - this will be your IMessageSink.
So ... how to do it nevertheless?
DISCLAIMER: You are now entering uncharted territory. Use at your own risk! If anything blows up, it wasn't my fault. Private fields should normally stay private. I warned you ;-)
Let's say you have a method which creates an object in a secondary AppDomain using something like the following lines of code:
AppDomain domain = AppDomain.CreateDomain( "SecondaryDomain" );
Object obj = m_domain.CreateInstanceAndUnwrap("SomeTest",
"SomeTest.SomeTest" );Let's further assume, that you have created an attribute MyCustomProxyAttribute which extends ProxyAttribute and have marked "SomeTest.SomeTest" with it. You thereby indicate that you want to create a custom proxy for accessing this object in a remote context.
This Attribute's implementation will look like this:
[System.AttributeUsage( System.AttributeTargets.Class )]
public class MyCustomProxyAttribute : ProxyAttribute
{
public override RealProxy CreateProxy(ObjRef objRef,
System.Type serverType, object serverObject,
System.Runtime.Remoting.Contexts.Context context )
{
return new MyCustomProxy(serverType,objRef);
}
}Great! So how will the proxy have to look like and what will its Invoke() method do?
As I already said, there is no direct way (at least non that's know to me) to access the underlying channel sinks so that you will have to rely on some use of reflection here to first get the reference to the RealProxy's _identity field and afterwards to the _channelSink field of the Identity object.
You then can simply call SyncProcessMessage() on this sink to forward the call to the remote AppDomain:
public class MyCustomProxy: RealProxy
{
ObjRef _objRef;
// we need access to the realproxy's identity field
FieldInfo _baseIdentityField;
// and to the sink chain
FieldInfo _sinkchainField;
public MyCustomProxy(Type serverType, ObjRef objRef)
:base( serverType )
{
_objRef = objRef;
// checking for the identity field of the RealProxy
_baseIdentityField = typeof(RealProxy).GetField("_identity",
BindingFlags.Instance | BindingFlags.NonPublic);
}
public override IMessage Invoke(IMessage message )
{
message.Properties["__Uri"] = _objRef.URI;
Object identity = _baseIdentityField.GetValue(this);
if (_sinkchainField == null)
{
// cache the FieldInfo for performance ;-)
_sinkchainField = identity.GetType().GetField("_channelSink",
BindingFlags.Instance | BindingFlags.NonPublic);
}
// fetch the sink chain from the RealProxy
IMessageSink sinkChain = (IMessageSink ) _sinkchainField.GetValue(identity);
// forward the call to the sink chain
IMessage returnMessage = sinkChain.SyncProcessMessage(message);
return returnMessage;
}
}