0

When it was a clickonce program it worked, but then I made an appxpackage and exported it as a centennial app for windows store and the upgrade does not work any more.

Right now I have in App.xaml.cs

protected override void OnStartup(StartupEventArgs e) {
   if (myprog.Properties.Settings.Default.UpgradeRequired)
   {    
      myprog.Properties.Settings.Default.Upgrade();
      myprog.Properties.Settings.Default.UpgradeRequired = false;
      myprog.Properties.Settings.Default.Save();
   }

With UpgradeRequired as a bool in user settings. Is that the right place?

I am getting settings reset on each version update. Now I have several of these directories

C:\Users\me\AppData\Local\progname\prog.exe_Url_randomChars

each with several different version of program settings. So after the upgrade another one of those is created, instead a subfolder with x.x.x.x of the current version.

As before, on each version release I increase version in Assembly Information the Assembly Version, File Version, and now I have the same numbers in AppxManifest.xml. I keep the last number group of the version to 0 as advised, and just increase the 3rd number group.

Is there something I am missing?

Stefan Wick MSFT
  • 13,600
  • 1
  • 32
  • 51
Daniel
  • 1,064
  • 2
  • 13
  • 30
  • Is the upgrading process working well in Centennial app? Has Upgrade method ever been executed? – Franklin Chen - MSFT Jan 03 '17 at 09:59
  • @FranklinChen-MSFT The store will show me there is an update for my Centennial app, and update the app. But on the start it would reset all settings from the previous version to the default values. I have the above code in a try/catch block and the MessageBox should popup if there is an issue, but it looks like Upgrade() does not do anything. – Daniel Jan 05 '17 at 16:22
  • Once the app has been converted to Centennial and is distributed via Windows Store, the app will get auto-updates just like a UWP. Unless I am missing something about the scenario here, I believe we don't need this code anymore, right? – Franklin Chen - MSFT Jan 11 '17 at 10:08
  • @FranklinChen-MSFT the app does auto-update like other UWP apps, but the app settings from the previous version are gone. Whether I have the above code or not the user looses all settings. – Daniel Jan 12 '17 at 20:51
  • 1
    @Daniel Did you manage to resolve this problem? I am having the same settings-loss-on-upgrade issue with my WPF-converted UWP app. – Andrey Shcherbakov Dec 03 '19 at 05:50

3 Answers3

1

UWP and Desktop Bridge apps need to store their settings in ApplicationData.LocalSettings:

https://learn.microsoft.com/en-us/windows/uwp/design/app-settings/store-and-retrieve-app-data#local-app-data

Stefan Wick MSFT
  • 13,600
  • 1
  • 32
  • 51
0

You could load the previous user.config file into current settings. This is just a workaround, and can be used to transition to ApplicationData.LocalSettings.

public static void Init()
{
    if (myprog.Properties.Settings.Default.UpgradeRequired)
    {    
        LoadPreviousSettings(myprog.Properties.Settings.Default);
        myprog.Properties.Settings.Default.UpgradeRequired = false;
        myprog.Properties.Settings.Default.Save();
    }
}

private static void LoadPreviousSettings(params ApplicationSettingsBase[] applicationSettings)
{
    const string companyName = "YOUR_COMPANY_NAME_HERE";
    var userConfigXml = GetUserConfigXml(companyName);
    Configuration config = ConfigurationManager.OpenExeConfiguration(
        ConfigurationUserLevel.PerUserRoamingAndLocal);
    foreach (ApplicationSettingsBase setting in applicationSettings)
    {
        try
        {
            // loads settings from user.config into configuration
            LoadSettingSection(setting, config, userConfigXml);
            config.Save(ConfigurationSaveMode.Modified);
            ConfigurationManager.RefreshSection("userSettings");
        }
        catch (FileNotFoundException)
        {
            // Could not import settings.
            // Perhaps user has no previous version installed
        }
        setting.Reload();
    }

}

private static void LoadSettingSection(ApplicationSettingsBase setting, Configuration config, XDocument userConfigXml)
{
    string appSettingsXmlName = setting.ToString();
    var settings = userConfigXml.XPathSelectElements("//" + appSettingsXmlName);
    config.GetSectionGroup("userSettings")
        .Sections[appSettingsXmlName]
        .SectionInformation
        .SetRawXml(settings.Single().ToString());
}

private static XDocument GetUserConfigXml(string companyName)
{
    var localPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + $@"\{companyName}";
    // previous package folder
    var previousLocal = GetDirectoryByWriteTime(localPath, 1);
    // previous version, e.g. 1.2.0
    var prevousVersion = GetDirectoryByWriteTime(previousLocal, 0);
    // application settings for previous version
    return XDocument.Load(prevousVersion + @"\user.config");
}

private static string GetDirectoryByWriteTime(string path, int order)
{
    var direcotires = new DirectoryInfo(path).EnumerateDirectories()
        .OrderBy(d => d.LastWriteTime)
        .Reverse()
        .ToList();
    if (direcotires.Count > order)
    {
        var previous = direcotires[order];
        return previous.FullName;
    }
    throw new FileNotFoundException("Previous config file not found.");
}
Trympet
  • 55
  • 2
  • 6
0

There is a working answer here.

Basically you need to create a duplicate version using UWP's ApplicationData.Settings and then loading it at the beginning of the app. It is very straightforward when your settings are strings, bools, etc. But not so if you have unique settings.

To elaborate more from the answer in the link, when you have settings consisting of custom types/classes, when creating UWP's duplicate version, you can use Newtonsoft.Json to serialise the custom setting:

try
{
    ApplicationData.Current.LocalSettings.Values[value.Name] = value.PropertyValue;
}
catch
{
    string serialised = JsonConvert.SerializeObject(value.PropertyValue);
    ApplicationData.Current.LocalSettings.Values[value.Name] = serialised;
}

Then when loading your custom setting:

if (s.Name == "MyCustomSetting")
{
    var deserialised = JsonConvert.DeserializeObject<MyCustomClass>((string)setting.Value);
    s.PropertyValue = deserialised;
}
Lola
  • 68
  • 4