views:

636

answers:

1

We had a rare exception occur when reading the standard .Net user settings (this are the ones found in "project properties" in VS 2008):

System.Configuration.ConfigurationErrorsException was caught
  Message="Configuration system failed to initialize"
  Source="System.Configuration"
  BareMessage="Configuration system failed to initialize"
  Line=0
  StackTrace:
       at System.Configuration.ConfigurationManager.PrepareConfigSystem()
       at System.Configuration.ConfigurationManager.GetSection(String sectionName)
       at System.Configuration.PrivilegedConfigurationManager.GetSection(String sectionName)
       at System.Diagnostics.DiagnosticsConfiguration.GetConfigSection()
       at System.Diagnostics.DiagnosticsConfiguration.Initialize()
       at System.Diagnostics.DiagnosticsConfiguration.get_IndentSize()
       at System.Diagnostics.TraceInternal.InitializeSettings()
       at System.Diagnostics.TraceInternal.get_Listeners()
  InnerException: System.Configuration.ConfigurationErrorsException
       Message="Unexpected end of file has occurred. The following elements are not closed: setting, SettingsTest.Properties.Settings, userSettings, configuration. Line 7, position 1. (C:\\Documents and Settings\\USER\\Local Settings\\Application Data\\Hitcents\\SettingsTest.vshost.exe_Url_ghwhc20utv4toanuinmj0pfsljthcugo\\1.0.0.0\\user.config line 7)"
       Source="System.Configuration"
       BareMessage="Unexpected end of file has occurred. The following elements are not closed: setting, SettingsTest.Properties.Settings, userSettings, configuration. Line 7, position 1."
       Filename="C:\\Documents and Settings\\USER\\Local Settings\\Application Data\\Hitcents\\SettingsTest.vshost.exe_Url_ghwhc20utv4toanuinmj0pfsljthcugo\\1.0.0.0\\user.config"
       Line=7
       StackTrace:
            at System.Configuration.ConfigurationSchemaErrors.ThrowIfErrors(Boolean ignoreLocal)
            at System.Configuration.BaseConfigurationRecord.ThrowIfParseErrors(ConfigurationSchemaErrors schemaErrors)
            at System.Configuration.BaseConfigurationRecord.ThrowIfInitErrors()
            at System.Configuration.ClientConfigurationSystem.OnConfigRemoved(Object sender, InternalConfigEventArgs e)
       InnerException: System.Xml.XmlException
            Message="Unexpected end of file has occurred. The following elements are not closed: setting, SettingsTest.Properties.Settings, userSettings, configuration. Line 7, position 1."
            Source="System.Xml"
            LineNumber=7
            LinePosition=1
            SourceUri=""
            StackTrace:
                 at System.Xml.XmlTextReaderImpl.Throw(Exception e)
                 at System.Xml.XmlTextReaderImpl.Throw(String res, String arg)
                 at System.Xml.XmlTextReaderImpl.Throw(Int32 pos, String res, String arg)
                 at System.Xml.XmlTextReaderImpl.ThrowUnclosedElements()
                 at System.Xml.XmlTextReaderImpl.ParseElementContent()
                 at System.Xml.XmlTextReaderImpl.Read()
                 at System.Xml.XmlTextReader.Read()
                 at System.Xml.XmlTextReaderImpl.Skip()
                 at System.Xml.XmlTextReader.Skip()
                 at System.Configuration.XmlUtil.StrictSkipToNextElement(ExceptionAction action)
                 at System.Configuration.BaseConfigurationRecord.ScanSectionsRecursive(XmlUtil xmlUtil, String parentConfigKey, Boolean inLocation, String locationSubPath, OverrideModeSetting overrideMode, Boolean skipInChildApps)
                 at System.Configuration.BaseConfigurationRecord.ScanSectionsRecursive(XmlUtil xmlUtil, String parentConfigKey, Boolean inLocation, String locationSubPath, OverrideModeSetting overrideMode, Boolean skipInChildApps)
                 at System.Configuration.BaseConfigurationRecord.ScanSections(XmlUtil xmlUtil)
                 at System.Configuration.BaseConfigurationRecord.InitConfigFromFile()
            InnerException: 

*NOTE: this is re-created from a test app.

I pulled up the user.config file, and half of it was missing.

I expect our application was terminated abruptly for some reason or another.

This seems very rare, here is how we interact with the settings:

//How we read
Settings settings = Settings.Default;
_ourStaticMemberVariable = settings.OurValue;

//How we save
Settings settings = Settings.Default;
settings.OurValue = "Our Value";
settings.Save();

Is there anything wrong with how we're using it? Both calls have a try-catch that place some default values, but the values need to be able to reset from our application.

When in this state, our application cannot save new settings--and I cannot figure out a good way to programmatically recover. I had to manually find the user.config and delete it.

I also tried calling Settings.Reset(), etc. but get the same exception.

Any ideas on how to fix this? Or are we better off writing our own settings system or saving persistent settings in another way?

EDIT: A workaround is to delete the file from code, if you get a ConfigurationErrorsException.

Anyone know how to get the full path of the user.config file?

+5  A: 

The way to programmatically recover is to do what you did manually - delete the user settings file. Then call Settings.Reset. (You could also write a new user settings file with default values instead of deleting it, but if you're using the configuration manager properly that's essentially the same thing.)

This is a pretty rare occurrence, but it's not totally unheard of. Not only can your program crash while writing the user settings file, the file itself is user-writeable, so other programs the user runs could mess with it.

To avoid this particular vulnerability, persist user settings in a durable store with transactional integrity, i.e. a database. (You'll still have vulnerabilities, just not this one.) That's a lot of work for what in most cases will be a marginal improvement in reliability. But "in most cases" doesn't mean "in all cases;" yours may warrant it.

Robert Rossney
How do I get the path to the user settings file? A database is overkill for my scenario.
Jonathan.Peppers
Note that the InnerException, of type System.Configuration.ConfigurationErrorsException, has a Filename property...
technophile
I guess I'll have to make a helper method to pull the filename out of the inner exception here.
Jonathan.Peppers
`string filename = ((ConfigurationErrorsException)e.InnerException).Filename;` should do it.
Robert Rossney
+1 Excellent, this is exactly what I need. Thanks mate
Binary Worrier