Global.asax event handling internals (part 3 of 3)

// In HttpApplicationFactory

private void HookupEventHandlersForAppplicationAndModules(MethodInfo[] handlers)
{
      // handlers receives a list of Global’s methods.
      for (int num1 = 0; num1 < handlers.Length; num1++)
      {
            MethodInfo info1 = handlers[num1];
            string text1 = info1.Name;
            int num2 = text1.IndexOf('_'); // Splits the method name to get the object and method’s name.
            string text2 = text1.Substring(0, num2);
            object obj1 = null;
            if (string.Compare(text2, "Application", true, CultureInfo.InvariantCulture) == 0)
            {
                  obj1 = this// Maps “Application_*” to HttpApplication
            }
            else if (this._moduleCollection != null)
            {
                  obj1 = this._moduleCollection[text2];
            }
            if (obj1 != null)
            {
                  // Tries to get an EventDescription that matches a method in Global.
                  Type type1 = obj1.GetType();
                  EventDescriptorCollection collection1 = TypeDescriptor.GetEvents(type1);
                  string text3 = text1.Substring(num2 + 1);
                  EventDescriptor descriptor1 = collection1.Find(text3, true);
                  if ((descriptor1 == null) && (string.Compare(text3.Substring(0, 2), "on", true, CultureInfo.InvariantCulture) == 0))
                  {
                        text3 = text3.Substring(2);
                        descriptor1 = collection1.Find(text3, true);
                  }
                  // Gets a method that corresponds to the “+” in Type.Event += EventHandler
                  MethodInfo info2 = null;
                  if (descriptor1 != null)
                  {
                        EventInfo info3 = type1.GetEvent(descriptor1.Name);
                        if (info3 != null)
                        {
                              info2 = info3.GetAddMethod();
                        }
                  }
                  if (info2 != null)
                  {
                        ParameterInfo[] infoArray1 = info2.GetParameters();
                        if (infoArray1.Length == 1)
                        {
                              Delegate delegate1 = null;
                              ParameterInfo[] infoArray2 = info1.GetParameters();
                              if (infoArray2.Length == 0)
                              {
                                    if (infoArray1[0].ParameterType != typeof(EventHandler))
                                    {
                                          goto Label_0168;
                                    }
                                    ArglessEventHandlerProxy proxy1 = new ArglessEventHandlerProxy(this, info1);
                                    delegate1 = proxy1.Handler;
                              }
                              else
                              {
                                    try
                                    {
                                          // Creates the delegate. Corresponds to “new EventHandler(metodoDeGlobal)”
                                          delegate1 = Delegate.CreateDelegate(infoArray1[0].ParameterType, this, text1);
                                    }
                                    catch (Exception)
                                    {
                                          goto Label_0168;
                                    }
                              }
                              try
                              {
                                    object[] objArray1 = new object[1] { delegate1 } ;
                                    // Binds the method to the event. 
                                    // Corresponds to executing the “+” in Type.Event += EventHandler                                    info2.Invoke(obj1, objArray1);
                              }
                              catch (Exception)
                              {
                              }
                        }
                  }
            }
      Label_0168:;
      }

}

Global.asax event handling internals (part 2 of 3)

// HttpApplication class, from which Global is derived.
private void ReflectOnApplicationType()
{
      // Enumerates all Global and it’s wrapper methods..
      ArrayList list1 = new ArrayList();
      MethodInfo[] infoArray1 = this._theApplicationType.GetMethods(BindingFlags.NonPublic | (BindingFlags.Public | (BindingFlags.Static | BindingFlags.Instance)));
      MethodInfo[] infoArray2 = infoArray1;
      int num2 = 0;
      while (num2 < infoArray2.Length)
      {
            MethodInfo info1 = infoArray2[num2];
            // If the method’s name looks like an event handler, adds it to the list.
            if (this.ReflectOnMethodInfoIfItLooksLikeEventHandler(info1))
            {
                  list1.Add(info1);
            }
            num2++;
      }
      Type type1 = this._theApplicationType.BaseType;
      if ((type1 != null) && (type1 != typeof(HttpApplication)))
      {
            infoArray1 = type1.GetMethods(BindingFlags.NonPublic | (BindingFlags.Static | BindingFlags.Instance));
            infoArray2 = infoArray1;
            for (num2 = 0; num2 < infoArray2.Length; num2++)
            {
                  MethodInfo info2 = infoArray2[num2];
                  if (info2.IsPrivate && this.ReflectOnMethodInfoIfItLooksLikeEventHandler(info2))
                  {
                        list1.Add(info2);
                  }
            }
      }
      // Stores the list of methods for further use.
      this._eventHandlerMethods = new MethodInfo[list1.Count];
      for (int num1 = 0; num1 < this._eventHandlerMethods.Length; num1++)
      {
            this._eventHandlerMethods[num1] = (MethodInfo) list1[num1];
      }
}

 

//Também na classe HttpApplicaton

private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m)
{
      ParameterInfo[] infoArray1;
      string text1;
      if (m.ReturnType == typeof(void))
      {
            infoArray1 = m.GetParameters();
            switch (infoArray1.Length)
            {
                  case 0:
                  {
                        goto Label_007A;
                  }
                  case 2:
                  {
                        // The event handler’s traditional signature in .NET: object, EventArgs
                        if (infoArray1[0].ParameterType != typeof(object))
                        {
                              return false;
                        }
                        if ((infoArray1[1].ParameterType != typeof(EventArgs)) && !infoArray1[1].ParameterType.IsSubclassOf(typeof(EventArgs)))
                        {
                              return false;
                        }
                        goto Label_007A;
                  }
            }
      }
      // If it doesn’t have 0 or 2 parameters, the method is not an event handler.
      return false;
Label_007A:
      text1 = m.Name;
      int num1 = text1.IndexOf('_');
      if ((num1 <= 0) || (num1 > (text1.Length - 1)))
      {
            // “_” has to be within the name but it cannot be neither the first nor the last character.
            return false;
      }
      // Special cases that won’t be treated in HookupEventHandlersForAppplicationAndModules
      if ((string.Compare(text1, "Application_OnStart", true, CultureInfo.InvariantCulture) == 0) || (string.Compare(text1, "Application_Start", true, CultureInfo.InvariantCulture) == 0))
      {
            this._onStartMethod = m;
            this._onStartParamCount = infoArray1.Length;
      }
      else if ((string.Compare(text1, "Application_OnEnd", true, CultureInfo.InvariantCulture) == 0) || (string.Compare(text1, "Application_End", true, CultureInfo.InvariantCulture) == 0))
      {
            this._onEndMethod = m;
            this._onEndParamCount = infoArray1.Length;
      }
      else if ((string.Compare(text1, "Session_OnEnd", true, CultureInfo.InvariantCulture) == 0) || (string.Compare(text1, "Session_End", true, CultureInfo.InvariantCulture) == 0))
      {
            this._sessionOnEndMethod = m;
            this._sessionOnEndParamCount = infoArray1.Length;
      }
      return true;
}

 

Global.asax event handling internals (part 1 of 3)

Some days ago I and another project member were seeing the best way to catch unhandled exceptions in ASP.NET.

We found the Global.Application_Error method in Global.asax which them.

Besides Application_Error, Visual Studio creates some other methods for handling events in Global.asax:

 

protected void Application_Start(Object sender, EventArgs e)

{

}

protected void Session_Start(Object sender, EventArgs e)

{

}

protected void Application_BeginRequest(Object sender, EventArgs e)

{

}

protected void Application_EndRequest(Object sender, EventArgs e)

{

}

protected void Application_AuthenticateRequest(Object sender, EventArgs e)

{

}

protected void Application_Error(Object sender, EventArgs e)

{

}

protected void Session_End(Object sender, EventArgs e)

{

}

protected void Application_End(Object sender, EventArgs e)

{

}

 

All the methods have a signature compatible with the EventHandler delegate but none of the events are declared in Global or HttpAppliaction.

So how are the methods bound to the events?

If you ask it to Doug Purdy (the Remoting’s father e one of the fathers of Indigo), he would probably answer that it is magic as he did when answering a question of mine about Remoting. But I (whom am not the father of Indigo or Remoting and started studying ASP.NET a couple of weeks ago) will answer:

It does the binding during application initialization using a lot of Reflection.

Handed with my debugger (Visual Studio) and .NET Reflector, I found an internal class called HttpApplicationFactory which is who initializes the application.

But before instantiating an object from Global (in fact a class derived from Global created automatically), HttpApplicationFactory enumerates all Global’s methods within the ReflectOnApplicationType method and with ReflectOnMethodInfoIfItLooksLikeEventHandler() inserts every method that has as name that looks like an event handler in a list.

This list will be enumerated by HttpApplicaton. HookupEventHandlersForAppplicationAndModules to find events whose name is “compatible” with the methods found in Global.

When a compatible name is found, Reflection is used again to bind the method to the event.

As a piece of code worth more than “ new Regex(@"((w+)s)*").Matches(milPalavras).Count” words, follow to the next post.

 

PS.: This is due to the fact that MSN Spaces has a limit on the post’s sizes.

Databinding: Web Forms vs. Windows Forms

In Windows Forms, all data binding done by the designer is saved as code in the choosen language: C# or VB.NET:

 

//

// dataGrid1

//

this.dataGrid1.DataMember = "Customers";

this.dataGrid1.DataSource = this.dataSet11;

 

On Web Forms, it’s somewhat different, because when binding a DataGrid to a DataTable via designer, the binding isn’t saved as code; instead it’s saved as markup:

 

<asp:DataGrid id=DataGrid1 style="Z-INDEX: 101; LEFT: 16px; POSITION: absolute; TOP: 32px" runat="server" DataSource="<%# dataSet11 %>" DataKeyField="CustomerID" DataMember="Customers" AutoGenerateColumns="False" EnableViewState="False">

      // Column definition

</asp:DataGrid>

 

Brazilian zip code Web Service

From Carlos Loth’s blog: Brazilian’s postal service publishes a web service to allow queries from Microsoft Office 2003.
It works like magic on Word.
I tried a quick and dirty approach to make it work from within a .NET application without success, but soon someone will figure out how to do it.
In my opinion, the postal service should open the zip code database since it’s a government owned company. For that reason it should publish the database as a web service instead of charging us for a CD-ROM subscription.

Another MCP certification

Just before TTT on last Friday (08/26/05), I passed 70-320 with a score of 952 out a total of 1000.
With this one they are already three certifications:

  • 70–306 Developing and Implementing Windows®-based Applications with Microsoft Visual Basic .NET and Microsoft Visual Studio .NET
  • 70–316 Developing and Implementing Windows-based Applications with Microsoft Visual C#; .NET and Microsoft Visual Studio .NET
  • 70–320 Developing XML Web Services and Server Components with Microsoft Visual C# .NET and the Microsoft .NET Framework

It took me about six months of study, but the exam was much easier than I thought it would be.
Now I only have to try the ASP.NET (70-315) exam to achieve MCAD. Unfortunately MCAD is going deprecated soon.

New article on Fórum Access magazine

Issue 68 of Fórum Access magazine just got published with another article written by me: “DataView with Microsoft Access-like search features”.
On the article I extend the DataView’s search features so they look somewhat like the features exposed by Microsoft Access’ Find dialog.
Unfortunately the magazine is not sold in newsstands. It’s only available for subscribers.