2010-02-15 19 views
5

Tuvimos producen una rara excepción al leer la configuración de usuario estándar .Net (esto son los que se encuentran en "propiedades del proyecto" en VS 2008):C# - Configuración de usuario roto

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: 

* Nota: Este es recreado desde una aplicación de prueba.

Abrí el archivo user.config y faltaba la mitad.

Espero que nuestra aplicación haya finalizado abruptamente por alguna razón u otra.

Esto parece muy raro, aquí es cómo interactuamos con los ajustes:

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

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

¿Hay algo malo en la forma en que estamos usando? Ambas llamadas tienen un try-catch que coloca algunos valores predeterminados, pero los valores deben poder restablecerse desde nuestra aplicación.

En este estado, nuestra aplicación no puede guardar configuraciones nuevas, y no puedo encontrar una buena manera de recuperarlas mediante programación. Tuve que encontrar manualmente el archivo user.config y eliminarlo.

También intenté llamar a Settings.Reset(), etc. pero obtuve la misma excepción.

¿Alguna idea sobre cómo solucionar esto? ¿O es mejor escribir nuestro propio sistema de configuración o guardar configuraciones persistentes de otra manera?

EDIT: Una solución consiste en eliminar el archivo del código, si obtiene una excepción ConfigurationErrorsException.

¿Alguien sabe cómo obtener la ruta completa del archivo user.config?

Respuesta

9

La manera de recuperar mediante programación es hacer lo que hizo manualmente; elimine el archivo de configuración del usuario. Luego llame al Settings.Reset. (También podría escribir un nuevo archivo de configuración de usuario con valores predeterminados en lugar de eliminarlo, pero si está utilizando el administrador de configuración correctamente, eso es esencialmente lo mismo.)

Esto es algo bastante raro, pero no es totalmente cierto inaudito. No solo se puede bloquear el programa al escribir el archivo de configuración del usuario, el archivo en sí mismo es de escritura por el usuario, por lo que otros programas que el usuario ejecute podrían meterse con él.

Para evitar esta vulnerabilidad en particular, persista la configuración del usuario en una tienda duradera con integridad transaccional, es decir, una base de datos. (Todavía tendrá vulnerabilidades, pero no esta). Eso es mucho trabajo para lo que en la mayoría de los casos será una mejora marginal en la confiabilidad. Pero "en la mayoría de los casos" no significa "en todos los casos"; el tuyo puede garantizarlo.

+0

¿Cómo obtengo la ruta al archivo de configuración del usuario? Una base de datos es excesiva para mi escenario. – jonathanpeppers

+0

Tenga en cuenta que InnerException, de tipo System.Configuration.ConfigurationErrorsException, tiene una propiedad Nombre del archivo ... – technophile

+0

supongo que voy a tener que hacer un método de ayuda para tirar el nombre de archivo de la excepción interna aquí. – jonathanpeppers

2
[STAThread] 
private static void Main(string[] args) 
{ 
    try 
    { 
     // ... 
    } 
    catch (System.Configuration.ConfigurationErrorsException ex) 
    { 
     var config = ((System.Configuration.ConfigurationErrorsException)ex.InnerException).Filename; 
     // notify user, ask them to restart 
     System.IO.File.Delete(config); 
     Application.Exit(); 
    } 
} 
+0

Esto es exactamente lo que hice, excepto que re-encontré con mi aplicación como un nuevo proceso en lugar de salir. – jonathanpeppers

8

Aquí hay una solución que no requiere que salga de la aplicación con felicitaciones a Jarle (http://www.codeproject.com/Articles/30216/Handling-Corrupt-user-config-Settings?msg=3608682#xx3608682xx). Desde el principio, antes de la configuración cada vez se vuelve a llamar, utilice este

public static bool CheckSettings() 
    { 
     var isReset = false; 

     try 
     { 
      ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal); 
     } 
     catch (ConfigurationErrorsException ex) 
     { 
      string filename = string.Empty; 
      if (!string.IsNullOrEmpty(ex.Filename)) 
      { 
       filename = ex.Filename; 
      } 
      else 
      { 
       var innerEx = ex.InnerException as ConfigurationErrorsException; 
       if (innerEx != null && !string.IsNullOrEmpty(innerEx.Filename)) 
       { 
        filename = innerEx.Filename; 
       }     
      } 

      if (!string.IsNullOrEmpty(filename)) 
      { 
       if (System.IO.File.Exists(filename)) 
       { 
        var fileInfo = new System.IO.FileInfo(filename); 
        var watcher 
         = new System.IO.FileSystemWatcher(fileInfo.Directory.FullName, fileInfo.Name); 
        System.IO.File.Delete(filename); 
        isReset = true; 
        if (System.IO.File.Exists(filename)) 
        { 
         watcher.WaitForChanged(System.IO.WatcherChangeTypes.Deleted); 
        } 
       } 
      } 
     } 

     return isReset; 
    } 

Esencialmente, en lugar de depender de Sesiones de tirar el error, lea el archivo con el ConfigurationManager, de esa manera la versión del sistema nunca se mete en un mal estado.