For background: in ContextBoundObject #1 – Creating Contexts we started reverse engineering SynchronizationAttribute to figure out how to create Contexts for ContextBoundObjects.
Creating a Context was fairly easy and… on it’s own useless. Yes, a context possibly got created, but does that do anything on its own? Not really. At this stage it’s like we created a filter which is all holes and lets everything fall through.
What does it take to actually do something when cross-context calls occur? Looking at the inheritance hierarchy of [SynchronizationAttribute] gives some clues.
We can implement them, set some breakpoints, and see what happens. In order to actually do this, we have to return some IMessageSink implementation which does some minimal amount of work. Here’s a guess at what a default, minimal IMessageSink should do:
public class MinimalMessageSink : IMessageSink
{
public MinimalMessageSink(IMessageSink next)
this.NextSink = next;
}
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
return NextSink.AsyncProcessMessage(msg, replySink);
public IMessageSink NextSink
get;
set;
public IMessage SyncProcessMessage(IMessage msg)
return NextSink.SyncProcessMessage(msg);
And the sample program
[AttributeUsage(AttributeTargets.Class)]
public class BasicContextAttribute : ContextAttribute, IContributeClientContextSink, IContributeServerContextSink
public BasicContextAttribute()
: base("BASICCONTEXTTEST")
public override bool IsContextOK(Context ctx, System.Runtime.Remoting.Activation.IConstructionCallMessage ctorMsg)
Console.WriteLine("IsContextOK: " + ctorMsg.ActivationType.ToString());
return base.IsContextOK(ctx, ctorMsg);
public IMessageSink GetClientContextSink(IMessageSink nextSink)
return new MinimalMessageSink(nextSink);
public IMessageSink GetServerContextSink(IMessageSink nextSink)
[BasicContext]
public class CBObj : ContextBoundObject
public string DoSomething(int c)
return new string('x', c);
public class Program
public static void Main(string[] args)
CBObj c = new CBObj();
string s = c.DoSomething(3);
Here’s the resulting order of operations:
OK, I think that’s pretty interesting for a start. We’ve gained two minor super powers, which are the ability to spy on construction and method invocations on CBObj. We also have a couple little mysteries to dig into
At this point I started mucking around with trying to fake an IConstructionReturnMessage response to the IConstructionCallMessage which comes into our message sink’s ProcessSync. When doing this I noticed two things:
(See - System.InvalidCastException was unhandled Message=Unable to cast object of type 'ConsoleApplication13.FakeCBObj' to type 'System.Runtime.Remoting.ObjRef'. Source=mscorlib StackTrace: at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) at ConsoleApplication13.CBObj..ctor() at ConsoleApplication13.Program.Main(String[] args) in C:\Users\tilovell\Desktop\BlogCode\ContextBoundObjectSamples\ConsoleApplication13\BasicContextAttribute.cs:line 220)
Since RealProxy is abstract, I wonder which particular implementation of RealProxy is getting used by default? … Well, well, well. It looks like it is a RemotingProxy.
Why?
It turns out that there is another interesting part to the ContextBoundObject story, proxy creation, which is controlled by yet another attribute. Oddly enough, that is ProxyAttribute.
‘Apply the current attribute to types that need custom proxies. You can use the ProxyAttribute class to intercept the new (New in Visual Basic) statement by deriving from the ProxyAttribute and placing the attribute on a child of ContextBoundObject. (Placing the proxy attribute on a child of MarshalByRefObject is not supported.)’
[more next time]