Friday, February 27, 2009

Getting all entries in an Enum as a list

I needed some code to get each field in an enum as its enum type and not just its string name or value. Most example on the web seem to use reflection, or getting the name and converting that to an enum, I wanted a simpler way:

User[] allValues = (User[])Enum.GetValues(typeof (User));
List<User> allUserFields = new List<User>(allValues);

That was the easiest way I could find, and is good enough for now, but maybe someone else has a nicer way...?

Alternatively you can get the List directly using:

((User[])Enum.GetValues(typeof (User))).ToList()

However if you need todo any processing on the list I find the first way more readable.

Tuesday, February 24, 2009

Inspecting Messages with an IClientMessageInspector

I was building up a library which consumes a RESTful API, and needed some way of intercepting the incoming messages before serialisation so I could inject some custom logic. The API is facebooks one in particular which isn't really truly RESTful but that's another story.

Effectively some of the service methods can either return the expected return message if nothing has gone wrong, or an exception POX message if something has gone wrong. This could range from a mal-formed querystring exception to permissions or an incorrect method call. So I wanted some way to catch this and throw a proper .NET exception instead of WCF throwing a could not deserialise exception when it's unable to hydrate an object designed to match this message:

<?xml version="1.0" encoding="UTF-8"?>
<fql_query_response 
  xmlns="http//api.facebook.com/1.0/" 
  xmlnsxsi="http//www.w3.org/2001/XMLSchema-instance" 
  xsischemaLocation="http//api.facebook.com/1.0/ http//api.facebook.com/1.0/facebook.xsd"
  list="true">
  <user>
    <name>Mark Zuckerberg</name>
  </user>
  <user>
    <name>Chris Hughes</name>
  </user>
</fql_query_response>


with this message:

<?xml version="1.0" encoding="UTF-8"?>
<error_response
  xmlns="http://api.facebook.com/1.0/"
  xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation ="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd">
  <error_code>5</error_code>
  <error_msg>Unauthorized source IP address (ip was 10.1.2.3)</error_msg>
</error_response>


So I really wanted to hook into the point at which WCF does the deserialising, catch the exception, and then rethrow my custom facebook exception. However I wasn't able to pull that off (lack of documentation) so for now I'm simply inspecting each message individually. This is not the best solution but will do until I figure out how to do it more gracefully and efficiently. It serves as an example for now.

The first thing we need to do is add a new behaviour to our Endpoint which will subsequently add our new message inspection logic.


var customServiceProxy = new ChannelFactory<ICustomService>("CustomService");
customServiceProxy.Endpoint.Behaviors.Add(new HookServiceBehaviour());


The next step is to add our new message inspector.


public class HookServiceBehaviour  IEndpointBehavior     
{         
    #region Implementation of IEndpointBehavior               
    
    public void Validate(ServiceEndpoint endpoint){}         
   
    public void AddBindingParameters(ServiceEndpoint endpoint, 
        BindingParameterCollection bindingParameters){}         
    
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 
        EndpointDispatcher endpointDispatcher){}                  
    
    public void ApplyClientBehavior(ServiceEndpoint endpoint,                
        ClientRuntime clientRuntime)        
    {            
        clientRuntime.MessageInspectors.Add(new CustomMessageInspector());        
    }     
        
    #endregion  
}


And implement our custom message inspector:


public class CustomMessageInspector  IClientMessageInspector    
{         
    #region Implementation of IClientMessageInspector              
    public object BeforeSendRequest(ref Message request,             
        IClientChannel channel)         
    {             
        return request;         
    }              
    public void AfterReceiveReply(ref Message reply, object correlationState)        
    {            
        MessageBuffer messageBuffer = reply.CreateBufferedCopy(int.MaxValue);            
        Message message = messageBuffer.CreateMessage();                 
        XmlDictionaryReader contents = message.GetReaderAtBodyContents();                        
        
        //inspect contents and do what you want, throw a custom              
        //exception for example...                 
        //We need to recreate the reply to resume the deserialisation as it             
        //can only be read once.            
        reply = messageBuffer.CreateMessage();            
        messageBuffer.Close();        
    }             
    #endregion    
}


An actual implementation of this example can be found here: http://www.codeplex.com/fluentfacebook

Thursday, February 5, 2009

Dog slow WPF transparency

It's been awhile since my last post, I've been busy at work so had to take a break from my TeamCity exploits, and then I got side-tracked building a little utility for myself.

The application is built in WPF and is yet another .NET natural language command window, but with some neat tricks. It was however, performing absolutely terribly, and I thought I only had myself to blame. Initially I thought it was me patching into some unmanaged funtions for some of the jiggery-pokery, as outlined below.

zrclip-001p2b8c83ba.png

However, I was unable to work out why SOO much time was being taken in the GetMessageW and DispatchMessage funtions, it was a real mind-f**k. After exausting all possibilities, I tried some random attacks here and there, one of them was turning off the transparency on the main window, and low and behold, the application is now performing super-quick, but why? Some googling turn up this.

So unfortunately, if your on an unpatched Vista or XP, you will need to get one of the following patches...

Vista

XP

According to the thread it seems the hotfix is already in Vista SP1 and XP SP3. Glad I found this one before... doesn't bear thinking about; my fault for not updating to SP1.