Add per-machine storage support to MSIX
Proposal: Add per-machine storage to MSIX
Summary
Even after several years, MSIX still has some significant limitations when compared to Win32 installation technologies such as MSI.
Rationale
- As a developer of a Desktop Bridge app, I cannot store licensing data in a common location that is not also easily erased. I can't store this in the app's private Registry, as then (a) it's per-user only, so I (or the admin if in an organization) have to enter the licensing information for every user that uses the app. Also, if the user resets the app in Settings (perhaps because it's malfunctioning), the license information will be erased as well, causing confusion and annoyance (and probably a call to the help desk so IT can reenter it).
Note: For purposes of brevity, I will refer to this per-machine storage feature as PMLS.
Scope
| Capability | Priority |
|---|---|
| When an app reads from PMLS, all local users will read from the same data | Must |
| An elevated process in a Desktop Bridge package can write to PMLS | Must |
| PMLS is only cleared when the app is uninstalled by all users on the PC | Could |
| A process running outside the Desktop Bridge package can write to PMLS | Could |
| An app must use a specific schema or format of data to store it in PMLS | Won’t |
| An unelevated process can write to PMLS | Won’t |
Implementation Notes
- I can accomplish this today by (ab)using
customInstallActionsto install a small support program that runs outside the MSIX container (so it can modify the shared HKLM hive), and the app then funnels all PMLS write operations through it. - This feature may be best implemented as an extension to the MSIX manifest format, which will not function correctly on downlevel version of Windows. We may need to do something to my workaround above backport this functionality to older versions of Windows.
Open Questions
- Should I provide a way for an elevated app to write to another package’s PMLS? This would be useful to installation or automation tools to write licensing data in an automated process, without needing to launch the app and manipulate its UI. Note that I could accomplish this today by using a console EXE in the AppX package that accepts a license key, and then using the existing support for putting packaged EXEs in the PATH to make it available.
Check out https://blogs.windows.com/windowsdeveloper/2016/05/24/sharing-your-local-app-data/ - MSIX supports a "shared publisher cache" that IT admins can run. It doesn't accomplish your "writeable by admin, readable by all" requirement.
I'll think some more on this - there might be a way to accomplish it today, but you'd need some Power Tooling. The "remove on last user uninstall" is frankly the hard part since we like metadata-driven-only uninstallation. Definitely something that a Reunion API could help with.
@jonwis I didn't know that. However, seeing as it requires a machinewide Registry value to be set, what is the best way I ensure that the admin/user installing my app does so? I certainly can conditionalize the use of this feature, and store the license for the local user only if it’s not enabled, but how should I best document the process for enabling the shared publisher caches?
Also, I don't need the "writable-by-admin-only" setting to be actually enforced by the system; I can use an old-fashioned Group Policy (or similar) to disable the UI features that modify the licensing data for non-admin users. And the "remove-on-uninstall" is really a nice-to-have. I’ll edit the table above. Thanks!
Well, if your app is going to elevate on first launch by administrator to write the shared location, maybe it could also set the group policy? Do note that this would be unexpected on end-customer machines that aren't under group policy control. So this isn't a slam-dunk answer, but something to think about.
I still think your shared location suggestion is useful, so I'll leave the issue open - close it if you think you have enough, or add more suggestions on how you'd like to see it work.
Unfortunately, if I elevate, won’t then the Registry value still be redirected into the app’s private hive, as I am running a process from within the MSIX namespace?
@jonwis Question for you. Having researched the Package Support Framework, I am wondering if the following process, which was inspired by how PowerShell scripts are run there, would be useful for enabling the Registry value for Shared Local caches:
- If the change is required, prompt the user with a dialog box stating what will happen. Provide OK/Cancel buttons. Make it clear to the user that they will need to provide elevation credentials if OK is pressed.
- Launch a helper process with
PROC_THREAD_ATTRIBUTE_DESKTOP_APP_POLICYspecified such that the it is launched outside the AppX context. - The helper process immediately relaunches itself elevated using ShellExecute/runas; the user will be prompted to elevate here.
- If that checks out, the elevated helper process sets the Registry value to enable Shared Local caches.
- The main app waits for the helper processes to exit; it then can check to see if the Shared Local cache is now enabled and, if so, use it!
The only thing that makes me slightly iffy about this idea is https://github.com/microsoft/ProjectReunion/issues/16#issuecomment-630964492. You have said that running custom code outside the AppX container as part of an install is generally seen as a no-no. Note that this would be the only thing that would affect the system outside of the AppX container, and it would technically happen on first-launch, not during installation proper. Would this process be acceptable from a design standpoint? Thanks!
This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 10 days. It will be closed if no further activity occurs within 10 days of this comment.
@jonwis, can you take a look at @wjk's last questions?
ApplicationData.MachineFolder should address the issue for per-machine storage
Will that suffice to meet your scenario?
Closing Issue as per DrusTheAxe's comment above. Please comment if the proposed fix does not meet the requirements and we can re-open.