Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

An IIS observer service to check the availabilty of the Intenet Information Server

3.44/5 (4 votes)
19 Jan 2009CPOL 22.7K   63  
This IIS observer service checks the availabilty of the Intenet Information Server.

Introduction

This article describes how to observe the IIS. My Microsoft home server is connected to the Internet via the DynDNS server from Microsoft. When IIS hangs-up or becomes offline because of an internal error, then can you not re-connect it easily. That is not funny. That is why I have written an IIS observer.

The observer checks whether the domain (by DynDNS) is available and restarts IIS if it is necessary. The observer is a Windows Service application. The home server website URL, the restart application, parameters, and the interval are configured in the app.config.

Background

The application is a Windows Service, and works with local system rights.

C#
static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] { new ObserverService() };
        ServiceBase.Run(ServicesToRun);
    }
}
/// <summary>
/// The observe inteface with the base information.
/// </summary>
public interface IObserve
{
    /// <summary>
    /// Gets the server URL.
    /// </summary>
    /// <value>The server URL.</value>
    string ServerUrl { get; }
    /// <summary>
    /// Gets the response timeout.
    /// </summary>
    /// <value>The response timeout.</value>
    int RequestTimeout { get; }
    /// <summary>
    /// Gets the check interval.
    /// </summary>
    /// <value>The check interval.</value>
    int CheckInterval { get; }
    /// <summary>
    /// Gets the restart application.
    /// </summary>
    /// <value>The restart application.</value>
    string RestartApp { get; }
    /// <summary>
    /// Gets the restart parameter.
    /// </summary>
    /// <value>The restart parameter.</value>
    string RestartParameter { get; }
}
    
/// <summary>
/// The class contains the 
/// </summary>
[Serializable]
public class ServerToObserve : IObserve
{
    /// <summary>
    /// Gets the response timeout.
    /// </summary>
    /// <value>The response timeout.</value>
    public int RequestTimeout
    {
        get
        {
#if RELEASE
            string value = ConfigurationManager.AppSettings["ResponseTimeout"];
            int timeout;
            if (string.IsNullOrEmpty(value) || !int.TryParse(value, out timeout))
                timeout = 60000; // 1 minute = 1000 * 60 sec.
            return timeout;
#else
            return 20000;
#endif
        }
    }
    public int CheckInterval
    {
        get
        {
#if RELEASE
            string value = ConfigurationManager.AppSettings["CheckInterval"];
            int timeout;
            if (string.IsNullOrEmpty(value) || !int.TryParse(value, out timeout))
                timeout = 1800000; // 30 minutes = 1000 * 60 sec. * 30 min.
            return timeout;
#else
            return 60000;
#endif
        }
    }
    /// <summary>
    /// Gets the server URL.
    /// </summary>
    /// <value>The server URL.</value>
    public string ServerUrl
    {
        get
        {
            string value = ConfigurationManager.AppSettings["ServerUrl"];
            if (string.IsNullOrEmpty(value))
                throw new SettingsPropertyNotFoundException(
                "ServerUrl setting not found");
            else if(value.IndexOf("://") == -1)
                throw new FormatException(
                    "Invalid format in the ServerUrl setting value. The tag must" +
                    "be contains the prefix http:// or ftp://.");
            return value;
        }
    }
    /// <summary>
    /// Gets the restart application.
    /// </summary>
    /// <value>The restart application.</value>
    public string RestartApp
    {
        get
        {
            return ConfigurationManager.AppSettings["RestartApp"];
        }
    }
    /// <summary>
    /// Gets the restart parameter.
    /// </summary>
    /// <value>The restart parameter.</value>
    public string RestartParameter
    {
        get
        {
            return ConfigurationManager.AppSettings["RestartParameter"];
        }
    }
}
public partial class ObserverService : ServiceBase
{
    Checker _iisObserver;
    public ObserverService()
    {
        InitializeComponent();
    }
    protected override void OnStart(string[] args)
    {
        _iisObserver = new Checker();
        _iisObserver.Start(new ServerToObserve());
    }
    protected override void OnStop()
    {
        _iisObserver.Stop();
    }
}
/// <summary>
/// This class contains the observer and restart engine.
/// </summary>
[Serializable]
public class Checker
{
    private Timer _checkTimer;
    private string _eventLogSource;
    #region initialize
    /// <summary>
    /// Initializes a new instance of the <see cref="Checker"/> class.
    /// </summary>
    public Checker()
    {
        _eventLogSource = Assembly.GetAssembly(GetType()).GetName().Name;
        if (EventLog.Exists(_eventLogSource))
            EventLog.CreateEventSource(_eventLogSource, "");
    }
    #endregion
    #region public methods
    /// <summary>
    /// Starts the specified observ.
    /// </summary>
    /// <param name="observe">The <see cref="IObserve"/> object.</param>
    public void Start(IObserve observe)
    {
        try
        {
            if (observe == null)
                throw new ArgumentNullException("observe");
            _checkTimer = new Timer(CheckTimerCallback, observe,
                observe.CheckInterval, observe.CheckInterval);
        }
        catch (Exception ex)
        {
            EventLog.WriteEntry(_eventLogSource, string.Format(
                "Start: {0}\r\n{1}\r\n{2}", ex.Message, ex.InnerException,
                ex.StackTrace), EventLogEntryType.Warning);
        }
    }
    /// <summary>
    /// Stops this instance.
    /// </summary>
    public void Stop()
    {
        try
        {
            _checkTimer.Dispose();
        }
        catch (Exception ex)
        {
            EventLog.WriteEntry(_eventLogSource, string.Format(
                "Stop: {0}\r\n{1}\r\n{2}", ex.Message, ex.InnerException, 
                ex.StackTrace), EventLogEntryType.Warning);
        }
    }
    #endregion
    #region non public methods
    private void CheckTimerCallback(object objectState)
    {
        try
        {
            IObserve observe = (IObserve)objectState;
            WebRequest request = CreateRequest(observe);
            request.BeginGetResponse(ResponseCallback, new ObserveHelper(observe,
                request));
        }
        catch (Exception ex)
        {
            EventLog.WriteEntry(_eventLogSource, string.Format(
                "CheckTimerCallback: {0}\r\n{1}\r\n{2}", ex.Message,
                ex.InnerException, ex.StackTrace), EventLogEntryType.Warning);
        }
    }
    private void ResponseCallback(IAsyncResult asyncResult)
    {
        ObserveHelper helper = null;
        try
        {
            helper = (ObserveHelper)asyncResult.AsyncState;
            HttpWebResponse response =
                helper.Request.EndGetResponse(asyncResult) as HttpWebResponse;
            if (response == null || (int)response.StatusCode >= 400)
                RestartISS(helper);

        }
        catch (Exception ex)
        {
            EventLog.WriteEntry(_eventLogSource, string.Format(
                "ResponseCallback: {0}\r\n{1}\r\n{2}", ex.Message,
                ex.InnerException, ex.StackTrace), EventLogEntryType.Warning);
            RestartISS(helper);
        }
    }
    #region CreateRequest
    /// <summary>
    /// Creates a <see cref="HttpWebRequest"/>
    /// </summary>
    /// <param name="observe">A <see cref="IObserve"/> object</param>
    private static HttpWebRequest CreateRequest(IObserve observe)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.CreateDefault(
            new Uri(observe.ServerUrl));
        request.KeepAlive = false;
        request.Timeout = observe.RequestTimeout;
        request.AllowAutoRedirect = false;
        request.ContentType = "text/html";
        request.Method = "GET";
        request.ProtocolVersion = HttpVersion.Version11;
        request.UserAgent = string.Format(
            "Mozilla/5.0 (compatible; Server hangup checker {0}; {1} {2} {3}; {4})",
            Environment.Version, Environment.OSVersion.Platform,
            Environment.OSVersion.ServicePack, Environment.OSVersion.VersionString,
           System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
        return request;
    }
    #endregion
    private void RestartISS(ObserveHelper helper)
    {
        if (helper == null)
        {
            EventLog.WriteEntry(_eventLogSource, "IIS Restart Helper is null",
                EventLogEntryType.Error);
            return;
        }
        EventLog.WriteEntry(_eventLogSource, "Restart IIS start",
            EventLogEntryType.Information);
        /*
        Übersicht von Iisreset.exe
        Iisreset.exe verwendet die folgende Syntax: 
        iisreset[ computername] 
        Hinweis: Elemente in [] sind optional.
        Sie möchten andere Funktionen durchführen kann, während das iisreset
        ohne Argumente ausführen. Sie können die folgenden Parameter mit
        Iisreset.exe verwenden: 
        Computername: verwendet diesen Parameter, um den Computer anzugeben, 
        den Sie verwalten sollen. Wenn Sie diesen Parameter weglassen, wird der
        lokale Computer angegeben. 
        /restart: verwendet diesen Parameter zu dem Beenden und neu zu dem
        /Starten von allen ausgeführten Internetdiensten. 
        /start: verwendet diesen Parameter, um alle Internetdienste zu starten,
        /die deaktiviert werden.
        /stop: verwendet diesen Parameter zu dem Beenden von allen
        /ausgeführten Internetdiensten.
        /reboot: verwendet diesen Parameter, um den Computer erneut zu starten.
        / Rebootonerror: um den Computer erneut zu starten, wenn ein Fehler
        /auftritt, nachdem die Internetdienste gestartet, die Internetdienste
        /zu beenden oder neu gestartet, versuchen, verwenden Sie diesen Parameter. 
        / Noforce: diesen Parameter verwenden Sie, damit die Internetdienste
        / nicht erzwingen heruntergefahren werden, wenn Sie die Dienste 
        / nicht ordnungsgemäß beenden können. 
        / Timeout: um die Zeit anzugeben, der der Computer wartet auf die
        / Internetdienste beenden, Verwendet diesen Parameter Werts
        / (wobei ist Wert ein Zeitwert in Sekunden). Wenn Sie das <B> verwenden,
        / startet es neu, nachdem der Computer angehalten wird/ 
        / Rebootonerror-</B>-Parameter. Der Standardwert wird in der folgenden
        / Liste beschrieben: 
        Der Standardwert ist 20 Sekunden, wenn Sie diesen Parameter mit /restart
        verwenden.
        Der Standardwert ist 60 Sekunden, wenn Sie diesen Parameter mit /stop
        verwenden.
        Der Standardwert ist 0 Sekunden, wenn Sie diesen Parameter mit /reboot
        verwenden.
        / Status: um den Status aller Internetdienste anzuzeigen, verwenden
        / Sie diesen Parameter. 
        /enable: verwendet diesen Parameter, um den Diensten zu ermöglichen
        /Internet neu gestartet. 
        /disable: Verwendung dieser Parameter, die Internetdienste zu
        /deaktivieren, starten Prozess erneut. 
        */
        Process proc = new Process();
        proc.StartInfo = new ProcessStartInfo(helper.Observe.RestartApp,
            helper.Observe.RestartParameter);
        proc.Start();
        EventLog.WriteEntry(_eventLogSource, "Restart IIS end",
            EventLogEntryType.Information);
    }
    #endregion
    /// <summary>
    /// This class contains the observe information and the request.
    /// </summary>
    private class ObserveHelper
    {
        private readonly IObserve _observe;
        private readonly WebRequest _request;
        #region properties
        /// <summary>
        /// Gets the observe.
        /// </summary>
        /// <value>The observe.</value>
        public IObserve Observe
        {
            get { return _observe; }
        }
        /// <summary>
        /// Gets the request.
        /// </summary>
        /// <value>The request.</value>
        public WebRequest Request
        {
            get { return _request; }
        }
        #endregion
        #region initialize
        /// <summary>
        /// Initializes a new instance of the <see cref="ObserveHelper"/> class.
        /// </summary>
        /// <param name="observe">The observe.</param>
        /// <param name="request">The request.</param>
        public ObserveHelper(IObserve observe, WebRequest request)
        {
            _observe = observe;
            _request = request;
        }
        #endregion
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)