2008-11-27

The GetField method

Last month I wrote an article about clicking a button in a winforms application and in my code example I referred to the ReflectionUtil.GetField method.
The ReflectionUtil class is an utility class that I have used for several years and they provide a better/easier way to use reflection. They are in line to be converted as Extension methods for the Type type, but for now they are ordinary static methods.
I'll post the GetField method to allow the example to be complete.

#region public static object GetField(Type type, string name, object instance)
/// <summary>
/// Gets the field from the instance
/// </summary>
/// <param name="type">The <see cref="Type"/> that contains the field</param>
/// <param name="name">The name of the field</param>
/// <param name="instance">The instance to get the value from</param>
/// <returns>The value of the field</returns>
public static object GetField(Type type, string name, object instance) {
return InvokeMember(type, name, instance, BindingFlags.GetField | BindingFlags.Instance);
}
#endregion

The GetField only wraps the InvokeMember method. (I have more wrappers named GetProperty, SetField, SetProperty, InvokeMethod, InvokeStaticMethod etc. that calls the InvokeMember method)
#region public static object InvokeMember(Type type, string name, object instance, BindingFlags flags, params object[] parameters)
/// <summary>
/// Invokes the member
/// </summary>
/// <param name="type">The <see cref="Type"/> that contains the member</param>
/// <param name="name">The name of the member</param>
/// <param name="instance">The instance to invoke on</param>
/// <param name="flags">The <see cref="BindingFlags"/> to use</param>
/// <param name="parameters">The <see cref="object"/> array to pass as parameters</param>
/// <returns>The returnvalue</returns>
public static object InvokeMember(Type type, string name, object instance, BindingFlags flags, params object[] parameters) {
if (instance == null) {
flags |= BindingFlags.Static;
flags &= ~BindingFlags.Instance;
}

try {
return type.InvokeMember(name,BindingFlags.Public | BindingFlags.NonPublic | flags,null,instance,parameters);

//if the target threw an exception, throw this instead.
} catch (TargetInvocationException e) {

// if no exception is found, throw the TIException instead.
if (e.InnerException == null)
throw;
throw e.InnerException;
}
}
#endregion

Hope the click button example can make more sense now :)

2008-11-02

Mvc for Winforms - Mapping the View event to the Controller action Part II

This time I will try to deliver part of the answer to the requirements from my previous post. To recap I would like to be able to connect a component to a controller action by calling the RegisterAction method like below.

Controller.RegisterAction(saveButton, "Save");
Controller.RegisterAction(createBoldTextButton, "CreateText", new {name="Bold", type=4});
Controller.RegisterAction(myTextBox, "ValidateText","Validating", null);
And letting those events call the actions defined below
public void Save() {...}
public void CreateText(string name, int type) {...}
public void ValidateText(CancelEventArgs e, object source) {...}
I will divide the solution in two steps and in this post I will cover the first, that is capturing the event of the object.

We start by creating the RegisterAction method. We get the object which event should be listened to and sometimes also the name of the event that we should listen to. If this argument isn't supplied we need to find the DefaultEvent of the object.

By using reflection we can retrieve the DefaultEventAttribute of the object. The following unit test shows how to get the attribute for a Button object. Notice the true flag on the GetCustomAttributes call. Since the DefaultEventAttribute is not present on the Button class itself, we need to go down in the inheritance chain to look for the attribute. Not until we reach the Control class we find the DefaultEventAttribute.
[Test]
public void GetDefaultEventAttribute() {
object obj = new Button();
DefaultEventAttribute attribute = null;
Attribute[] attributes = obj.GetType().GetCustomAttributes(typeof(DefaultEventAttribute), true) as Attribute[];
if (attributes != null && attributes.Length > 0) {
attribute = attributes[0] as DefaultEventAttribute;
}
Assert.IsNotNull(attribute);
}
When we have the attribute, we just look at the Name property to get the name of the default event.

Now when we have the name of the event (either by parameter or using the DefaultEventAttribute) we should add listener to the event. The listener method is a method in the Controller class, not the controller Action (we will get to that in the next part), but a event hub where all the Views events will pass before they are dispatched to the correct Action. The event hub method is declared as below
public void ExecuteAction(object source, object eventArgs, ActionData actionData)
The ExecuteAction method takes three parameters. The source and arguments of the event that was fired (this is the same values that the original event passes along). The third parameter contains the data for the action that is to take place, like the name of the action and any value parameters (The values that are stated when registering the action).

So we got the object and the name of the event and the target method of the event, but how can we connect them?

My first thought was to generate a delegate to the ExecuteAction and use reflection to get the EventInfo for the event and use the AddEventHandler of the EventInfo class to bind to the ExecuteAction method.
[Test]
public void BindToButtonClickEvent() {
object obj = new Button();
EventInfo info = obj.GetType().GetEvent("Click");
MethodInfo method = GetType().GetMethod("ExecuteAction");
Delegate d = Delegate.CreateDelegate(typeof(MyExecuteAction),method);
info.AddEventHandler(obj,d);
}
private delegate void MyExecuteAction(object source, object eventArgs, ActionData actionData);
public void ExecuteAction(object source, object eventArgs, ActionData actionData) {}
But when I ran this code I got an Exception
System.ArgumentException: Error binding to target method.
Of course this won't work since the Click event cannot directly connect to the ExecuteAction method because the Click event can only add handlers that matches the EventHandler delegate, a method with a void return value and two arguments, object and EventArgs.

The conclusion of this is that if I would like to have a single method that acts as an event hub and it must be able to handle any type of delegate that the event declares (note that events as practice should always return void and take two arguments, object and a instance of an EventArgs derived class), I need to generate this method in runtime.

The first option that comes to mind is using Emit. I have tested this in the past, it has worked but comes not so natural to me. Oren Eini used this technique but since I would like to pass a local variable (the ActionData instance) in the call, I needed to modify this piece of code, and possibly use an external list of ActionData instances if I couldn't pass them along using the dynamic method, I searched a bit more for an alternative (second opinion)...

Finally I came across an answer from Mark Cidade that compiled a method in runtime using lambda expressions and that was fairly easy to modify.
First we need to setup the call to the ExecuteAction method. This is done using a lambda expression and storing it in the Action delegate.
ActionData actionData = new ActionData("Save", null);
//Create the delegate using an lambda expression
Action<object,object> eventHubCall = (source, e) => ExecuteAction(source, e, actionData);
This action will take two parameters and call the ExecuteAction just the way as we would like it to. The problem is that this expression is not typed the correct way as the event is so we need to create a new method using lambda expressions again but with the correct declaration. So we start by getting the information about the event.
Type type = obj.GetType();
EventInfo evt = type.GetEvent("Click");
ParameterInfo[] eventParams = evt.EventHandlerType.GetMethod("Invoke").GetParameters();
Then we create the lambda
ParameterExpression[] parameters = eventParams.
Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray();
MethodCallExpression body = Expression.Call(Expression.Constant(eventHubCall),
eventHubCall.GetType().GetMethod("Invoke"), parameters);
LambdaExpression lambda = Expression.Lambda(body, parameters);
Now we have a method with two parameters of the correct type, the only thing left is to create a delegate that we can use for adding to the event.
Delegate proxy = Delegate.CreateDelegate(evt.EventHandlerType, lambda.Compile(), "Invoke", false);
evt.AddEventHandler(obj, proxy);
Tada!! No more Error binding to target method errors.
The complete test follows.
[Test]
public void BindToButtonClickEvent2() {
ActionData actionData = new ActionData("Save", null);
//Create the delegate using an lambda expression
Action<object,object> eventHubCall = (source, e) => ExecuteAction(source, e, actionData);

object obj = new Button();
//find the exact definition of the event
Type type = obj.GetType();
EventInfo evt = type.GetEvent("Click");
ParameterInfo[] eventParams = evt.EventHandlerType.GetMethod("Invoke").GetParameters();

//create a new lambda expression using the correct parameters
ParameterExpression[] parameters = eventParams.
Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray();
//call the event hub
MethodCallExpression body = Expression.Call(Expression.Constant(eventHubCall),
eventHubCall.GetType().GetMethod("Invoke"), parameters);
//create the expression with the correct parameters to match the event
LambdaExpression lambda = Expression.Lambda(body, parameters);
//and then create the delegate that wraps the lambda expression.
Delegate proxy= Delegate.CreateDelegate(evt.EventHandlerType, lambda.Compile(), "Invoke", false);

evt.AddEventHandler(obj, proxy);
}
Not so dumb ey! All credits goes to Mark Cidade for providing the elegant solution. (So I perhaps my greatest talent is to find solutions that others have done before me, and use them ;)
Anyway, that concludes part II and next we will look how to call the action method of the controller from the ExecuteAction method.

2008-11-01

Mvc for Winforms - Mapping the View event to the Controller action

In my current (or rather one of my current projects) I'm writing a small MVC framework for a WinForms application (I mentioned this in my last post). The latest issue that I stumbled upon and found worth mentioning is about connecting the Views events to the Controllers actions.

If we look at the MVC for ASP.NET the events is the requests by the web client and the URL contains the information that the Routing module uses to find an appropriate action to call. It should be nice to have such a feature in the WinForms solution as well so after some thought I summarized a small example.

In my view I can have several objects (not necessary controls) that can/will fire some events (like a button firering the click event when the user clicks it).
In my controller I have several action methods that should be called by the view when the appropriate event occurs. (Like a Save action that should be called when the user clicks the save button, thus triggering the save button click event) (nothing out of the ordinary here;)

Whats the fuzz about this then, you may think? This is normal stuff, no strange things!? (who is this dumb guy anyway...)

Yes I see your point. I could easily use the Forms designer in Visual studio, double click the Save button and enter the code below to call the Save button.

private void saveButton_Click(object sender, System.EventArgs e) {
Controller.Save();
}
But thats not what I want. I don't want to litter the View with a lot of event methods just calling one line of code. The purpose of the MVC pattern is to separate the View from the logic and if we start putting those event handler methods in the view, soon some line of controller logic will be entered in there (just to test) and we'll never find the way to clean them up again.

My vision is to register these events one by one in the Views constructor, on one line that enables me to
  • Listen to an event from an object in the view
  • Capture the event and pass it to an action in the controller
  • Allow the mapping to pass additional parameters to the action (compare the routing in MVC for ASP.NET)
If I would make the same example like the one above but using the one liner instead, it would look like below.
Controller.RegisterAction(saveButton,"Save");
If I would pass some parameters to the action I could use the same syntax as when defining default routes in MVC for ASP.NET.
Controller.RegisterAction(createBoldTextButton,"CreateText", new {name="Bold",type=4});
This would then be mapped to the action CreateText which declaration looks like below.
public void CreateText(string name, int type) {...}
If I want to listen to an event that isn't the DefaultEvent (see the DefaultEventAttribute) I could state this as well in the call
Controller.RegisterAction(myTextBox,"ValidateText","Validating", null);
And if I declare the action to include the event argument and/or source they should be passed along as well
public void ValidateText(CancelEventArgs e, object source) {...}
How do I code this to be like I want it to then???

In my next post I will try to give you a solution to the requirements above and hopefully prove that I'm not a dumb guy after all ;)
See ya!

2008-10-27

Clicking a winforms button from code

Today I was writing a unit test for a winforms application (or rather a small Mvc framework for winforms) and I would like to simulate a button click on the form to se that all events had been wired correctly.
Since the event is a delegate, I thought it would be easy to just call the Invoke/DynamicInvoke methods of the Click delegate instance to get the events fiering...
It wasn't quite that easy though. When I tried to use the code

button.Click.Invoke(this,EventArgs.Empty);
I got a compiler error

The event 'System.Windows.Forms.Control.Click' can only appear on the left hand side of += or -=

After a search I found an article by Partha S that summarized the issue quite well, namely the difference between an event and a delegate is that the event can control the chaining and the event can only be invoked from within the declaring class. Partha also provided a solution for declaring an event that can be called from external code but that don't help me since I cannot modify the Button class.

A workaround is then to use reflection for retrieving the delegate field from the button class and when we have direct access to the delegate (not through the event wrapper construct) we will be able to Invoke the event.
The code below contains a method "GetField" that is included in our base library that I can post if anyone is interested, but for now it returns the value of a field.
//All events in a Component is listed in an EventHandlerList 
//called events in the Component class.
//b is the button instance.
EventHandlerList list = ReflectionUtil.GetField(
typeof(Component), "events", b) as EventHandlerList;
Assert.IsNotNull(list,"Cannot get eventlist from component");

//in the EventHandlerList, each delegate is identified by a
//static object instance in the control class
object eventClick = ReflectionUtil.GetField(typeof(Control),
"EventClick",null);

//now we get the click event delegate
Delegate click = list[eventClick];

//finally we can invoke the listeners of the click event.
click.DynamicInvoke(this, EventArgs.Empty);
So now I can click da button in my unittest :)

2008-10-22

BadImageFormatException when loading Microsoft.TeamFoundation.Client.dll

I tried to make a small winforms application on my 64bit Vista that talks with our TeamFoundationServer today. I use a plugin based application that loads the assemblies using Assembly.Load (utilizing the AppDomain.AssemblyResolve event).

The problem surfaced when I tried to activate a function in one of my loaded plugins that used the TeamFoundation client dll, this was not located in the same folder as the exe.
Using the AppDomain.CurrentDomain.AssemblyResolve event, I was easy to find the assembly dll and using Assembly.LoadFrom(filename) I could return the needed assembly.

The problem was that when I tried to do this, I got a BadImageFormatException stating that

Could not load file or assembly 'file:///C:\MyPath\MyProject\bin\Debug\Plugins\MyPlugin\Microsoft.TeamFoundation.Client.dll' or one of its dependencies. An attempt was made to load a program with an incorrect format.
at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.LoadFrom(String assemblyFile)
at Meridium.Plugins.PluginLoader.CurrentDomain_AssemblyResolve(Object sender, ResolveEventArgs args) in C:\VSS\TFS\Meridium\Source\Meridium.Plugins\PluginLoader.cs:line 606

This seemed not quite correct, since the assembly at the location is ok and can be opened for instance in Reflector.

After a bit of searching I found more clues pointing to the fact that a 64bit application cannot load 32bit assemblies if they are in "Mixed mode" since they contain native code and thus are platform specific. The forum post http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=665884&SiteID=1 stated the problem and provided the solution to set the project output to be built for the x86 platform.

After changing the settings in the project, it worked like a charm.

2008-08-26

Adding an appender to log4net in runtime

I was testing logging with log4net today and wanted to create a unit test that verified that the method logged alright.
To verify that the logging worked I wanted to create a log appender (the MemoryAppender) and add it to the log4net configuration to be able to compare the actual logged messages with the expected ones. (The MemoryAppender has the GetEvents() and Clear() methods which works well in a unit test case).

Normally you use a configuration file (xml) to add your appenders but using configuration files in a unit test is not quite the optimal solution. So how could I set it in runtime in my test method. After a bit of testing and reading (I wonder why the testing and reading always seem to be in that order? (for me anyway) Shouldn't you start reading about the theory before you start coding and testing? ;)

I found some tips using the Hierarchy class to access the root level logger and add append the MemoryAppender there.

The important steps where to set the Configured property of the Hierarchy and call the RaiseConfigurationChanged() method to actually utilize the configuration changes.

Here is a complete example.

//First create and configure the appender
MemoryAppender memoryAppender = new MemoryAppender();
memoryAppender.Name = "MyAppender";

//Notify the appender on the configuration changes
memoryAppender.ActivateOptions();

//Get the logger repository hierarchy.
log4net.Repository.Hierarchy.Hierarchy repository =
LogManager.GetRepository() as Hierarchy;

//and add the appender to the root level
//of the logging hierarchy
repository.Root.AddAppender(memoryAppender);

//configure the logging at the root.
repository.Root.Level = Level.All;

//mark repository as configured and
//notify that is has changed.
repository.Configured = true;
repository.RaiseConfigurationChanged(EventArgs.Empty);
Now we have prepared the logging for the root (all log events) and directed it to the memoryAppender.
//Call the method to test (this one creates
//an info level message to the log
//stating the string "GetConfiguration()".
ServerDataFactory.Instance.GetConfiguration();
When we have ran the code that has produced some logging, we add assert statements to check that the code behaved as we expected.
//use the memoryAppender to get the logged events
LoggingEvent[] events = memoryAppender.GetEvents();

//We only expect one log statement on the info level
Assert.AreEqual(1, events.Length,
"Incorrect number of log statements");
LoggingEvent e = events[0];
Assert.AreEqual(Level.Info, e.Level,
"Incorrect logging level");
Assert.AreEqual("GetConfiguration()", e.MessageObject,
"Incorrect log message");
Thats how I did it. Does anyone has another solution that works well?

2008-02-28

ClickOnce, SharePoint and Anonymous access

I have been laborating with a ClickOnce deployed application in SharePoint and all seems to work well until I tried to connect to SharePoint (wss3) from a workstation where I used another locally logged in user than the one I used to authenticate myself at the SharePoint server.

When I then tried to access the ClickOnce application the bootstrap downloader (ApplicationActivator) prompts a "Cannot Start Application" error that says "Cannot retrieve application. Authentication error".

The details of the error indicates a 401 response from the webserver.

System.Deployment.Application.DeploymentDownloadException (Unknown subtype)
- Downloading http://wm20031/_layouts/MyApp/MyApp.application did not succeed.
- Source: System.Deployment
- Stack trace:
at System.Deployment.Application.SystemNetDownloader.DownloadSingleFile(DownloadQueueItem next)
at System.Deployment.Application.SystemNetDownloader.DownloadAllFiles()
at System.Deployment.Application.FileDownloader.Download(SubscriptionState subState)
at System.Deployment.Application.DownloadManager.DownloadManifestAsRawFile(Uri& sourceUri, String targetPath, IDownloadNotification notification, DownloadOptions options, ServerInformation& serverInformation)
at System.Deployment.Application.DownloadManager.DownloadDeploymentManifestDirectBypass(SubscriptionStore subStore, Uri& sourceUri, TempFile& tempFile, SubscriptionState& subState, IDownloadNotification notification, DownloadOptions options, ServerInformation& serverInformation)
at System.Deployment.Application.DownloadManager.DownloadDeploymentManifestBypass(SubscriptionStore subStore, Uri& sourceUri, TempFile& tempFile, SubscriptionState& subState, IDownloadNotification notification, DownloadOptions options)
at System.Deployment.Application.ApplicationActivator.PerformDeploymentActivation(Uri activationUri, Boolean isShortcut, String textualSubId, String deploymentProviderUrlFromExtension)
at System.Deployment.Application.ApplicationActivator.ActivateDeploymentWorker(Object state)

--- Inner Exception ---
System.Net.WebException
- The remote server returned an error: (401) Unauthorized.
- Source: System
- Stack trace:
at System.Net.HttpWebRequest.GetResponse()
at System.Deployment.Application.SystemNetDownloader.DownloadSingleFile(DownloadQueueItem next)

If I check the weblog we can se the following two entries

2008-02-28 12:05:29 W3SVC1265274740 192.168.1.199 GET /_layouts/MyApp/MyApp.application 80 domain\administrator 192.168.1.2 Mozilla/4.0+(...) 206 0 0
2008-02-28 12:05:29 W3SVC1265274740 192.168.1.199 GET /_layouts/MyApp/MyApp.application 80 - 192.168.1.2 - 401 5 0
The first is the webbrowser access where I authenticate using the domain\administrator account and the application manifest is successfully returned to the browser. Then the ApplicationActivator tries the same thing as anonymous but fails utterly. Ok, so perhaps this is only anonymous user access that is denied, but I had already checked the "Enable Anonymous access" in the IIS manager for the _layouts/MyApp folder.

Testing with firefox and anonymous access proved that is not the access rights that is incorrectly set.
2008-02-28 12:16:56 W3SVC1265274740 192.168.1.199 GET /_layouts/MyApp/MyApp.application 80 - 192.168.1.2 Mozilla/5.0+... 200 0 0
So the IIS is not blocking access, then it has to be SharePoint? (_layouts is a SharePoint managed folder)

To test I checked the code for the System.Deployment.Application.SystemNetDownloader.DownloadSingleFile(DownloadQueueItem next) method and reproduced the code that was processed there.

WebRequest request = WebRequest.Create("http://wm20031/_layouts/MyApp/MyApp.application");
request.Credentials = CredentialCache.DefaultCredentials;
RequestCachePolicy policy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
request.CachePolicy = policy;
HttpWebRequest request2 = request as HttpWebRequest;
if (request2 != null) {
request2.UnsafeAuthenticatedConnectionSharing = true;
request2.AutomaticDecompression = DecompressionMethods.GZip;
request2.CookieContainer = GetUriCookieContainer(request2.RequestUri);
WebRequest.DefaultWebProxy.Credentials = CredentialCache.DefaultCredentials;
}
WebResponse response = null;
response = request.GetResponse();
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
Console.WriteLine(reader.ReadToEnd());
}
response.Close();
This code fails with the same exception as the Application Activator and after some debugging I noticed that if I didn't copy the cookies to the request, then it worked?!
Checking further in the cookie container, it only added one cookie MSOWebPartPage_AnonymousAccessCookie and with the value of the webapplication port. (80)

The GetUriCookieContainer parsed the cookies retrieved in IE for the url. If I removed the cookie for the server in the temporary internet files folder, the code above worked even with the cookie row (no cookies added).

To solve the problem one workaround is to create a virtual folder in the root of the SharePoint site (MyApp) (that is not managed by SharePoint), map this to the same folder as _layouts/MyApp, allow anonymous access there and navigate to that url instead (/MyApp/MyApp.application). This works since the cookie is not present for the application.

But what I really like to know is the purpose of the MSOWebPartPage_AnonymousAccessCookie, especially why SharePoint sets this cookie when I navigate to the site and why it throws an access denied when I try to access with the cookie set?