Angel Hernández

November 2009 - Posts

Assembler Language in .NET and some other ideas

Hi Community,

This post is about something not very recent in .NET and it’s been available for a while, but the first time I read about it I couldn’t blog it because I didn’t have a blog back then Stick out tongue and since I’m currently reading this book once again "The art of assembly language" (I think that I’m reading it for the third time… It just contains too much information) so I thought it’s worthy to mention it. Even when .NET is  managed code, in the end the CPU understands  machine code which can be represented through  assembler language. This happens the same with the rest of other languages (managed and non-managed). Back in  2001, someone had the brilliant idea of doing  ASP.NET with  assembler language.  The article can be found  here and below a sample page is depicted

The few things I learned about assembler language was back in 1995, thanks to my mate Virginia when she was about to earn her degree in Bsc. Systems Engineering and I was a freshman in Uni. I used to spend a lot of time with Microsoft C/C++ 7.0 (MFC 1.0 was bundled in it) and I thought it was cool to place inline code inside  __asm, at the same time I was learning VB 4.0 and in my spare time I played  Heretic and MK II... I remember my mom calling me to go and have lunch Stick out tongue *sigh* What a wonderful time!!!... There’s one thing I’ve been trying to keep from that time and throughout the years and it’s been like a golden rule: "whatever you do, do it with passion and… there’s no a better way to learn something new than by playing with it”  and that’s probably why I never get bored of PCs, technology and related stuff.

Changing of topics, some days ago one of our customers had a new requirement “I’d like to detect when an active session is about to expire and store the existing cart in CRM", this is something very easy and straightforward to do in ASP.NET, where I can put some code in the Global.asax and intercept the Session_End event but what I’m talking about is a SharePoint based solution with form based authentication enabled and as we all MUST know by now, SharePoint supports this authentication scheme since  version 2007, because it has always relied on  NTLM and Kerberos.  Anyways I had a couple of approaches which I mention below: 

  • Every single SharePoint application is an instance of SPHttpApplication which in turn inherits from HttpApplication, so I said  "Nah... this is a piece of cake", I just need to modify the Global.asax and write some inline code to handle Session_End  but it doesn’t work Sad so I thought about creating a new project and explicit inherit from SPHttpApplication but it doesn’t work either…  So I gave it a try to another alternative…

  • Write an HttpModule which contains a singleton and at the same time it keeps a data structure updated and in sync by a  critical section at the moment of a session timing out, code is shown below
  • using System;
    using System.ComponentModel;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Web;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.ApplicationRuntime;
    using System.IO;
    using Microsoft.SharePoint.Security;
    using System.Runtime;
    using System.Runtime.InteropServices;
    using System.Web.SessionState;
     
     
    namespace Demo {
        public class MyCustomApplication : IHttpModule {
            #region IHttpModule Members
            private SyncedInfo _synced = null;
            private GCHandle? _allocatedMem = null;
     
            private GCHandle? AllocatedMemory {
                get {
                    return _allocatedMem;
                }
                set {
                    _allocatedMem = value;
                }
            }
     
            private SyncedInfo Singleton {
                get {
                    return _synced;
                }
                set {
                    _synced = value;
                }
            }
     
            public void Dispose() {
                if (AllocatedMemory != null) {
                    AllocatedMemory.Value.Free();
                }
            }
     
            public void Init(HttpApplication context) {
                if (Singleton == null) {
                    Singleton = new SyncedInfo(context.Modules["Session"]);
                    AllocatedMemory = GCHandle.Alloc(Singleton);
                }
            }
            #endregion
        }
     
        [Serializable]
        public class SyncedInfo {
            protected SessionStateModule _sessionModule;
            protected HttpContext _currentContext = HttpContext.Current;
     
            public SyncedInfo(IHttpModule context) {
                _sessionModule = (SessionStateModule)context;
                _currentContext.Application["UserData"] = new Dictionary<string, string>();
                _sessionModule.Start += Session_Start;
                _sessionModule.End += Session_End;
            }
     
            public bool Push(string customer, string selectedData) {
                bool retval = false;
                object userData = _currentContext.Application["UserData"];
     
                try {
                    lock (userData) {
                        if (!((Dictionary<string, string>)userData).ContainsKey(customer)) {
                            ((Dictionary<string, string>)userData).Add(customer, selectedData);
                        } else {
                            ((Dictionary<string, string>)userData)[customer] = selectedData;
                        }
                        retval = true;
                    }
                } catch { } // Swallow the exception
                return retval;
            }
     
            public KeyValuePair<string, string>? Pop(string customer) {
                KeyValuePair<string, string>? retval = null;
                object userData = _currentContext.Application["UserData"];
     
                try {
                    lock (userData) {
                        if (((Dictionary<string, string>)userData).ContainsKey(customer)) {
                            retval = new KeyValuePair<string, string>(customer, ((Dictionary<string, string>)userData)[customer]);
                            ((Dictionary<string, string>)userData).Remove(customer);
                        }
                    }
                } catch { retval = null; } // Swallow the exception
                return retval;
            }
     
            private void Session_End(object sender, EventArgs e) {
                // Serialize data here (Pop)
     
                SPSecurity.RunWithElevatedPrivileges(new SPSecurity.CodeToRunElevated(delegate() {
                    WriteActivity(false);
                }));
     
            }
     
            private void Session_Start(object sender, EventArgs e) {
                // Push should be call from the App (UI)
     
                SPSecurity.RunWithElevatedPrivileges(new SPSecurity.CodeToRunElevated(delegate() {
                    WriteActivity(true);
                }));
            }
     
            private void WriteActivity(bool state) {
                string fileName = @"c:\log.txt";
     
                try {
                    using (StreamWriter logWriter = (!File.Exists(fileName) ? File.CreateText(fileName) : File.AppendText(fileName))) {
                        logWriter.WriteLine(string.Format("Session {0} at {1}", new object[] { (state ? "Started" : "Ended"), DateTime.Now }));
                        logWriter.Flush();
                        logWriter.Close();
                    }
                } catch { } // Swallow exception
            }
        }
    }

This approach should work, it does as a matter of fact, when Session_Start is triggered but it doesn’t fire when the session expires and in my humble opinion this is quite annoying because  SharePoint is taking away from me something that ASP.NET provides, this is valid when security relies on Windows and “a valid ticket” is always available, but what about form based authentication?

In conclusion, I can do ASP.NET with assembler language (according to the previously mentioned article) but I can’t intercept any event related to a  session timing out in SharePoint Sad  How cool is that!!!!

Regards,

Angel

Lenguaje Ensamblador en .NET y algunas otras ideas

Hola Comunidad,

El siguiente post es sobre algo no tan nuevo en .NET que leí hace un tiempo atrás, pero para ese entonces no tenía blog Stick out tongue y como actualmente estoy leyendo de nuevo este libro "El arte del lenguaje ensamblador" (creo que por tercera vez, porque contiene demasiada información) pensé que sería interesante mencionarlo. Aunque .NET es código gestionado al final de cuentas lo que entiende el procesador es lenguaje de máquina que puede representarse simbólicamente a traves del lenguaje Ensamblador, lo mismo pasa con los demás lenguajes (gestionados y no gestionados). En el año 2001 ha alguien se le ocurrió la idea de hacer ASP.NET usando lenguaje ensamblador.  El artículo completo lo pueden ver acá y abajo pueden ver un ejemplo de como luce el código de la página

Lo poco que aprendí sobre lenguaje Ensamblador fue por allá en el año 1995, gracias a mi amiga Virginia que ya estaba por graduarse de Ingeniero de Sistemas y yo estaba recién entrando en universidad. Me la pasaba horas haciendo cosas con Microsoft C/C++ 7.0 (que traía consigo MFC 1.0) por lo que me parecía chévere poner código inline dentro del __asm al mismo tiempo estaba aprendiendo VB 4.0 y en los ratos libres jugando Heretic y MK II... Mientras mi mamá me peleaba para que fuera a comer Stick out tongue que buenos tiempos aquellos... de verdad que si... Una cosa que he tratado mantener con los años es eso de "hacer las cosas con pasión y aprender jugando" por eso creo que no me aburro de la computadora.

Cambiando de tema, en días pasados un cliente hizo un requerimiento, "quiero detectar cuando la sesión haya expirado y guardar el carrito de compras en CRM", claro... en ASP.NET puedo escribir algo en el Global.asax e interceptar el evento Session_End y es sencillo, pero esto es una solución basada en SharePoint que hace uso de autenticación basada en formas y como es bien sabido SharePoint viene a soportar este tipo de autenticación  a partir de la versión 2007, porque siempre ha utilizado NTLM y Kerberos para ello. En fin, se me ocurrieron un par de ideas las cuales menciono a continuación:

  • Toda aplicación basada en SharePoint es una instancia de SPHttpApplication que a la vez deriva de HttpApplication, al estar consciente de esto me dije "Nah... sencillo" con modificar el Global.asax y escribir código para el evento Session_End lo consigo... No funciona Sad entonces pensé en crear un proyecto y explicitamente derivar de SPHttpApplication  y tampoco funciona... Por lo que pensé en otra alternativa...

  • Crear un HttpModule que contiene un singleton y este a su vez mantiene una estructura que mediante una región crítica permite su actualización al “expirar alguna sesión”, el código es mostrado a continuación
  • using System;
    using System.ComponentModel;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Web;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.ApplicationRuntime;
    using System.IO;
    using Microsoft.SharePoint.Security;
    using System.Runtime;
    using System.Runtime.InteropServices;
    using System.Web.SessionState;
     
     
    namespace Demo {
        public class MyCustomApplication : IHttpModule {
            #region IHttpModule Members
            private SyncedInfo _synced = null;
            private GCHandle? _allocatedMem = null;
     
            private GCHandle? AllocatedMemory {
                get {
                    return _allocatedMem;
                }
                set {
                    _allocatedMem = value;
                }
            }
     
            private SyncedInfo Singleton {
                get {
                    return _synced;
                }
                set {
                    _synced = value;
                }
            }
     
            public void Dispose() {
                if (AllocatedMemory != null) {
                    AllocatedMemory.Value.Free();
                }
            }
     
            public void Init(HttpApplication context) {
                if (Singleton == null) {
                    Singleton = new SyncedInfo(context.Modules["Session"]);
                    AllocatedMemory = GCHandle.Alloc(Singleton);
                }
            }
            #endregion
        }
     
        [Serializable]
        public class SyncedInfo {
            protected SessionStateModule _sessionModule;
            protected HttpContext _currentContext = HttpContext.Current;
     
            public SyncedInfo(IHttpModule context) {
                _sessionModule = (SessionStateModule)context;
                _currentContext.Application["UserData"] = new Dictionary<string, string>();
                _sessionModule.Start += Session_Start;
                _sessionModule.End += Session_End;
            }
     
            public bool Push(string customer, string selectedData) {
                bool retval = false;
                object userData = _currentContext.Application["UserData"];
     
                try {
                    lock (userData) {
                        if (!((Dictionary<string, string>)userData).ContainsKey(customer)) {
                            ((Dictionary<string, string>)userData).Add(customer, selectedData);
                        } else {
                            ((Dictionary<string, string>)userData)[customer] = selectedData;
                        }
                        retval = true;
                    }
                } catch { } // Swallow the exception
                return retval;
            }
     
            public KeyValuePair<string, string>? Pop(string customer) {
                KeyValuePair<string, string>? retval = null;
                object userData = _currentContext.Application["UserData"];
     
                try {
                    lock (userData) {
                        if (((Dictionary<string, string>)userData).ContainsKey(customer)) {
                            retval = new KeyValuePair<string, string>(customer, ((Dictionary<string, string>)userData)[customer]);
                            ((Dictionary<string, string>)userData).Remove(customer);
                        }
                    }
                } catch { retval = null; } // Swallow the exception
                return retval;
            }
     
            private void Session_End(object sender, EventArgs e) {
                // Serialize data here (Pop)
     
                SPSecurity.RunWithElevatedPrivileges(new SPSecurity.CodeToRunElevated(delegate() {
                    WriteActivity(false);
                }));
     
            }
     
            private void Session_Start(object sender, EventArgs e) {
                // Push should be call from the App (UI)
     
                SPSecurity.RunWithElevatedPrivileges(new SPSecurity.CodeToRunElevated(delegate() {
                    WriteActivity(true);
                }));
            }
     
            private void WriteActivity(bool state) {
                string fileName = @"c:\log.txt";
     
                try {
                    using (StreamWriter logWriter = (!File.Exists(fileName) ? File.CreateText(fileName) : File.AppendText(fileName))) {
                        logWriter.WriteLine(string.Format("Session {0} at {1}", new object[] { (state ? "Started" : "Ended"), DateTime.Now }));
                        logWriter.Flush();
                        logWriter.Close();
                    }
                } catch { } // Swallow exception
            }
        }
    }

Esta solución debería funcionar, de hecho lo hace cuando Session_Start es disparado pero no cuando la sesión expira lo cual me molesta pues SharePoint me está quitando algo ofrece ASP.NET,  quizás esto no aplica cuando la seguridad está basada en Windows pero puede darse el caso con autenticación basada en formas sí.

Entonces es posible hacer ASP.NET con lenguaje Ensamblador (según el artículo antes mencionado) pero no puedo interceptar el cierre de una sesión en SharePoint Sad  Que les parece!!!!

Saludos,

Angel

Upcoming MSDN Webcast: A lap around Microsoft ASP.NET 4.0 and Microsoft Visual Studio 2010 / Próximo Webcast en MSDN: Una vuelta por Microsoft ASP.NET 4.0 y Microsoft Visual Studio 2010

Hi Community,

I'll be presenting the following MSDN Webcast (Saturday, 28th November 2009 @ 7:00 AM - Australia Time)

I look forward to seeing you there Wink

Regards,

Angel


Hola Comunidad,

Estaré presentando el siguiente MSDN Webcast (Viernes, 27 de Noviembre de 2009 (05:00 p.m. - Hora Colombia) - (05:30 p.m. - Hora Venezuela))

Espero verlos ese día Wink

Saludos,

Angel