13 votes

C# - Paramètres utilisateur cassés

Nous avons eu une exception rare se produire lors de la lecture des paramètres utilisateur standard .Net (ceux que l'on trouve dans les "propriétés du projet" dans VS 2008):

System.Configuration.ConfigurationErrorsException a été interceptée
  Message="L'initialisation du système de configuration a échoué"
  Source="System.Configuration"
  BareMessage="L'initialisation du système de configuration a échoué"
  Ligne=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="Une fin de fichier inattendue s'est produite. Les éléments suivants ne sont pas fermés : setting, SettingsTest.Properties.Settings, userSettings, configuration. Ligne 7, position 1. (C:\\Documents and Settings\\USER\\Local Settings\\Application Data\\Hitcents\\SettingsTest.vshost.exe_Url_ghwhc20utv4toanuinmj0pfsljthcugo\\1.0.0.0\\user.config ligne 7)"
       Source="System.Configuration"
       BareMessage="Une fin de fichier inattendue s'est produite. Les éléments suivants ne sont pas fermés : setting, SettingsTest.Properties.Settings, userSettings, configuration. Ligne 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"
       Ligne=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="Une fin de fichier inattendue s'est produite. Les éléments suivants ne sont pas fermés : setting, SettingsTest.Properties.Settings, userSettings, configuration. Ligne 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: ceci est recréé à partir d'une application de test.

J'ai ouvert le fichier user.config et la moitié était manquante.

Je suppose que notre application a été arrêtée brusquement pour une raison ou une autre.

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

//Comment nous lisons
Paramètres settings = Paramètres par défaut;
_notreVariableMembreStatique = settings.NotreValeur;

//Comment nous sauvegardons
Paramètres settings = Paramètres par défaut;
settings.NotreValeur = "Notre Valeur";
settings.Save();

Y a-t-il quelque chose de mal dans notre utilisation? Les deux appels ont un try-catch qui met en place certaines valeurs par défaut, mais les valeurs doivent pouvoir être réinitialisées depuis notre application.

Dans cet état, notre application ne peut pas enregistrer de nouveaux paramètres - et je ne peux pas trouver de bonne façon de récupérer de manière programmatique. J'ai dû trouver manuellement le user.config et le supprimer.

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

Des idées sur comment résoudre ceci? Ou devrions-nous plutôt écrire notre propre système de paramètres ou enregistrer des paramètres persistants d'une autre manière?

EDIT: Une solution de contournement est de supprimer le fichier depuis le code, si vous obtenez une ConfigurationErrorsException.

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

16voto

tofutim Points 5045

Voici une solution qui ne nécessite pas de quitter l'application avec des félicitations à Jarle (http://www.codeproject.com/Articles/30216/Handling-Corrupt-user-config-Settings?msg=3608682#xx3608682xx). Tôt dans le processus, avant que les paramètres ne soient jamais appelés, utilisez ceci

    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, au lieu de compter sur les paramètres pour générer l'erreur, lisez le fichier avec ConfigurationManager, de cette façon la version du système ne se retrouve jamais dans un mauvais état.

11voto

Robert Rossney Points 43767

La façon de récupérer de manière programmée est de faire ce que vous avez fait manuellement - supprimer le fichier des paramètres utilisateur. Ensuite appeler Paramètres.Réinitialiser. (Vous pourriez é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.)

C'est un cas assez rare, mais ce n'est pas totalement inconnu. Non seulement votre programme peut planter lors de l'écriture du fichier des paramètres utilisateur, mais le fichier lui-même est modifiable par l'utilisateur, donc d'autres programmes que l'utilisateur exécute pourraient le modifier.

Pour éviter cette vulnérabilité particulière, stockez les paramètres utilisateur dans un magasin durable avec une intégrité transactionnelle, c'est-à-dire une base de données. (Vous aurez toujours des vulnérabilités, juste 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 pourrait le justifier.

3voto

Seba Illingworth Points 1536
[STAThread]
private static void Main(string[] args)
{
    try
    {
        // ...
    }
    catch (System.Configuration.ConfigurationErrorsException ex)
    {   
        var config = ((System.Configuration.ConfigurationErrorsException)ex.InnerException).Filename;
        // informer l'utilisateur, lui demander de redémarrer
        System.IO.File.Delete(config);
        Application.Exit();
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X