2010-02-15 17 views
5

Nous avons eu une rare exception se produit lors de la lecture des paramètres utilisateur standard .Net (ce sont ceux trouvés dans les « propriétés du projet » dans VS 2008):C# - Paramètres utilisateur cassé

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: 

* NB: ceci est recréé à partir d'une application de test.

J'ai extrait le fichier user.config et la moitié manquait.

Je m'attends à ce que notre application a été interrompue brusquement pour une raison ou une autre.

Cela semble très rare, voici comment nous interagissons avec les paramètres suivants:

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

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

Y at-il quelque chose de mal avec la façon dont nous l'utilisons? Les deux appels ont un try-catch qui place certaines valeurs par défaut, mais les valeurs doivent être en mesure de réinitialiser à partir de notre application. Dans cet état, notre application ne peut pas enregistrer de nouveaux paramètres - et je n'arrive pas à trouver un bon moyen de récupérer par programme. J'ai dû trouver manuellement le user.config et le supprimer.

J'ai également essayé d'appeler Settings.Reset(), etc., mais obtenir la même exception.

Des idées pour résoudre ce problème? Ou sommes-nous mieux d'écrire notre propre système de paramètres ou d'enregistrer les paramètres persistants d'une autre manière?

EDIT: Une solution de contournement consiste à supprimer le fichier du code, si vous obtenez une exception ConfigurationErrorsException.

Quelqu'un sait comment obtenir le chemin complet du fichier user.config?

Répondre

9

La méthode de récupération par programme consiste à effectuer manuellement ce que vous avez fait: supprimez le fichier de paramètres utilisateur. Puis appelez Settings.Reset. (Vous pouvez également écrire un nouveau fichier de paramètres utilisateur avec des valeurs par défaut au lieu de le supprimer, mais si vous utilisez correctement le gestionnaire de configuration, c'est essentiellement la même chose.)

Ceci est une occurrence assez rare, mais ce n'est pas totalement du jamais vu. Non seulement votre programme peut-il tomber en panne lors de l'écriture du fichier de paramètres utilisateur, mais le fichier lui-même est modifiable par l'utilisateur, de sorte que d'autres programmes exécutés par l'utilisateur peuvent le perturber.

Pour éviter cette vulnérabilité particulière, maintenez les paramètres utilisateur dans un magasin durable avec intégrité transactionnelle, c'est-à-dire une base de données. (Vous aurez toujours des vulnérabilités, mais pas celle-ci.) C'est beaucoup de travail pour ce qui, dans la plupart des cas, sera une amélioration marginale de la fiabilité. Mais "dans la plupart des cas" ne signifie pas "dans tous les cas"; le vôtre peut le justifier.

+0

Comment obtenir le chemin d'accès au fichier de paramètres utilisateur? Une base de données est trop lourde pour mon scénario. – jonathanpeppers

+0

Notez que InnerException, de type System.Configuration.ConfigurationErrorsException, a une propriété Filename ... – technophile

+0

Je suppose que je vais devoir faire une méthode d'assistance pour extraire le nom de fichier de l'exception interne ici. – 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

C'est exactement ce que j'ai fait, sauf que j'ai répété mon application comme un nouveau processus plutôt que de sortir. – jonathanpeppers

8

est ici une solution qui ne nécessite pas que vous quittez l'application avec Kudos à Jarle (http://www.codeproject.com/Articles/30216/Handling-Corrupt-user-config-Settings?msg=3608682#xx3608682xx). Dès le début, avant que les réglages jamais est appelé, utilisez ce

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; 
    } 

Essentiellement, plutôt que de compter sur séances de jeter l'erreur, lire le fichier avec le ConfigurationManager, de cette façon la version du système ne fait jamais dans un mauvais état.