WindowsAppSDK
WindowsAppSDK copied to clipboard
Microsoft.Storage.ApplicationData
Proposal: Microsoft.Storage.ApplicationData
Summary
Provide a modern enhanced version of Windows.Storage.ApplicationData and Windows.Management.Core.ApplicationDataManager
Microsoft-internal task 40050113
Rationale
Windows.Storage.ApplicationData.Currentonly works if you have package identity AND running in AppContainerWindows.Management.Core.ApplicationDataManager.CreateForPackageFamilyis equivalent but requires you have package identity AND running MediumIL or higher- Unpackaged apps have long been advised to use
%LOCALAPPDATA%\publisher\product,%LOCALAPPDATA%\TempandHKCU\SOFTWARE\publisher\productbut ApplicationData doesn't support that Windows.Storage.ApplicationDatahas several deprecated and obsolete features (e.g. .RoamingFolder)ApplicationData.*Folderonly provide access to the filesystem viaStorageFolder. There's no way to get their.Pathwithout going thrug aStorageFolderincurring significant performance overheadApplicationDatahas async methods which should be available synchronously e.g.ClearAsync()should offer `Clear() as a synchronous variant (or replacement?)
Windows App SDK can and should provide a modern revision of the ApplicationData API, addressing the issues above (one API supporting both packaged and unpackaged apps).
Scope
| Capability | Priority |
|---|---|
| APIs equivalent to Windows.Storage.ApplicationData (minus deprecated APIs) | Must |
| APIs available to packaged and unpackaged applications | Must |
| APIs offer filesystem access without requiring use of StorageFolder | Must |
| Synchronous equivalents to Windows.Storage.ApplicationData.*Async() APIs | Must |
| WinRT API | Must |
Important Notes
Microsoft.Storage.ApplicationData supports packaged and unpackaged apps. The underlying data stores are different but equivalent(ish):
| Packaged | Unpackaged |
|---|---|
ApplicationData.LocalCacheFolder |
%LOCALAPPDATA%\<publisher>\<product> |
ApplicationData.LocalFolder |
%LOCALAPPDATA%\<publisher>\<product> |
ApplicationData.MachineFolder |
%ProgramData%\<publisher>\<product> |
ApplicationData.RoamingFolder |
%LOCALAPPDATA%\Roaming |
ApplicationData.TemporaryFolder |
%LOCALAPPDATA%\Temp |
ApplicationData.LocalSettings |
HKCU\SOFTWARE\<publisher>\<product> |
WinRT API
Here's a sketch of the general shape of the API to express the intent of the ask. The actual API when designed/spec'd/reviewed/approved may differ.
Microsoft.Storage.ApplicationData.idl
namespace Microsoft.Storage.ApplicationData
{
[contract(ApplicationData, 1)]
enum ApplicationDataLocality
{
// NOTE: Values compatible with Windows.Storage.ApplicationDataLocality
Local = 0,
LocalCache = 3,
SharedLocal = 4,
Temporary = 2,
}
[contract(ApplicationData, 1)]
runtimeclass ApplicationData
{
/// Return an ApplicationData object if the current process has package identity
/// NOTE: This is equivalent to `ApplicationData.ForPackageFamily(Windows.ApplicationModel.Package.Current.Id.FamilyName)`
static ApplicationData GetDefault();
/// NOTE: Requires MediumIL (or higher) if the process lacks package identity.
/// NOTE: Requires MediumIL (or higher) if the process has package identity but different from the packageFamilyName parameer.
/// NOTE: GetForPackageFamily(Windows.ApplicationModel.Package.Current.Id.FamilyName) is equivalent to GetDefault()
static ApplicationData GetForPackageFamily(String packageFamilyName);
/// NOTE: Requires admin privilege if user != current user
/// NOTE: GetForUser(Window.System.User.GetDefault) is equivalent to GetDefault()
static ApplicationData GetForUser(Windows.System.User user);
/// NOTE: Requires admin privilege if user != current user
/// NOTE: GetForUser(Window.System.User.GetDefault, packageFamilyName) is equivalent to GetForPackageFamily(packageFamilyName)
static ApplicationData GetForUser(Windows.System.User user, String packageFamilyName);
StorageFolder LocalCacheFolder;
StorageFolder LocalFolder;
StorageFolder TemporaryFolder;
StorageFolder SharedLocalFolder; //NOTE: Returns NULL if process lacks package identity
String LocalCachePath;
String LocalPath;
String TemporaryPath;
String SharedLocalPath; //NOTE: Returns NULL if process lacks package identity
ApplicationDataContainer LocalSettings;
void Clear();
void Clear(ApplicationDataLocality locality);
AsyncAction ClearAsync();
AsyncAction ClearAsync(ApplicationDataLocality locality);
}
}
Open Questions
Q: Is there an unpackaged equivalent to GetPublisherCacheFolder()?
See https://github.com/microsoft/WindowsAppSDK/issues/2621#issuecomment-1158341320 for the latest discussion that sparked the creation of this issue
Windows.Storage.ApplicationData.Current only works fir you have package identity AND running in AppContainer
Nit: I'd add the word reliably in there (e.g. only works reliably), just to bring attention to how evil this API truly is. Because the API does work most of the time, it misleads devs and puts them on a path to failure.
Otherwise thanks for writing this up and moving the needle. I've been talking about these tricky APIs/scenarios since around 2017!
Windows.Storage.ApplicationData.Current only works if you have package identity AND running in AppContainer
Nit: I'd add the word reliably in there (e.g. only works reliably), just to bring attention to how evil this API truly is. Because the API does work most of the time, it misleads devs and puts them on a path to failure.
Can you elaborate on what you find unreliable about ApplicationData.Current?
Is it really unreliable or rather the complexity of having to understand the right contexts it does/not support (i.e. the Rationale's first 3 bullets)?
Can you elaborate on what you find unreliable about ApplicationData.Current?
ApplicationData.Current.LocalSettings usage outside of AppContainer is on a best effort basis I'm told. Was a top crasher for EarTrumpet (runFullTrust app) until I replaced it recently with ApplicationDataManager.CreateForPackageFamily(...).LocalSettings. 😎
ApplicationData.*Folderonly provide access to the filesystem viaStorageFolder. There's no way to get their.Pathwithout going thrug aStorageFolderincurring significant performance overhead
Can definitely see the importance of getting a filesystem Path without StorageFolder. However, it would be valuable to know whether Microsoft sees the performance overhead as inevitable with StorageFolder/StorageFile. #8 is obviously a separate issue to tackle, but should new APIs really be built around paths if an improved StorageFolder (or something new) is on the backlog?
EDIT: IDL in spec shows that both a StorageFolder and Path will be available for the given locations, so this seems like less of a statement on the future of StorageFolder and more about facilitating interoperability from non Windows.Storage APIs which take a filesystem path.
Gets a 👍🏻 from me
EDIT: IDL in spec shows that both a StorageFolder and Path will be available for the given locations, so this seems like less of a statement on the future of StorageFolder and more about facilitating interoperability from non Windows.Storage APIs which take a filesystem path.
Bingo! :-)
Just to let you know what I did (a manual and not an elegant solution) to walkaround the Windows.Storage.ApplicationData for now:
- Use
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)to replace the (path to) local storage folder (Windows.Storage.ApplicationData.Current.LocalFolder). - Use XML for local settings (Windows.Storage.ApplicationData.Current.LocalSettings).
Edited: for my situation only as in discussion #2618
Use System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) to replace the (path to) local storage folder (Windows.Storage.ApplicationData.Current.LocalFolder).
BTW that's not equivalent. ApplicationData.LocalFolder is a per-user location in your user profile1,2 but GetExecutingAssembly() is wherever the assembly is located (i.e. where your .exe or .dll lives). That could be in your user profile but could be lots other places, and it's the same answer for the assembly regardless of the user.
Windows.Storage.AppDataPaths.GetDefault().LocalAppData + \publisher\product is the unpackaged equivalent to ApplicationData.Current.LocalFolder -- a per-user location for data for the product/package.
Windows.Storage.AppDataPaths.GetDefault().LocalAppData == SHGetKnownFolderPath(KnownFolderID_LocalAppData...) == %LOCALAPPDATA% == %USERPROFILE%\AppData\Local (but APIs are better than environment variables as the latter can be trivially altered/hacked; they're not reliable or definitive sources of the answer, those APIs are).
1 Maybe. ApplicationData for a package family exists under %USERPROFILE% if the package is installed on %SystemDrive% (i.e. C:) but if you install packages to other drives then their ApplicationData will be created on the same drive e.g. install a packaged app on Q: and ApplicationData.LocalFolder will likewise be located on Q:
2 But that's not absolute. If you then move the package to another drive the package moves but not the data e.g. pkgmgr.MovePackageAsync(foo,...) from Windows.ApplicationModel.Package.InstalledPath=Q:... to M:... but ApplicationData will still be on Q: