Reputation: 501
I have a WPF .NET Core 3.1 app packaged as an MSIX app. The app downloads some assets from S3 to the AppData folder and at some point, it starts another process (another app) with one of the arguments being the path to one of the downloaded assets (a Settings.xml file).
I'm facing two problems:
The app sometimes downloads the assets to the "real" AppData path (C:\Users\my_user\AppData\Local\some_created_folder
), sometimes to the virtualized path (C:\Users\my_user\AppData\Local\Packages\package_id\LocalCache\Local\some_created_folder
). I noticed the latter only recently in 3 different releases (3 consecutive versions): 1st used "real", 2nd used virtualized, 3rd used "real" once again. I'm pretty sure there was no code change that could cause this.
I'm composing the download paths using Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
. When the assets are being downloaded to the virtualized path, the 2nd app is not starting correctly, because the settings file path set as an argument when starting the process is pointing to the "real" path (always!). No exceptions or errors are thrown!
var appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var settingsFilePath = Path.Combine(appData, "Settings", "Settings.xml");
...
var settingsFile = new FileInfo(settingsFilePath);
if (settingsFile.Exists)
{
var arguments = $"-l \"{settingsFile.FullName}\"";
var fileInfo = new FileInfo(_options.ExePath);
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = fileInfo.FullName,
WorkingDirectory = fileInfo.DirectoryName ?? string.Empty,
Arguments = arguments
}
};
if (process.Start())
{
process.WaitForInputIdle();
}
_logger.LogDebug("Started {name} {arguments}", fileInfo.FullName, arguments);
}
else
{
throw new FileNotFoundException($"Settings file not found at path '{settingsFile.FullName}'!", Path.GetFileName(settingsFile.Name));
}
I read this, but I don't understand why the app is acting this unpredictable. Or am I missing something? The package manifest file has the EntryPoint="Windows.FullTrustApplication"
. I'm also aware that UWP Desktop Bridge virtualizes some File System paths, but I'm expecting it to be predictable.
Upvotes: 4
Views: 1148
Reputation: 73605
I read this, but I don't understand why the app is acting this unpredictable. Or am I missing something? The package manifest file has the EntryPoint="Windows.FullTrustApplication". I'm also aware that UWP Desktop Bridge virtualizes some File System paths, but I'm expecting it to be predictable.
From that link you provided, the details for the reasons why it appears "unpredictable" (note: the behaviour is slightly different before and after Windows 10 1903):
In response to a file open command, the OS will open the file from the per-user, per-package location first. If this location doesn't exist, the OS will attempt to open the file from the real AppData location. If the file is opened from the real AppData location, no virtualization for that file occurs.
And
All newly created files and folders in the user's AppData folder (e.g., C:\Users\user_name\AppData) are written to a private per-user, per-app location but merged at runtime to appear in the real AppData location. ... Modifications to existing files under the user's AppData folder is allowed to provide a higher degree of compatibility and interactivity between applications and the OS. ... State separation also allows packaged desktop applications to pick up where a non-packaged version of the same application left off.
The affected directories are:
Local
Local\Microsoft
Roaming
Roaming\Microsoft
Roaming\Microsoft\Windows\Start Menu\Programs
So, to answer you questions:
To always use the virtualized directories, look at the Windows.Storage.ApplicationData
API. The function Local[Cache]Folder
will always return the virtualized path.
Failing that, you must make sure that you (or a user) have never written to the non-virtualized AppData locations (e.g. C:\Users\user_name\AppData\MyCompany
) as that will always be used in preference to the virtualized location (e.g. C:\Users\user_name\AppData\Local\Packages\MyCompany.MyApp_PublisherId\LocalCache\
). That way, you will only ever read and write to the virtualized location.
The virtualized path to the file is the "real" file.
However, files in this location are only visible to the packaged app itself (see https://learn.microsoft.com/en-us/windows/msix/desktop/flexible-virtualization):
Files in the virtualized location are visible only to the app.
One final thing to note. When a user uninstalls an MSIX package, it will delete all of the virtualized AppData that your application wrote.
Upvotes: 0
Reputation: 10993
I don't have an anwer yet, but I need more details from you to further investigate this:
From what I know, packaged apps work with the plain, real AppData resource and path when they open a folder or file already present in that location. If the Appdata folder/files are created from scratch then it should use the virtualized location.
This behavior is useful when migrating from MSI to MSIX, until the user chooses to uninstall the MSI version, the MSI and MSIX variant can be used in parallel both having access to the same AppData files.
Then again, if this is not the first version of your app, and you have previously deployed it as an MSI to your users, you will not be able to know for sure which AppData folder you are working with when running from inside the container.
C:\Users\my_user\AppData\Local\some_created_folder
However, while researching this I found that you can also use Windows.Storage.ApplicationData.Current.LocalFolder.Path to write your appdata. Maybe this would work?
If that is the case, you could try saving the data in the CommonApplicationData folder, if this isn't user-specific data.
Related question:
Upvotes: 3