Potty Little Details

Just another WordPress.com weblog

Archive for the ‘C#’ Category

Demystifying Delegates

leave a comment »

On the surface, delegates seem easy to use: you define them using C#’s delegate keyword, you construct instances of them using the familiar new operator, and you invoke the callback using familiar “method call” syntax (except instead of a method name, you use the variable that refers to the delegate object).However, what’s really going on is quite a bit more complex . The compilers and the common language runtime (CLR) do a lot of behind-thescenes processing to hide the complexity. In this section, I’ll focus on how the compiler and the CLR work together to implement delegates.

Let’s start by reexamining this line of code:

public delegate void Feedback(

Object value, Int32 item, Int32 numItems);

When it sees this line, the compiler actually defines a complete class definition that lookssomething like this:

public class Feedback : System.MulticastDelegate {

// Constructor

public Feedback(Object target, Int32 methodPtr);

// Method with same prototype as specified by the source code

public void virtual Invoke(

Object value, Int32 item, Int32 numItems);

// Methods allowing the callback to be called asynchronously

public virtual IAsyncResult BeginInvoke(

Object value, Int32 item, Int32 numItems,

AsyncCallback callback, Object object);

public virtual void EndInvoke(IAsyncResult result);

}

The class defined by the compiler has four methods: a constructor, Invoke, BeginInvoke, and EndInvoke.In fact, you can verify that the compiler did indeed generate this class automatically by examining the resulting module with ILDasm.exe.In this example, the compiler has defined a class called Feedback that is derived from the System.MulticastDelegate type defined in the .NET Framework Class Library (FCL).(All delegate types are derived from MulticastDelegate.) The class is public because the delegate is declared as public in the source code. If the source code had indicated private or protected, the Feedback class the compiler generated would also be private or protected, respectively. You should be aware that delegate types can be defined within a class (as in the example, Feedback is defined within the Set class) or at global scope.Basically, because delegates are classes, a delegate can be defined anywhere a class can be defined.Because all delegate types are derived from MulticastDelegate, they inherit MulticastDelegate’s fields, properties, and methods. Of all these members, three private fields are probably most significant.

Field Type Description

_target System.Object Refers to the object that should be operated on when the callback method is called.This field is used for instance method callbacks.

_methodPtr System.Int32 An internal integer that the CLR uses to identify the method that is to be called back.

_prev System.MulticastDelegate Refers to another delegate object. This field is usually null.

Notice that all delegates have a constructor that takes two parameters: a reference to an object and an integer that refers to the callback method.A reference to the object is passed for the target parameter, and a special Int32 value (obtained from a MethodDef or MethodRef metadata token) that identifies the method is passed for the methodPtr parameter. For static methods, null is passed for the target parameter.Inside the constructor, these two parameters are saved in their corresponding private fields.In addition, the constructor sets the _prev field to null. This _prev field is used to create a linked list of MulticastDelegate objects.So, each delegate object is really a wrapper around a method and an object to be operated on when the method is called. The MulticastDelegate class defines two read-only public instance properties: Target and Method. Given a reference to a delegate object, you can query these properties. The Target property returns a reference to the object that will be operated on if the method is called back. If the method is a static method, Target returns null. The Method property returns a System.Reflection.MethodInfo object thatidentifies the callback method.You could use this information in several ways. For example, you could check to see whether a delegate object refers to an instance method of a specific type:

Boolean DelegateRefersToInstanceMethodOfType(

MulticastDelegate d, Type type) {

return((d.Target != null) && d.Target.GetType() == type);

}

You could also write code to check whether the callback method has a specific name (such as FeedbackToMsgBox):

Boolean DelegateRefersToMethodOfName(

MulticastDelegate d, String methodName) {

return(d.Method.Name == methodName);

}

Now that you know how delegate objects are constructed, let’s talk about how the callback method is invoked.

public void ProcessItems(Feedback feedback) {

for (Int32 item = 0; item < items.Length; item++) {

if (feedback != null) {

// If any callbacks are specified, call them.

feedback(items[item], item, items.Length);

}

}

}

Just below the comment is the line of code that invokes the callback method. It might seem as if I’m calling a function named feedback and passing it three parameters. However,there is no function called feedback. Again, because it knows that feedback is a variable that refers to a delegate object, the compiler generates code to call the delegate object’s Invoke method. In other words, the compiler sees this:

feedback(items[item], item, items.Length);

But the compiler generates code as though the source code said this:

feedback.Invoke(items[item], item, items.Length);

You can verify that the compiler produces code to call the delegate type’s In-voke method by using ILDasm.exe to examine the code for the ProcessItems method. More details check out dot net programming by Jeffery Ritcher

Advertisements

Written by oneil

September 13, 2008 at 2:24 am

Posted in C#

Encrypting the Message Transfer -Dotnet Remoting

leave a comment »

Encrypting the Transfer
Even though using an asymmetric/symmetric combination such as HTTPS/SSL for the encryption of the network traffic provides the only real security, in some situations HTTPS isn’t quite helpful.

First, .NET Remoting by default only supports encryption when using an HTTP channel and when hosting the server-side components in IIS. If you want to use a TCP channel or host your objects in a Windows service, there’s no default means of secure communication.

Second, even if you use IIS to host your components, callbacks that are employed with event notification will not be secured. This is because your client (which is the server for the callback object) does not publish its objects using HTTPS, but only HTTP.

Essential Symmetric Encryption
Symmetric encryption is based on one key fact: client and server will have access to the same encryption key. This key is not a password as you might know it, but instead is a binary array in common sizes from 40 to 192 bits. Additionally, you have to choose from among a range of encryption algorithms supplied with the .NET Framework: DES, TripleDES, RC2, or Rijndael.

To generate a random key for a specified algorithm, you can use the following code snippet. You will find the key in the byte[] variable mykey afterwards.

String algorithmName = “TripleDES”;
SymmetricAlgorithm alg = SymmetricAlgorithm.Create(algorithmName);

int keylen = 128;
alg.KeySize = keylen;
alg.GenerateKey();

byte[] mykey = alg.Key;

Because each algorithm has a limited choice of valid key lengths, and because you might want to save this key to a file, you can run the separate KeyGenerator console application, which is shown in below.
A Complete Keyfile Generator

using System;
using System.IO;
using System.Security.Cryptography;
class KeyGen
{
static void Main(string[] args)
{
if (args.Length != 1 && args.Length != 3)
{
Console.WriteLine(“Usage:”);
Console.WriteLine(“KeyGenerator [ ]”);
Console.WriteLine(“Algorithm can be: DES, TripleDES, RC2 or Rijndael”);
Console.WriteLine();
Console.WriteLine(“When only is specified, the program”);
Console.WriteLine(“will print a list of valid key sizes.”);
return;
}

String algorithmname = args[0];

SymmetricAlgorithm alg = SymmetricAlgorithm.Create(algorithmname);

if (alg == null)
{
Console.WriteLine(“Invalid algorithm specified.”);
return;
}

if (args.Length == 1)
{
// just list the possible key sizes
Console.WriteLine(“Legal key sizes for algorithm {0}:”,algorithmname);
foreach (KeySizes size in alg.LegalKeySizes)
{
if (size.SkipSize != 0)
{
for (int i = size.MinSize;i<=size.MaxSize;i=i+size.SkipSize)
{
Console.WriteLine(“{0} bit”, i);
}
}
else
{
if (size.MinSize != size.MaxSize)
{
Console.WriteLine(“{0} bit”, size.MinSize);
Console.WriteLine(“{0} bit”, size.MaxSize);
}
else
{
Console.WriteLine(“{0} bit”, size.MinSize);
}
}
}
return;
}

// user wants to generate a key
int keylen = Convert.ToInt32(args[1]);
String outfile = args[2];
try
{
alg.KeySize = keylen;
alg.GenerateKey();
FileStream fs = new FileStream(outfile,FileMode.CreateNew);
fs.Write(alg.Key,0,alg.Key.Length);
fs.Close();
Console.WriteLine(“{0} bit key written to {1}.”,
alg.Key.Length * 8,
outfile);

}
catch (Exception e)
{
Console.WriteLine(“Exception: {0}” ,e.Message);
return;
}

}
}

When this key generator is invoked with KeyGenerator.exe (without any parameters), it will print a list of possible algorithms. You can then run KeyGenerator.exe to get a list of possible key sizes for the chosen algorithm. To finally generate the key, you have to start KeyGenerator.exe . To generate a 128-bit key for a TripleDES algorithm and save it in c:\testfile.key, run KeyGenerator.exe TripleDES 128 c:\testfile.key.

The Initialization Vector
Another basic of symmetric encryption is the use of a random initialization vector (IV). This is again a byte array, but it’s not statically computed during the application’s development. Instead, a new one is generated for each encryption taking place.

To successfully decrypt the message, both the key and the initialization vector have to be known to the second party. The key is determined during the application’s deployment (at least in the following example) and the IV has to be sent via remoting boundaries with the original message. The IV is therefore not secret on its own.

Creating the Encryption Helper
Next I show you how to build this sink in the same manner as the previous CompressionSink, which means that the sink’s core logic will be extracted to a helper class. I call this class EncryptionHelper. The encryption helper will implement two methods, ProcessOutboundStream() and ProcessInboundStream(). The methods’ signatures look like this:

public static Stream ProcessOutboundStream(
Stream inStream,
String algorithm,
byte[] encryptionkey,
out byte[] encryptionIV)

public static Stream ProcessInboundStream(
Stream inStream,
String algorithm,
byte[] encryptionkey,
byte[] encryptionIV)

As you can see in the signature, both methods take a stream, the name of avalid cryptoalgorithm, and a byte array that contains the encryption key as parameters. The first method is used to encrypt the stream. It also internally generates the IV and returns it as an out parameter. This IV then has to be serialized by the sink and passed to the other party in the remoting call. ProcessInboundStream(), on the other hand, expects the IV to be passed to it, so this value has to be obtained by the sink before calling this method. The implementation of these helper methods can be seen below.

The EncryptionHelper Encapsulates the Details of the Cryptographic Process

using System;
using System.IO;
using System.Security.Cryptography;

namespace EncryptionSink
{

public class EncryptionHelper
{

public static Stream ProcessOutboundStream(
Stream inStream,
String algorithm,
byte[] encryptionkey,
out byte[] encryptionIV)
{
Stream outStream = new System.IO.MemoryStream();

// setup the encryption properties
SymmetricAlgorithm alg = SymmetricAlgorithm.Create(algorithm);
alg.Key = encryptionkey;
alg.GenerateIV();
encryptionIV = alg.IV;

CryptoStream encryptStream = new CryptoStream(
outStream,
alg.CreateEncryptor(),
CryptoStreamMode.Write);

// write the whole contents through the new streams
byte[] buf = new Byte[1000];
int cnt = inStream.Read(buf,0,1000);
while (cnt>0)
{
encryptStream.Write(buf,0,cnt);
cnt = inStream.Read(buf,0,1000);
}
encryptStream.FlushFinalBlock();
outStream.Seek(0,SeekOrigin.Begin);
return outStream;
}
public static Stream ProcessInboundStream(
Stream inStream,
String algorithm,
byte[] encryptionkey,
byte[] encryptionIV)
{
// setup decryption properties
SymmetricAlgorithm alg = SymmetricAlgorithm.Create(algorithm);
alg.Key = encryptionkey;
alg.IV = encryptionIV;

// add the decryptor layer to the stream
Stream outStream = new CryptoStream(inStream,
alg.CreateDecryptor(),
CryptoStreamMode.Read);

return outStream;
}

}
}

Creating the Sinks
The EncryptionClientSink and EncryptionServerSink look quite similar to the previous compression sinks. The major difference is that they have custom constructors that are called from their sink providers to set the specified encryption algorithm and key. For outgoing requests, the sinks will set the X-Encrypt header to “yes” and store the initialization vector in Base64 coding in the X-EncryptIV header. The complete client-side sink is shown in Listing 9-8.

The EncryptionClientSink

using System;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.IO;
using System.Text;

namespace EncryptionSink
{
public class EncryptionClientSink: BaseChannelSinkWithProperties,
IClientChannelSink
{
private IClientChannelSink _nextSink;
private byte[] _encryptionKey;
private String _encryptionAlgorithm;

public EncryptionClientSink(IClientChannelSink next,
byte[] encryptionKey,
String encryptionAlgorithm)
{
_encryptionKey = encryptionKey;
_encryptionAlgorithm = encryptionAlgorithm;
_nextSink = next;
}

public void ProcessMessage(IMessage msg,
ITransportHeaders requestHeaders,
Stream requestStream,
out ITransportHeaders responseHeaders,
out Stream responseStream)
{

byte[] IV;

requestStream = EncryptionHelper.ProcessOutboundStream(requestStream,
_encryptionAlgorithm,_encryptionKey,out IV);

requestHeaders[“X-Encrypt”]=”yes”;
requestHeaders[“X-EncryptIV”]= Convert.ToBase64String(IV);

// forward the call to the next sink
_nextSink.ProcessMessage(msg,
requestHeaders,
requestStream,
out responseHeaders,
out responseStream);

if (responseHeaders[“X-Encrypt”] != null &&
responseHeaders[“X-Encrypt”].Equals(“yes”))
{
IV = Convert.FromBase64String(
(String) responseHeaders[“X-EncryptIV”]);
responseStream = EncryptionHelper.ProcessInboundStream(
responseStream,
_encryptionAlgorithm,
_encryptionKey,
IV);
}
}

public void AsyncProcessRequest(IClientChannelSinkStack sinkStack,
IMessage msg,
ITransportHeaders headers,
Stream stream)
{

byte[] IV;

stream = EncryptionHelper.ProcessOutboundStream(stream,
_encryptionAlgorithm,_encryptionKey,out IV);

headers[“X-Encrypt”]=”yes”;
headers[“X-EncryptIV”]= Convert.ToBase64String(IV);

// push onto stack and forward the request
sinkStack.Push(this,null);
_nextSink.AsyncProcessRequest(sinkStack,msg,headers,stream);
}

public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack,
object state,
ITransportHeaders headers,
Stream stream)
{
if (headers[“X-Encrypt”] != null && headers[“X-Encrypt”].Equals(“yes”))
{

byte[] IV =
Convert.FromBase64String((String) headers[“X-EncryptIV”]);
stream = EncryptionHelper.ProcessInboundStream(
stream,
_encryptionAlgorithm,
_encryptionKey,
IV);
}

// forward the request
sinkStack.AsyncProcessResponse(headers,stream);
}

public Stream GetRequestStream(IMessage msg,
ITransportHeaders headers)
{
return null; // request stream will be manipulated later
}

public IClientChannelSink NextChannelSink {
get
{
return _nextSink;
}
}

}
}

The EncryptionServerSink shown in Listing 9-9 works basically in the same way as the CompressionServerSink does. It first checks the headers to determine whether the request has been encrypted. If this is the case, it retrieves the encryption initialization vector from the header and calls EncryptionHelper to decrypt the stream.

The EncryptionServerSink

using System;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.IO;

namespace EncryptionSink
{
public class EncryptionServerSink: BaseChannelSinkWithProperties,
IServerChannelSink
{

private IServerChannelSink _nextSink;
private byte[] _encryptionKey;
private String _encryptionAlgorithm;

public EncryptionServerSink(IServerChannelSink next, byte[] encryptionKey,
String encryptionAlgorithm)
{
_encryptionKey = encryptionKey;
_encryptionAlgorithm = encryptionAlgorithm;
_nextSink = next;
}

public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack,
IMessage requestMsg,
ITransportHeaders requestHeaders,
Stream requestStream,
out IMessage responseMsg,
out ITransportHeaders responseHeaders,
out Stream responseStream) {

bool isEncrypted=false;

//checking the headers
if (requestHeaders[“X-Encrypt”] != null &&
requestHeaders[“X-Encrypt”].Equals(“yes”))
{
isEncrypted = true;

byte[] IV = Convert.FromBase64String(
(String) requestHeaders[“X-EncryptIV”]);
// decrypt the request
requestStream = EncryptionHelper.ProcessInboundStream(
requestStream,
_encryptionAlgorithm,
_encryptionKey,
IV);
}
// pushing onto stack and forwarding the call,
// the flag “isEncrypted” will be used as state
sinkStack.Push(this,isEncrypted);

ServerProcessing srvProc = _nextSink.ProcessMessage(sinkStack,
requestMsg,
requestHeaders,
requestStream,
out responseMsg,
out responseHeaders,
out responseStream);

if (isEncrypted)
{
// encrypting the response if necessary
byte[] IV;

responseStream =
EncryptionHelper.ProcessOutboundStream(responseStream,
_encryptionAlgorithm,_encryptionKey,out IV);

responseHeaders[“X-Encrypt”]=”yes”;
responseHeaders[“X-EncryptIV”]= Convert.ToBase64String(IV);
}

// returning status information
return srvProc;
}

public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack,
object state,
IMessage msg,
ITransportHeaders headers,
Stream stream)
{
// fetching the flag from the async-state
bool isEncrypted = (bool) state;

if (isEncrypted)
{
// encrypting the response if necessary
byte[] IV;
stream = EncryptionHelper.ProcessOutboundStream(stream,
_encryptionAlgorithm,_encryptionKey,out IV);

headers[“X-Encrypt”]=”yes”;
headers[“X-EncryptIV”]= Convert.ToBase64String(IV);
}

// forwarding to the stack for further ProcessIng
sinkStack.AsyncProcessResponse(msg,headers,stream);
}

public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack,
object state,
IMessage msg,
ITransportHeaders headers)
{
return null;
}

public IServerChannelSink NextChannelSink {
get {
return _nextSink;
}
}

}
}

Creating the Providers
Contrary to the previous sink, the EncryptionSink expects certain parameters to be present in the configuration file. The first one is “algorithm”, which specifies the cryptographic algorithm that should be used (DES, TripleDES, RC2, or Rijndael). The second parameter, “keyfile”, specifies the location of the previously generated symmetric keyfile. The same file has to be available to both the client and the server sink.

The following excerpt from a configuration file shows you how the client-side sink will be configured:

In the following snippet you see how the server-side sink can be initialized:

You can access additional parameters in the sink provider’s constructor as shown in the following source code fragment:

public EncryptionClientSinkProvider(IDictionary properties,
ICollection providerData)
{
String encryptionAlgorithm = (String) properties[“algorithm”];
}

In addition to reading the relevant configuration file parameters, both the client-side sink provider and the server-side sink provider have to read the specified keyfile and store it in a byte array. The encryption algorithm and the encryption key are then passed to the sink’s constructor.

Listing 9-10: The EncryptionClientSinkProvider

using System;
using System.IO;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
using System.Collections;

namespace EncryptionSink
{
public class EncryptionClientSinkProvider: IClientChannelSinkProvider
{

private IClientChannelSinkProvider _nextProvider;

private byte[] _encryptionKey;
private String _encryptionAlgorithm;

public EncryptionClientSinkProvider(IDictionary properties,
ICollection providerData)
{
_encryptionAlgorithm = (String) properties[“algorithm”];
String keyfile = (String) properties[“keyfile”];

if (_encryptionAlgorithm == null || keyfile == null)
{
throw new RemotingException(“‘algorithm’ and ‘keyfile’ have to ” +
“be specified for EncryptionClientSinkProvider”);
}
// read the encryption key from the specified fike
FileInfo fi = new FileInfo(keyfile);

if (!fi.Exists)
{
throw new RemotingException(“Specified keyfile does not exist”);
}

FileStream fs = new FileStream(keyfile,FileMode.Open);
_encryptionKey = new Byte[fi.Length];
fs.Read(_encryptionKey,0,_encryptionKey.Length);
}

public IClientChannelSinkProvider Next
{
get {return _nextProvider; }
set {_nextProvider = value;}
}

public IClientChannelSink CreateSink(IChannelSender channel, string url,
object remoteChannelData)
{
// create other sinks in the chain
IClientChannelSink next = _nextProvider.CreateSink(channel,
url, remoteChannelData);

// put our sink on top of the chain and return it
return new EncryptionClientSink(next,_encryptionKey,
_encryptionAlgorithm);
}
}
}

Listing 9-11: The EncryptionServerSinkProvider

using System;
using System.IO;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
using System.Collections;

namespace EncryptionSink
{
public class EncryptionServerSinkProvider: IServerChannelSinkProvider
{
private byte[] _encryptionKey;
private String _encryptionAlgorithm;

private IServerChannelSinkProvider _nextProvider;

public EncryptionServerSinkProvider(IDictionary properties,
ICollection providerData)
{
_encryptionAlgorithm = (String) properties[“algorithm”];
String keyfile = (String) properties[“keyfile”];

if (_encryptionAlgorithm == null || keyfile == null)
{
throw new RemotingException(“‘algorithm’ and ‘keyfile’ have to ” +
“be specified for EncryptionServerSinkProvider”);
}

// read the encryption key from the specified fike
FileInfo fi = new FileInfo(keyfile);

if (!fi.Exists)
{
throw new RemotingException(“Specified keyfile does not exist”);
}

FileStream fs = new FileStream(keyfile,FileMode.Open);
_encryptionKey = new Byte[fi.Length];
fs.Read(_encryptionKey,0,_encryptionKey.Length);
}

public IServerChannelSinkProvider Next
{
get {return _nextProvider; }
set {_nextProvider = value;}
}

public IServerChannelSink CreateSink(IChannelReceiver channel)
{
// create other sinks in the chain
IServerChannelSink next = _nextProvider.CreateSink(channel);
// put our sink on top of the chain and return it
return new EncryptionServerSink(next,
_encryptionKey,_encryptionAlgorithm);
}

public void GetChannelData(IChannelDataStore channelData)
{
// not yet needed
}

}
}

When including the sink providers in your configuration files a s presented previously, the transfer will be encrypted as shown in Figure .

A TCP-trace of the encrypted HTTP traffic
You can, of course, also chain the encryption and compression sinks together to receive an encrypted and compressed stream.

Written by oneil

September 9, 2008 at 3:51 pm

Posted in C#

Context Bound Object- Part II

leave a comment »

When you create a class that is derived from ContextBoundObject, nothing special happens yet: by default all objects are still created in the same context. You can, however, decorate this class with an attribute that inherits from ContextAttribute and overrides the following two methods:

public bool IsContextOK(Context ctx, IConstructionCallMessage ctor)
public void GetPropertiesForNewContext(IConstructionCallMessage ctor)

When doing this, the first method is called whenever someone is creating a new instance of the target class (for example, the previous Organization class). If it returns true, nothing happens, and the object is created in the same context as the client. There won’t be the chance to intercept a call from the client to this instance by using a message sink.

If the method returns false, on the other hand, a new “virtual” remoting boundary—the context—is created. In this case the framework will subsequently call GetPropertiesForNewContext() to allow you to add the IContextProperty objects that you want to use with this context.

The implementation of a complete attribute that will later be used to create a sink to intercept calls to this object is shown below:

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Messaging;

namespace ContextBound
{
   [AttributeUsage(AttributeTargets.Class)]
   public class CheckableAttribute: ContextAttribute
   {
      public CheckableAttribute(): base (“MyInterception”) { }

      public override bool IsContextOK(Context ctx,
         IConstructionCallMessage ctor)
      {
         // if this is already an intercepting context, it’s ok for us
         return ctx.GetProperty(“Interception”) != null;
      }

      public override void GetPropertiesForNewContext(
         IConstructionCallMessage ctor)
      {
         // add the context property which will later create a sink
         ctor.ContextProperties.Add(new CheckableContextProperty());
      }
   }
}

An IContextProperty on its own doesn’t provide you with a lot of functionality

public interface IContextProperty
{
    string Name { get; }

    void Freeze(Context newContext);
    bool IsNewContextOK(Context newCtx);
}


public interface IContributeObjectSink
{
    IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink);
}

Freeze() is called when the context itself is frozen. This indicates that no change of context properties is allowed afterwards. IsNewContextOk() is called after all context attributes have added their context properties to allow your property to check for dependencies. If IContextProperty A can only be used together with IContextProperty B, it can check in this method if both properties are available for the newly created context. If this method returns false, an exception will be thrown.

Name simply has to return the context property’s name that will be used to retrieve it by calling Context.GetProperty(“<name>”). To be able to create a sink to intercept calls to this object, this class will have to implement one of the following interfaces: IContributeObjectSink, IContributeEnvoySink, IContributeClientContextSink, or IContributeServerContextSink. In the examples to follow, I use IContributeObjectSink.To create a new instance of CheckerSink, you can implement the IContextProperty as shown here
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Messaging;

namespace ContextBound
{
   public class CheckableContextProperty: IContextProperty,
      IContributeObjectSink

   {
      public bool IsNewContextOK(Context newCtx)
      {
         return true;
      }

      public void Freeze(Context newContext)
      {
         // nothing to do
      }

      public string Name
      {
         get
         {
            return “Interception”;
         }
      }

      public IMessageSink GetObjectSink(MarshalByRefObject obj,
            IMessageSink nextSink)
      {
         return new CheckerSink(nextSink);
      }

   }
}
CheckerSink itself is a common IMessageSink implementation. Its first iteration is shown below
using System;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;

namespace ContextBound
{

   public class CheckerSink: IMessageSink
   {
      IMessageSink _nextSink;

      public CheckerSink(IMessageSink nextSink)
      {
         _nextSink = nextSink;
      }

      public IMessage SyncProcessMessage(IMessage msg)
      {
         Console.WriteLine(“CheckerSink is intercepting a call”);
         return _nextSink.SyncProcessMessage(msg);

      }

      public IMessageCtrl AsyncProcessMessage(IMessage msg,
         IMessageSink replySink)
      {
         Console.WriteLine(“CheckerSink is intercepting an async call”);
         return _nextSink.AsyncProcessMessage(msg,replySink);

      }

      publicIMessageSink NextSink
      {
         get
         {
            return _nextSink.
         }
      }
   }
}
To enable this way of intercepting the Organization class shown at first part of this series you have to mark it with [Checkable] and have it inherit from ContextBoundObject to create the context property.
using System;

namespace ContextBound
{
   [Checkable]
   public class Organization: ContextBoundObject
   {
      String _name;
      double _totalDonation;

      public String Name
      {
         set
         {
            _name = value;
         }
         get
         {
            return _name;
         }
      }

      public void Donate(double amount)
      {
         Organization x = new Organization();
         x.Name = “Hello World”;
         _totalDonation = _totalDonation + amount;
      }

   }
}

A simple client for this class is shown below:

using System;
using System.Runtime.Remoting.Contexts;

namespace ContextBound
{
   public class TestClient
   {
      public static void Main(String[] args) {
         Organization org = new Organization();
         Console.WriteLine(“Will set the name”);
         org.Name = “Happy Hackers”;
         Console.WriteLine(“Will donate”);
         org.Donate(103);

         Console.WriteLine(“Finished, press <return> to quit.”);
         Console.ReadLine();
      }
   }
}

The first step to enabling the sink to do something useful is to create a custom attribute that will later be used to designate a parameter’s maximum length and maximum value. This attribute, which can be used for parameters and methods, stores the properties MaxLength, MaxValue, and NonNull . Its DoCheck() method will later be called by the sink to check a given value against the attribute’s definition.

using System;

namespace ContextBound
{
   [AttributeUsage (AttributeTargets.Parameter | AttributeTargets.Method)]
   public class CheckAttribute: Attribute
   {
      private int _maxLength;
      private int _maxValue;
      private bool _nonNull;

      public int MaxLength {
         get {
            return _maxLength;
         }
         set {
            _maxLength = value;
         }
      }

      public int MaxValue
      {
         get
         {
            return _maxValue;
         }
         set
         {
            _maxValue = value;
         }
      }

      public bool NonNull
      {
         get
         {
            return _nonNull;
         }
      set
      {
         _nonNull = value;
      }
   }
   public void DoCheck (Object val)
      {
        // check for NonNull
        if (_nonNull && val == null)
        {
           throw new Exception(“This value must not be null”);
        }

        // check for MaxLength
        if (_maxLength > 0 && val.ToString().Length > _maxLength)
        {
           throw new Exception(“This value must not be longer than “ +
                     _maxLength + “ characters”);
        }

        // check for MaxValue
        if (_maxValue > 0)
        {
           if ((double) val > _maxValue)
           {
              throw new Exception(“This value must not be higher than “ +
                    _maxValue );
           }

        }
     }
  }
}

To make use of this attribute in the organization class, you have to mark the parameter to Donate() and the set method for the Name property as shown here:
public String Name { [Check(NonNull=true,MaxLength=30)] set { _name = value; } get { return _name; } } public void Donate([Check(NonNull=true,MaxValue=100)] double amount) { _totalDonation = _totalDonation + amount; }

Written by oneil

September 9, 2008 at 3:02 pm

Posted in C#

Context Bound Object- Part I

leave a comment »

 

  Caution 

Everything in this article is 100 percent undocumented. Reliance on these techniques is not supported by either Microsoft.. Use at your own risk! If your computer won’t work afterwards, your toaster blows up, or your car doesn’t start, I assume no liability whatsoever.You’re now about to enter the uncharted territories of .NET and you do so on your own risk. I can only provide some guidance.

Well, it’s great that you’re still with me after this introductory warning. So let’s start with a look at some common business applications. You will quite likely have some object model that holds local data before it’s committed to the database. Those classes will contain parts of your business logic. For example, assume that your application provides an instant way for employees of your company to donate various amounts of their paychecks to charity organizations. In that case you might have a data object that looks like the one shown below , which allows a user to set an organization’s name and the donation of a specified amount to it.

Start example

using System;

namespace ContextBound
{
   public class Organization
   {
      String _name;
      double _totalDonation;
      public String Name
      {
         set
         {
            _name = value;
         }
         get
         {
            return _name;
         }
      }

      public void Donate(double amount)
      {
         _totalDonation = _totalDonation + amount;
      }
   }
}

End example

You might also have some database restriction or business logic that limits an organization’s name to 30 characters and allows a maximum donation of $100.00. Therefore you need to extend Donate() and the setter of Name to check for this logic:

public String Name
{
   set
   {
      if (value != null && value.Length > 30)
      {
         throw new Exception(“This field must not be longer than 30 characters”);
      }

      _name = value;
   }
   get
   {
      return _name;
   }
}

public void Donate(double amount)
{

   if (amount > 100)
   {
      throw new Exception(“This parameter must not be greater than 100.”);
   }
   _totalDonation = _totalDonation + amount;
}

You’re checking the business logic and your application works as expected. So far, so good. The problems only commence as soon as more developers start using your objects as the base for their applications because they don’t know about those restrictions by reading the interface definition alone. As in most real-world applications, the business logic is in this case hidden inside the implementation and is not part of the metadata level. There is no way for another developer to tell that the maximum amount for a valid donation is $100.00 without looking at your source code.

If you’re a well-informed developer, you already know that you can at least document those parameters using inline XML comments to automatically generate online documentation for your classes—but you still have to document and implement the logic in two separate places. If you’ve never, ever changed any implementation detail without updating the inline documentation, you don’t need to read further—you already solved the problem.

In most projects though (at least in some I’ve recently heard of) there is a direct proportionality between days to deadline and quality of documentation. Somehow people tend to forget to update comments as soon as their boss is reminding them that they should have shipped it yesterday.

Wouldn’t it be great to just specify those checks using some source code attributes and have some “black magic” happen between the client and your objects that takes care of checking the passed values against those attributes?

In a perfect world, these methods might simply look like this:

public String Name
{
   [Check(MaxLength=30)]
   set
   {
      _name = value;
   }
   get
   {
      return _name;
   }
}

public void Donate([Check(MaxValue=100)] double amount)
{
   _totalDonation = _totalDonation + amount;
}

Now the documentation of your business logic is applied on the metadata level! You could easily use reflection to generate printed or online documentation that includes these basic business logic checks as well.

Well, unfortunately, no checks have been done yet. In fact, when using this class, you could easily set Name to any possible value and Donate() to whatever amount you’d like.

  Caution 

You’re now really about to read about unsupported and undocumented features of .NET Framework.Your mileage may vary.

What’s still missing is that magic something I mentioned that would sit between the client and your object (running maybe within the same process and application) and perform those checks. This is where ContextBoundObject enters the game we will explore on next series.

Written by oneil

September 9, 2008 at 2:40 pm

Posted in C#