0

I have converted my Windows Forms (desktop) app to UWP using the latest iteration of the Desktop Bridge (via a Packaging Project in Visual Studio 2017) and am now testing the UWP install and upgrade processes for my app. When my app was deployed via the prior methodology (ClickOnce / .MSI / Windows Installer) my User Settings (preferences) were properly migrated and preserved when my app was upgraded to a later version. Under UWP all User Settings are lost/reset on upgrade. My issue is the same issue that was described in this thread (for which no resolution was provided):

Here is the prior thread I found, same issue, no resolution

Any ideas on how to properly preserve User Settings for a Desktop app brought to UWP via the Desktop Bridge?

Thanks!

2 Answers2

3

Store the data in ApplicationData.LocalSettings.

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

Stefan Wick MSFT
  • 13,600
  • 1
  • 32
  • 51
  • Is this a legacy/UWP incompatibility that could be fixed some day via the Desktop Bridge or other method? As you would expect, my WinForms app uses My.Settings to persist user preferences since that is the standard for WinForms apps. Its clear in testing the converted UWP version of my app that it is able to store/retrieve data to/from My.Settings without a problem. So the only part of My.Settings that is not currently working in UWP is preserving the user-level My.Settings data on app upgrade. Are there any plans to improve legacy/UWP compatibility in this area? Thanks for your responses! – Elkland Technologies Mar 30 '18 at 01:37
  • @ElklandTechnologies Yes, we are looking at ways to address this so that apps don't have to change their code. It's work we have on our backlog. For now you will have to change your code and use the modern app settings API. – Stefan Wick MSFT Mar 30 '18 at 13:34
  • Stefan...I've researched "extending desktop apps with UWP" but haven't found examples specific to the minimal changes required to access the ApplicationData.LocalSettings API from within a WinForms app's (.NET) code. From what I've gathered it looks like I need to add one or more references to my WinForms project to accomplish this but am wondering if you could tell me what are the minimum references I need to add. Btw I found info on Nuget package "Desktop Bridge Helpers" which will help me write conditional code to allow my app to adapt to running in both legacy & UWP environments. Thanks! – Elkland Technologies Mar 30 '18 at 20:59
  • Add references to Windows.winmd and System.Runtime.WindowsRuntime.dll. – Stefan Wick MSFT Mar 30 '18 at 21:03
  • Stefan...When I attempt to add the reference to Windows.winmd I get 2 build errors in my solution. I added the ref as described here: < https://docs.microsoft.com/en-us/windows/uwp/porting/desktop-to-uwp-enhance >. When I remove the ref the errors go away. The errors are: 1) The "GenerateDesktopBridgeAppxManifest" task was not given a value for the required parameter "EntryPointExe." 2) Problem generating manifest. Could not load file or assembly Windows.winmd or one of its dependencies. An attempt was made to load a program with an incorrect format. So I am unable to add the ref. – Elkland Technologies Mar 31 '18 at 18:59
  • Make sure the Windows.winmd reference is NOT being copied to the output folder. – Stefan Wick MSFT Mar 31 '18 at 20:25
  • When I added the ref I made sure I set the "Copy Local" property to "False" for the ref as described in the doc I linked above. Is there something else I need to do to prevent the ref from being copied to output? Unfortunately the build error prevents me from publishing, rebuilding an MSI (or viewing "Detected Dependencies" or details of "Primary Output"), or creating a UWP package so I can't easily see if the Windows.winmd is ending up in output. The only folder getting updated on-build is "obj/release" or "obj/debug" of my app project...Windows.winmd is NOT being copied to those folders. – Elkland Technologies Mar 31 '18 at 22:34
  • I found the solution to the build error related to Windows.winmd described above. In addition to setting "Copy Local" to "False", you must ALSO go to the main project's properties page, Publish tab, click Application Files and exclude Windows.winmd. That resolves the build error. Stefan...this info should be added to the MS document I linked above to save you from having to answer this question repeatedly. – Elkland Technologies Apr 01 '18 at 01:07
  • I just created a new Winforms project, added the reference and wrote code to write to LocalSettings. Everything worked fine without any extra steps. Were those build errors by any chance specific to your particular project? Do you see the same when you start with a new project? What version of VS are you using . Btw, might be worth starting a new question for this topic. – Stefan Wick MSFT Apr 01 '18 at 06:09
  • Thanks Stefan. This will be my last post on this thread. I created a new Windows Forms project, added the 2 refs (setting Copy Local to False), added a UWP Packaging project and was able to build without error. Others are experiencing this issue: – Elkland Technologies Apr 01 '18 at 17:26
  • See.. https://codedocu.com/Net-Framework/Windows-Store/Bridge/Erros/Bridge-Errors_colon_-Problem_dot_-Could-not-load-file-or-assembly-Windows_dot_winmd-or-one-of-its-dependencies?2023 – Elkland Technologies Apr 01 '18 at 17:56
  • And... https://stackoverflow.com/questions/32783102/could-not-load-file-or-assembly-windows-winmd?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa – Elkland Technologies Apr 01 '18 at 17:56
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() {
    LoadPreviousSettings(ApplicationSettings.Default, MyFancySettings.Default);
}

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