Web skin : créez une interface aspnet pour votre client lourd dotnet sans iis. hébergement interne de webapp.

Description

Vous en rêviez : Un client lourd DotNet peut héberger une WebApp complète sans avoir à installer IIS sur chaque poste.

Imaginez. Souvent, en entreprise, le schéma classique consiste en une base de donnée hébergée à l'entreprise, et des applications dites 'clients lourds' installées chez les commerciaux ou les clients. Généralement ces 'clients lourds' sont développés à l'aide d'une base Access, et avec une interface utilisateur constituée de WinForms (si ce n'est parfois directement en VBA).

Problème : Les interfaces à base de WinForm sont généralement laides, condensées, complexes, et incompréhensible pour un néophyte bien plus habitué à naviguer dans des interfaces Web. Je vous propose ici LA solution : Créer une 'WebSkin' à votre affreux 'client lourd' pour le rendre plus digeste à vos commerciaux ou clients. Profitez de la puissance de ASP.Net pour réaliser une interface claire, basée sur les pages qui pourront être données à de vrais Web Designers.

Grand habitué de csharpfr.com, ayant souvent trouvé des sources intéréssantes pour mes applications, sur ce sujet que je savais possible, je n'ai malheureusement trouvé aucune source. J'ai donc décidé de retrousser mes manches et de m'y coller moi-même. Je vous livre le résultat d'une petite semaine de travail, largement aidé par le livre référence "Pratique de .Net 2 et C#" de Patrick Smacchia, que je ne saurais que trop conseiller à tous les développeurs C#.

Cet exemple est réalisé avec Visual C# 2008 Express, et Visual Web Developper 2008 Express.

Cette technique est extrêmement puissante et permet d'héberger simplement la totalité d'une WebApp ASP.Net sur chaque poste ou est installée le 'client lourd', accessible en localhost sans connexion internet. On obtient donc une application déconnectée de la base entreprise, mais consultable localement en mode déconnecté à travers une belle interface Web.

Il y a un BUG sous Windows Vista qui bloque le HttpListener pour des questions de sécurités. Il doit y avoir un bout de code à rajouter pour que ca fonctionne sous Vista, mais lequel ? J'ai chercher un peu sur le web mais je n'ai rien trouvé.

Je ne suis pas Web Developper (bien que je vais devoir m'y mettre) donc l'exemple de la WebApp fournie est très sommaire. Juste pour vérifier que la technique fonctionne. A ce propos le code qui ne semble pas marcher, et c'est logique puisqu'on se passe de IIS, est la redirection de la page à la fin d'un traitement :
this.Response.Redirect("Welcome.aspx");//BUG in Local Server ASPNET

Il faut donc feinter et inclure une fonction javascript dans la page retournée à la fin du traitement qui fait la redirection directement depuis le browser de l'utilisateur. Le code ci-dessus est alors remplacé par :
//use body.onLoad
HtmlGenericControl body = this.FindControl("body") as HtmlGenericControl;
body.Attributes["onLoad"] = "RedirectWelcome()"; //nom de la fonction javascript

Laquelle fonction javascript est écrite directement dans la page aspx :
<script language="javascript" type="text/javascript">
function RedirectWelcome() {
window.location = "Welcome.aspx";
}
</script>

Voilà l'astuce. Pour le reste tout semble fonctionner normalement (avec IE).

Bonne lecture, et bon tests...

MAJ V2 V3:

J'ai du intégrer cette exemple dans mon application, et pour cela j'ai du revoir tout le code de l'exemple. J'ai donc fais une V2 et une V3, dont le résultat est une dll bien pratique qui doit être insérée dans le dossier /bin/ de votre WebApp. Puis il suffit d'implémenter la classe 'ServerASPNET' qui encapsule toute la complexité du fonctionnement du server ASPNET local. On peut même instancier plusieur 'ServerASPNET', un pour chaque WebApp, et ainsi constituer une ferme de WebApp. A partir de la pourquoi pas redevelopper un serveur Web complet compatible ASPNET ?

En particulier il doit être possible de pallier le BUG de redirection à la fin d'un traitement (voir le code).

MAJ V4:

J'ai ajouté quelques fonctionnalités indispensables :
- Gestion des sous-repertoires de la WebApp (dont App_GlobalRessources)
- Gestion des fichiers Non-ASP (images, css, html)

Source / Exemple :


using System;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Web.Hosting;
using System.Windows.Forms;

namespace Peerocracy.PAPICS.Client.Beta0.Skin
{
    /// <summary>
    /// Class ServerASPNET that can start a thread for a Server ASPNET.
    /// This can process one WebApp.
    /// This can be closed to stop http listening.
    /// </summary>
    public class ServerASPNET : IDisposable
    {

        #region Properties

        /// <summary>
        /// Return true if the ServerASPNET is running.
        /// </summary>
        public bool IsStarted
        {
            get { return _isStarted; }
        }
        private bool _isStarted = false;

        /// <summary>
        /// The full path to the WebApp.
        /// The WebApp must have a /bin/ folder that contains this dll.
        /// </summary>
        public string WebAppPath
        {
            get { return _WebAppPath; }
            set
            {
                //check if not already started
                if (this.IsStarted)
                {
                    this.ConsoleWrite("Error: Cannot change Path of WebApp until server stopped.");
                    return;
                }
                //null value
                if (value == null || !new DirectoryInfo(value).Exists)
                {
                    this.ConsoleWrite("Error: the given WebAppPath folder not exist.");
                    return;
                }
                //affect
                _WebAppPath = value;
            }
        }
        private string _WebAppPath = null;

        /// <summary>
        /// The name of WebApp.
        /// Use to build the URI to listen : http://IptoListen:PortToListen/WebAppName/.
        /// </summary>
        public string WebAppName
        {
            get { return _WebAppName; }
            set
            {
                //check if not already started
                if (this.IsStarted)
                {
                    this.ConsoleWrite("Error: Cannot change Name of WebApp until server stopped.");
                    return;
                }
                //null value
                if (value == null || value.Length == 0)
                {
                    this.ConsoleWrite("Error: the given WebAppName is null.");
                    return;
                }
                //affect
                _WebAppName = value;
            }
        }
        private string _WebAppName = "WebAppName";

        /// <summary>
        /// This is the address IP to listen.
        /// Use to build the URI to listen : http://IptoListen:PortToListen/WebAppName/.
        /// Must be : 'localhost' or the public IP of this station.
        /// See GetIPHostStation().
        /// </summary>
        public string IPtoListen
        {
            get { return _IPtoListen; }
            set
            {
                //check if not already started
                if (this.IsStarted)
                {
                    this.ConsoleWrite("Error: Cannot change IP to listen until server stopped.");
                    return;
                }
                //null value
                if (value == null || value.Length == 0)
                    value = "localhost";
                //affect
                _IPtoListen = value;
            }
        }
        private string _IPtoListen = "localhost";

        /// <summary>
        /// This is the port of this station to listen http request.
        /// Use to build the URI to listen : http://IptoListen:PortToListen/WebAppName/.
        /// </summary>
        public int PortToListen
        {
            get { return _PortToListen; }
            set
            {
                //check if not already started
                if (this.IsStarted)
                {
                    this.ConsoleWrite("Error: Cannot change Port to listen until server stopped.");
                    return;
                }
                //null value
                if (value <= 0) return;
                //affect
                _PortToListen = value;
            }
        }
        private int _PortToListen = 8008;

        /// <summary>
        /// Define the URI to listen : http://IptoListen:PortToListen/WebAppName/.
        /// Set: fill the properties : _IPtoListen, _PortToListen, _WebAppName.
        /// </summary>
        public string URItoListen
        {
            get
            {
                return "http://" + _IPtoListen + ":" + _PortToListen + "/" + _WebAppName + "/";
            }
            set
            {
                //null value 
                if (value == null || !value.StartsWith("http://"))
                    return;
                //regex.Match
                Match matchURI = new Regex(@"^http://(?'IPtoListen'[^:/]*):(?'PortToListen'[^/]*)/(?'WebAppName'.*?)/$", RegexOptions.Compiled).Match(value); ;
                //affect properties
                this.IPtoListen = matchURI.Groups["IPtoListen]"].Value;
                this.PortToListen = Int32.Parse(matchURI.Groups["PortToListen"].Value);
                this.WebAppName = matchURI.Groups["WebAppName"].Value;
            }
        }

        /// <summary>
        /// The thread that contains the _HostProcessor_ASPNET.
        /// Init by fct StartThread_ServerASPNET().
        /// </summary>
        public Thread Thread_ServerASPNET
        {
            get { return _Thread_ServerASPNET; }
        }
        private Thread _Thread_ServerASPNET = null;

        /// <summary>
        /// Class of the host _HostProcessor_ASPNET.
        /// Init by fct StartThread_ServerASPNET().
        /// </summary>
        public HostProcessorASPNET HostProcessor_ASPNET
        {
            get { return _HostProcessor_ASPNET; }
        }
        private HostProcessorASPNET _HostProcessor_ASPNET = null;

        #endregion

        #region Ctors

        /// <summary>
        /// Ctor, nothing.
        /// </summary>
        public ServerASPNET()
        {
            _timerUpdateConsoleProcessor = new System.Windows.Forms.Timer();
            _timerUpdateConsoleProcessor.Interval = 1000; //1sec
            _timerUpdateConsoleProcessor.Tick += new EventHandler(this.timerUpdateConsoleProcessor_Tick);
        }

        /// <summary>
        /// Ctor with given uriToListen, and webAppPath.
        /// </summary>
        /// <param name="uriToListen">The URI to listen http requests.</param>
        /// <param name="webAppPath">The full path of the WebApp.</param>
        public ServerASPNET(string uriToListen, string webAppPath)
            : this()
        {
            //affect
            this.URItoListen = uriToListen;
            this.WebAppPath = webAppPath;

        }

        /// <summary>
        /// Ctor with given uriToListen, and webAppPath, and rtfConsole.
        /// </summary>
        /// <param name="uriToListen">The URI to listen http requests.</param>
        /// <param name="webAppPath">The full path of the WebApp.</param>
        /// <param name="rtfConsole">The RichTextBox control of the console.</param>
        public ServerASPNET(string uriToListen, string webAppPath, RichTextBox rtfConsole)
            : this(uriToListen, webAppPath)
        {
            //affect
            this.ConsoleRtf = rtfConsole;
        }

        #endregion

        #region Functions

        /// <summary>
        /// Return the list of the address IP of this Host Station.
        /// </summary>
        /// <returns>One string for each IP.</returns>
        public string[] GetIPHostStation()
        {
            //get IPs
            IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
            //make new string[]
            string[] IPs = new string[ips.Length];
            //fill new string[]
            for (int i = 0; i < ips.Length; i++)
                IPs[i] = ips[i].ToString();
            //return
            return IPs;

        }

        /// <summary>
        /// Start the ServerASPNET.
        /// Start a new background thread for the function fctRunThread_ServerASPNET.
        /// </summary>
        public void StartThread_ServerASPNET()
        {
            //check if not closed
            if (_isDisposed) return;
            //check if not already started
            if (this.IsStarted)
            {
                this.ConsoleWrite("Local Server ASPNET already started !");
                return;
            }
            //check if given WebApp path
            if (_WebAppPath == null || !new DirectoryInfo(_WebAppPath).Exists)
                throw new Exception("Error: Cannot start until given the path of WebAppPath property.");
            //check if given WebApp name
            if (_WebAppName == null || _WebAppName.Length == 0)
                throw new Exception("Error: Cannot start until given the name of WebAppName property.");

            //make new background thread
            _Thread_ServerASPNET = new Thread(this.RunThread_ServerASPNET);
            _Thread_ServerASPNET.IsBackground = true;
            //start thread
            _Thread_ServerASPNET.Start();
            //wait for started
            while (!this.IsStarted)
            {
                Application.DoEvents();
                Thread.Sleep(10);
            }
            //console
            if (this.ConsoleRtf != null)
                _strConsoleBeforeConsoleProcessor = this.ConsoleRtf.Text;
            _timerUpdateConsoleProcessor.Start();

        }

        /// <summary>
        /// Stop the ServerASPNET.
        /// Stop the thread of the ServerASPNET.
        /// </summary>
        public void StopThread_ServerASPNET()
        {
            //check if not closed
            if (_isDisposed) return;
            //check if server already stopped
            if (!this.IsStarted)
            {
                this.ConsoleWrite("Local Server ASPNET already stopped !");
                return;
            }
            try
            {
                //stop listening
                _HostProcessor_ASPNET.StopHttpListening();
                //_HostProcessor_ASPNET = null;
            }
            catch (Exception err)
            { }
            //abort thread
            _Thread_ServerASPNET.Abort();
            //_Thread_ServerASPNET = null;
            _isStarted = false;
            //console
            _timerUpdateConsoleProcessor.Stop();
            this.UpdateConsoleFromProcessor();
            this.ConsoleWrite("Stop: Server Aborted !");

        }

        /// <summary>
        /// Run the ServerASPNET.
        /// Call by fct StartThread_ServerASPNET();
        /// Exec in new thread.
        /// </summary>
        private void RunThread_ServerASPNET()
        {

            //check if not closed
            if (_isDisposed) return;

            //console
            //this.ConsoleClean();
            this.ConsoleWrite("Start new Server ASPNET threaded on this station,");
            this.ConsoleWrite("  for URI to listen : '" + this.URItoListen + "'.");
            this.ConsoleWrite("  for WebApp : '" + _WebAppPath + "'.");

            //search the dll containing the class 'HostProcessorASPNET'
            //search in '_WebAppPath/bin/*.dll|exe'
            _HostProcessor_ASPNET =
                (HostProcessorASPNET)ApplicationHost.CreateApplicationHost(
                 typeof(HostProcessorASPNET), @"/", _WebAppPath); //BUG if not found this dll in '_WebAppPath/bin'

            ////subscribe on events
            ////BUG: because different 'Domain Application'.
            //_HostProcessor_ASPNET.OnConsoleChanged +=
            //    new HostProcessorASPNET.ConsoleChangedEventHandler(_HostProcessor_ASPNET_OnConsoleChanged);
            //_HostProcessor_ASPNET.OnIsStartedChanged +=
            //    new HostProcessorASPNET.IsStartedChangedEventHandler(_HostProcessor_ASPNET_OnIsStartedChanged);

            //start http listening : lost control
            this.ConsoleWrite("OK: Server ASPNET is started.");
            //_strConsoleBeforeConsoleProcessor = this.ConsoleRtf.Text;
            _isStarted = true;
            _HostProcessor_ASPNET.StartHttpListening(this.URItoListen, this.WebAppPath);

            //never reach
            return;

        }

        //void _HostProcessor_ASPNET_OnIsStartedChanged(object sender, EventArgs args)
        //{
        //    _isStarted = _HostProcessor_ASPNET.IsStarted;
        //}

        //void _HostProcessor_ASPNET_OnConsoleChanged(object sender, ConsoleChangedEventArgs args)
        //{
        //    this.ConsoleWrite(args.NewLineText);
        //}

        /// <summary>
        /// Start a new IE browser on the default page of the running WebApp.
        /// </summary>
        public void StartBrowserOnWebApp()
        {
            this.StartBrowserOnWebApp(null, true);
        }
        
        /// <summary>
        /// Start a new browser on the given page of the running WebApp.
        /// </summary>
        /// <param name="page">The name of the page to show.</param>
        /// <param name="isIEBrowser">True for open IE browser, or false for default browser.</param>
        public void StartBrowserOnWebApp(string page, bool bIEBrowser)
        {
            //check if closed
            if (_isDisposed) return;
            //check if started
            if (!this.IsStarted) return;
            //null value
            if (page == null) page = "";
            //search page
            if (page.StartsWith("/"))
                page = page.Substring(1);
            if (!page.StartsWith("http://"))
                page = this.URItoListen + page;

            //if start in IE
            if (bIEBrowser)
            {
                //show in new Process: IE
                string iexplore = Path.Combine(Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"),
                    "Internet Explorer"), "iexplore.exe");
                System.Diagnostics.Process.Start(iexplore, page);
                return;
            }

            //show in new process: DefaultBrowser
            System.Diagnostics.Process.Start(page);
            return;

        }

        #endregion

        #region Console Membres

        /// <summary>
        /// Internal timer to update the console from Processor.
        /// </summary>
        private System.Windows.Forms.Timer _timerUpdateConsoleProcessor;

        /// <summary>
        /// Update the console from the Processor.
        /// </summary>
        private void timerUpdateConsoleProcessor_Tick(object sender, EventArgs e)
        {
            if (!_isStarted) return;

            this.UpdateConsoleFromProcessor();
        }

        /// <summary>
        /// Update the console from the Processor.
        /// </summary>
        private void UpdateConsoleFromProcessor()
        {
            if (_isDisposed) return;
            //null value
            if (_rtfConsole == null) return;
            //check if invoke is required
            if (_rtfConsole.InvokeRequired)
            {
                //not same thread: invoke
                dlgUpdateConsoleFromProcessor Delegate = new dlgUpdateConsoleFromProcessor(this.UpdateConsoleFromProcessor);
                _rtfConsole.Invoke(Delegate);
            }
            //test if differents
            else if (_rtfConsole.Text != _strConsoleBeforeConsoleProcessor + _HostProcessor_ASPNET.Console)
            {
                //same thread: affect
                _rtfConsole.Text = _strConsoleBeforeConsoleProcessor + _HostProcessor_ASPNET.Console;
            }
        }
        /// <summary>
        /// Delegate to synchronous threads on function UpdateConsoleFromProcessor().
        /// </summary>
        private delegate void dlgUpdateConsoleFromProcessor();

        /// <summary>
        /// Define the RichTextBox console of the server.
        /// </summary>
        public RichTextBox ConsoleRtf
        {
            get { return _rtfConsole; }
            set
            {
                _rtfConsole = value;
            }
        }
        private RichTextBox _rtfConsole = null;
        /// <summary>
        /// This string contains the console state before the consoleProcessor.  
        /// </summary>
        private string _strConsoleBeforeConsoleProcessor = "";

        /// <summary>
        /// fctCleanConsole: ThreadSafe: Clean the text of the rtfConsole control.
        /// </summary>
        public void ConsoleClean()
        {
            if (_isDisposed) return;
            //null value
            if (_rtfConsole == null) return;
            //check if invoke is required
            if (_rtfConsole.InvokeRequired)
            {
                //not same thread: invoke
                dlgConsoleClean Delegate = new dlgConsoleClean(this.ConsoleClean);
                _rtfConsole.Invoke(Delegate);
            }
            else
            {
                //same thread: affect
                _rtfConsole.Text = "";
            }
        }
        /// <summary>
        /// Delegate to synchronous threads on function ConsoleClean().
        /// </summary>
        private delegate void dlgConsoleClean();

        /// <summary>
        /// fctWriteConsole: ThreadSafe: Write given text to rtfConsole control.
        /// </summary>
        /// <param name="text">Text line to write in console control.</param>
        public void ConsoleWrite(string text)
        {
            if (_isDisposed) return;
            //null value
            if (_rtfConsole == null) return;
            if (text == null) text = "";
            //show in rtfConsole
            if (_rtfConsole.InvokeRequired)
            {
                //not same thread: invoke
                dlgConsoleWrite Delegate = new dlgConsoleWrite(this.ConsoleWrite);
                _rtfConsole.Invoke(Delegate, new object[] { text });
            }
            else
            {
                //new line char
                if (!text.EndsWith("\n"))
                    text += "\n";
                //same thread: affect
                _rtfConsole.Text += text;
            }
        }
        /// <summary>
        /// Delegate to synchronous threads on function ConsoleWrite().
        /// </summary>
        private delegate void dlgConsoleWrite(string text);

        #endregion

        #region IDisposable Membres

        /// <summary>
        /// True if this is disposed or closed.
        /// </summary>
        private bool _isDisposed = false;

        /// <summary>
        /// Idem as Dispose().
        /// </summary>
        public void Close()
        {
            this.Dispose();
        }

        /// <summary>
        /// Close the http listening properly and abort the thread.
        /// </summary>
        public void Dispose()
        {
            //check if already disposed
            if (_isDisposed) return;
            //dispose with managed ressources
            this.Dispose(true);
            //GC dont call finalizer of this
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Finalizer: dispose but managed ressources.
        /// </summary>
        ~ServerASPNET()
        {
            this.Dispose(false);
        }

        /// <summary>
        /// Internal: Dispose.
        /// </summary>
        /// <param name="isDisposeManagedRessources"></param>
        protected virtual void Dispose(bool isDisposeManagedRessources)
        {
            //check if already disposed
            if (_isDisposed) return;
            //set disposed
            _isDisposed = true;

            //dispose unmanaged ressources
            this.StopThread_ServerASPNET();

            if (isDisposeManagedRessources)
            {
                //dispose managed ressources
                _HostProcessor_ASPNET = null;
                _Thread_ServerASPNET = null;
                _rtfConsole = null;
            }
        }

        #endregion

    }//class

    /// <summary>
    /// This class treat each http request from browser in function of given URI to listen.
    /// </summary>
    public class HostProcessorASPNET : MarshalByRefObject, IDisposable
    {

        #region Properties

        /// <summary>
        /// Return true if the processor is started and listening http requests.
        /// </summary>
        public bool IsStarted
        {
            get { return _IsStarted; }
        }
        private bool _IsStarted = false;
        /// <summary>
        /// Event on IsStarted changed.
        /// BUG: cannot subscribe bacause different 'Application Domain'.
        /// </summary>
        public event IsStartedChangedEventHandler OnIsStartedChanged;
        public delegate void IsStartedChangedEventHandler(object sender, EventArgs args);

        /// <summary>
        /// Return the URI to listen http requests.
        /// Format : http://IptoListen:PortToListen/WebAppName/.
        /// </summary>
        public string URItoListen
        {
            get { return _URItoListen; }
            //set ?
        }
        private string _URItoListen = null;

        /// <summary>
        /// Memorize the WebApp name.
        /// </summary>
        private string _WebAppName = null;

        /// <summary>
        /// Internal path to the WebApp directory.
        /// </summary>
        private string _PathWebApp = null;

        /// <summary>
        /// Internal HttpListener.
        /// </summary>
        private HttpListener _HttpListener = null;

        /// <summary>
        /// Internal Regex to find no ASP files.
        /// </summary>
        private Regex _rgxNoAspFile;

        #endregion

        #region Functions

        /// <summary>
        /// Stop the HttpListener properly. Set to null.
        /// </summary>
        public void StopHttpListening()
        {
            //check if closed
            if (_isDisposed) return;
            //check if started
            if (!_IsStarted) return;
            //stop HttpListener
            _HttpListener.Stop();
            _HttpListener.Abort();
            _HttpListener = null;
            //isStarted
            _IsStarted = true;
            this.ConsoleWrite("");
            this.ConsoleWrite("Stop: HttpListening aborted !");

        }

        /// <summary>
        /// Start the http listening on the given uriToListen.
        /// </summary>
        /// <param name="uriToListen">The URI to listen : http://IptoListen:PortToListen/WebAppName/.</param>
        public void StartHttpListening(string uriToListen, string pathWebApp)
        {
            //check if closed
            if (_isDisposed) return;

            this.ConsoleClean();

            //check if started
            if (_IsStarted)
            {
                this.ConsoleWrite("Error: HostProcessorASPNET already started.");
                return;
            }

            //check WebApp path
            if (!new DirectoryInfo(pathWebApp).Exists)
            {
                this.ConsoleWrite("Error: Cannot find the WebApp path : '" + pathWebApp + "'.");
                return;
            }
            //affect
            _PathWebApp = pathWebApp;

            //build _rgxNoAspFile //TODO
            _rgxNoAspFile = new Regex(@"\.(gif|jpe?g?|png|tiff?|css|html?)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

            //check uriToListen
            if (uriToListen == null || !uriToListen.StartsWith("http://"))
            {
                this.ConsoleWrite("Error: URI to listen dont start with 'http://'.");
                return;
            }
            _URItoListen = uriToListen;

            //get _WebAppName
            _WebAppName = new Regex(@"^https?://[^:/]*:?[^/]*/(?'WebAppName'.*?)/$")
                .Match(_URItoListen).Groups["WebAppName"].Value;

            //create new HttpListener
            _HttpListener = new HttpListener();
            //add uri to listen.
            _HttpListener.Prefixes.Add(_URItoListen);
            try
            {
                //start the listener
                _HttpListener.Start();
            }
            catch (HttpListenerException err)
            {
                //TODO find exception on Vista
                if (err.ErrorCode == 404) //HACK find exception on Vista
                    //error on windows vista
                    this.ConsoleWrite("Error Cause Windows Vista : Cant start the 'HttpListener' class.");
                else
                    //error on windows xp
                    this.ConsoleWrite("Error: Cant start the 'HttpListener' class.");
                this.ConsoleWrite("WinErr: " + err.ToString());
                return;
            }
            //show started
            this.ConsoleWrite("OK: 'HttpListener' Started and now listen http requests.");
            _IsStarted = true;
            if (this.OnIsStartedChanged != null)
                this.OnIsStartedChanged(this, new EventArgs());

            //start the global loop to treat each request from browser
            this.RunHttpListening();
            //never reach

        }

        /// <summary>
        /// Internal: global loop to treat each request from browser.
        /// </summary>
        private void RunHttpListening()
        {
            //start the global loop to treat each request from browser
            while (true) //to quit: _HttpListener.Abort()
            {
                //get httpContext from _HttpListener
                HttpListenerContext httpContext;
                try
                {
                    //wait for http request from browser : lost constrol
                    this.ConsoleWrite("");
                    this.ConsoleWrite("  'HttpListener' wait for http request from browser ...");
                    Application.DoEvents();
                    httpContext = _HttpListener.GetContext(); //to quit: _HttpListener.Abort()
                    Application.DoEvents();

                }
                catch (HttpListenerException err)
                {
                    //if aborted
                    this.ConsoleWrite("  'HttpListener' waiting aborted !");
                    this.ConsoleWrite("  Err: " + err.ToString());
                    return;
                }

                //get name of the page to show without '/WebAppName/'
                string page = httpContext.Request.Url.LocalPath.Substring(1);
                //delete '/WebAppName/'
                if (page.StartsWith(_WebAppName))
                    page = page.Substring(_WebAppName.Length + 1);
                //set default page if null
                if (page.Length == 0)
                    page = "default.aspx";

                //get query of the request
                string query = httpContext.Request.Url.Query;
                //add POSTed data in the body of request
                if (httpContext.Request.HasEntityBody)
                {
                    //read the content of the 'HTML form inputs' as a string
                    Stream streamFormInputs = httpContext.Request.InputStream;
                    StreamReader readerFormInputs = new StreamReader(streamFormInputs, httpContext.Request.ContentEncoding);
                    string sFormInputs = readerFormInputs.ReadToEnd();
                    readerFormInputs.Close();
                    streamFormInputs.Close();
                    //add to query
                    query += (query.Length != 0 ? "&" : "") + sFormInputs;
                }

                //show in console
                string text = "/" + _WebAppName + "/" + page + (query.Length != 0 ? "?" + query : "");
                this.ConsoleWrite("    Http Request received : '" + text + "'");

                //get file relativ path
                string file = page.Replace('/', '\\');

                //treat App_GlobalResources
                if (file.Contains("App_GlobalResources"))
                    file = file.Substring(file.IndexOf("App_GlobalResources"));

                //treat no ASP files
                if (_rgxNoAspFile.IsMatch(file))
                {
                    //get full path file
                    string pathFile = Path.Combine(_PathWebApp, file);
                    //check if exist
                    if (!new FileInfo(pathFile).Exists)
                    {
                        //if no exist : close response
                        httpContext.Response.Close();
                        continue;
                    }
                    //open a FileStream
                    FileStream streamFile;
                    try
                    {
                        streamFile = new FileStream(pathFile, FileMode.Open);
                    }
                    catch (Exception err)
                    {
                        this.ConsoleWrite("Error: Opening FileStream to file : '" + pathFile + "'.");
                        continue;
                    }
                    //get streamResponse
                    Stream streamResponse = httpContext.Response.OutputStream;
                    //make new buffer
                    int bufferLength = 512;
                    Byte[] buffer = new Byte[bufferLength];
                    //read the file
                    int bytesRead = streamFile.Read(buffer, 0, bufferLength);
                    while (bytesRead > 0)
                    {
                        //write in the response
                        streamResponse.Write(buffer, 0, bytesRead);
                        //read the file
                        bytesRead = streamFile.Read(buffer, 0, bufferLength);
                    }
                    //close the streams
                    streamFile.Close();
                    httpContext.Response.OutputStream.Flush();
                    httpContext.Response.Close();
                    continue;
                }

                //new writer unicode
                StreamWriter writerResponse = new StreamWriter(httpContext.Response.OutputStream,
                    System.Text.Encoding.Unicode);

                //call ProcessRequest
                this.ProcessRequest(file, query, writerResponse);

                //close the response
                try
                {
                    writerResponse.Flush();
                    writerResponse.Close();
                    httpContext.Response.Close();
                }
                catch (Exception err)
                {
                }

            }//while
            //never reach
        }

        /// <summary>
        /// Process to the full ASPNET response from the given fields.
        /// </summary>
        /// <param name="fileName">The name of the file to process.</param>
        /// <param name="query">The full query of the http request.</param>
        /// <param name="writerResponse">The writer of the response.</param>
        public void ProcessRequest(string fileName, string query, TextWriter writerResponse)
        {
            //check if not closed
            if (_isDisposed) return;
            //check if started
            if (!_IsStarted) return;

            //create new aspnetWorker
            SimpleWorkerRequest aspnetWorker = new SimpleWorkerRequest(fileName, query, writerResponse);
            //process to the full ASPNET response 
            HttpRuntime.ProcessRequest(aspnetWorker);

            //TODO chercher des fonctionnalités avec F1
            //peut-être est-il possible d'implementer une fonctionnalité de redirection ? 

            //if (aspnetWorker.IsClientConnected)
            //{
            //    string mapPath = aspnetWorker.MapPath();
            //    aspnetWorker.
            //}

            //send the response to client
            aspnetWorker.FlushResponse(true);

        }

        #endregion

        #region Console

        /// <summary>
        /// Return the text of the console.
        /// </summary>
        public string Console
        {
            get { return _strConsole; }
        }
        private string _strConsole = null;
        /// <summary>
        /// Event on Console text changed.
        /// BUG: because different 'Application Domain'.
        /// </summary>
        public event ConsoleChangedEventHandler OnConsoleChanged;
        public delegate void ConsoleChangedEventHandler(object sender, ConsoleChangedEventArgs args);

        /// <summary>
        /// Clean the text of the console.
        /// Call by a new HttpListening.
        /// </summary>
        private void ConsoleClean()
        {
            //affect
            _strConsole = "";
            if (this.OnConsoleChanged != null)
                this.OnConsoleChanged(this, new ConsoleChangedEventArgs(null));
        }

        /// <summary>
        /// Write given line to the console.
        /// </summary>
        /// <param name="text">Text line to write in console control.</param>
        private void ConsoleWrite(string text)
        {
            //null value
            if (text == null) text = "";
            //new line char
            if (!text.EndsWith("\n"))
                text += "\n";
            //affect
            _strConsole += text;
            if (this.OnConsoleChanged != null)
                this.OnConsoleChanged(this, new ConsoleChangedEventArgs(text));

        }

        #endregion

        #region IDisposable Membres

        /// <summary>
        /// True if this is disposed/closed, and cannot function.
        /// </summary>
        private bool _isDisposed = false;

        /// <summary>
        /// Idem that Dispose();
        /// </summary>
        public void Close()
        {
            this.Dispose();
        }

        /// <summary>
        /// Stop the _HttpListener properly. _HttpListener = null;
        /// </summary>
        public void Dispose()
        {
            //check if already disposed
            if (_isDisposed) return;
            //dispose with managed ressources
            this.Dispose(true);
            //Garbage Collector dont call finalizer of this
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Finalizer: dispose but managed ressources.
        /// </summary>
        ~HostProcessorASPNET()
        {
            this.Dispose(false);
        }

        /// <summary>
        /// Internal: Stop the HttpListener properly.
        /// </summary>
        /// <param name="isDisposeManagedRessources">Internal: true to annul HttpListener.</param>
        protected virtual void Dispose(bool isDisposeManagedRessources)
        {
            //check if already disposed
            if (_isDisposed) return;
            //set disposed
            _isDisposed = true;

            //dispose unmanaged ressources
            this.StopHttpListening();

            if (isDisposeManagedRessources)
            {
                //dispose managed ressources
                _HttpListener = null;
            }
        }

        #endregion

    }//class HostProcessorASPNET

    /// <summary>
    /// The EventArg for the event OnConsoleChanged.
    /// </summary>
    public class ConsoleChangedEventArgs : EventArgs
    {
        /// <summary>
        /// The new line added to the console.
        /// Is null if console is cleaned.
        /// </summary>
        public string NewLineText
        {
            get { return _NewLineText; }
        }
        private string _NewLineText = null;

        /// <summary>
        /// Ctor, init the EventArg with given newLineText.
        /// </summary>
        /// <param name="newLineText">The new line added to the console.</param>
        public ConsoleChangedEventArgs(string newLineText)
        {
            //affect
            _NewLineText = newLineText;
        }

    }//class ConsoleChangedEventArgs

}//namespace

Conclusion :


Vous pouvez faire un copier/coller d'une de vos WebApp ASPNET 2.0 dans cet exemple et tester si ca fonctionne.
Normalement tout fonctionne sauf les redirections en fin de traitement PostBack (auquel je m'attaquerais surement dans les semaines à venir...).

Faites moi un commentaire si vous détecter un BUG.

Si quelqu'un réussit à améliorer le code qu'il n'hésite pas à m'en faire part ou à poster sa nouvelle source sur le site.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.