views:

792

answers:

3

I have a windows service that has a custom configuration section. In the configSectionHandler class I am using attributes on the properties to validate the settings like this:

    //ProcessingSleepTime Property
    [ConfigurationProperty("ProcessingSleepTime", DefaultValue = 1000, IsRequired = false)]
    [IntegerValidator(MinValue = 5, MaxValue = 60000)]
    public Int32 ProcessingSleepTime
    {
        get
        {
            if (this["ProcessingSleepTime"] == null)
                return 100;

            return (Int32)this["ProcessingSleepTime"];
        }
        set
        {
            this["ProcessingSleepTime"] = value;
        }
    }

If a value in the configuration file fails validation, a ConfigurationErrorsException is thrown. In a windows service this happens as it is trying to start and it's really ugly (it offers to launch the debugger). How can I gracefully handle this error? I tried wrapping the OnStart method in a try/catch but it had no effect.

Thanks.

A: 

First, check if your configuration contains the key that you're looking for, then wrap it in a try catch, then check if it's a valid integer:

int retValue = 100;
if(this.ContainsKey("ProcessingSleepTime"))
{
    object sleepTime = this["ProcessingSleepTime"];
    int sleepInterval;
    if(Int32.TryParse(sleepTime.ToString(), out sleepInterval)
    {
       retValue = sleepInterval;
    }
}
return retValue;
Ricardo Villamil
+1  A: 

Or better yet (as you might need multiple such a properties), using the code from @Ricardo Villiamil, create:

int GetIntFromConfigSetting(string settingName, int defaultValue)
{
   int retValue = defaultValue;
   if(this.ContainsKey(settingName))
   {
      int sleepInterval;
      if(Int32.TryParse(this[settingName], out sleepInterval)
      {
         retValue = sleepInterval;
      }
   }
   return retValue;
}

Then use it from any property you need to.

EDIT: actually, after re-reading the question once more, looks like this solves your problem only half-way, as if the value is out of the defined range, it will throw exception anyway.

EDIT2: You can hook the AppDomain.UnhandledException event in the static ctor of your config section handler. The static ctor is ran before any instance or static member of a class are accessed, so it guarantees that you will intercept the exception even if the main method of your service is not yet called.

And then, when you intercept and log the error, you can exit the service with some error code != 0 ( Environment.Exit(errorCode) ), so the service manager knows it failed, but not try to invoke a debugger.

Sunny
This works beautifully! Thanks!
Loki Stormbringer
A: 

Ok I think I have it. In my service I have code that looks like this in the constructor:

config = ConfigurationManager.GetSection("MyCustomConfigSection") as MyCustomConfigSectionHandler;

This is where the error is thrown. I can catch the error and log it. The error must be rethrown in order to prevent the service from continuing. This still causes the ugly behavior but at least I can log the error thereby informing the user of why the service did not start

Loki Stormbringer
No need to re-throw, just exit with error code - see my answer.
Sunny