Bad experience with app container virtualization and workarounds

Occasional Contributor

The MSIX filesystem/registry virtualization sounds like a good idea on the surface but isn't usable for some sorts of apps. This post is a warning to other developers about what can go wrong, and hopefully a wakeup call to Microsoft. You guys are being too ambitious and keep shipping features that are buggy. 

 

Problems, in no particular order:

 

  1. The virtualization is justified on the grounds of helping Windows uninstall apps and clean up their data, but it also breaks common use cases like opening Notepad to edit a config file or show logs because Notepad can't see the file.

  2. Getting apps to run "inside" the container is painful. Windows has no support for this. MSIX Hero can do it, sometimes, and sometimes not. Explorer will let you see the package-specific directories but sometimes will show them as empty even if they aren't, etc etc. Generally this feature breaks a lot of stuff you'd expect to work and makes it hard to get access to your app's own files.

  3. Bugs in the app container / virtualization system can cause sub-processes to make complaints like "invalid working directory" when started inside a normal AppData path.

  4. No problem, you think. Luckily the manifest lets you disable the virtualization. It's just a little bit of XML. Sure enough this fixes the execution bugs, but ....

  5. Now AppInstaller refuses to install the MSIX! There's apparently no technical reason for this - it merely chooses not to, because in Microsoft's esteemed opinion only antisocial deviants would want to do this. You can install such an MSIX from the command line only.

  6. Even when that one is fixed (see below), that doesn't mean file IO will be reliable. Oh no. Even though the EXEs, DLLs and the MSIX itself are signed, Windows Defender will still randomly cause file IO operations to fail in apparently "impossible" ways, without any notification it's doing this whatsoever, for instance just randomly failing to copy a file half way through a tree copy operation. I had to figure this one out by using Processor Monitor and some lucky guesses. What's the point of signing software if Windows will still treat it like malware anyway?

Combined with the sudden yanking of ms-appinstaller://, the total lack of useful communication about when it'll be back, the caching bugs etc, you guys are simply punching developers in the face at this point. The whole reason I'm trying to opt out of virtualization is because your OS is full of bugs, not because I have an inexplicable fetish for leaving temporary files behind after uninstallation. If the feature worked we'd use it.

 

Fortunately there are some simple workarounds in this case: don't write files into AppData. If you're writing software that will be distributed using MSIX I strongly advise taking this path, or at least setting things up so that it's easy to do so. Write files into the user's home directory instead. The App Installer team have apparently not remembered that this is possible and their attempts to "force" developers to make files removable during uninstallation are pointless as a result. 

4 Replies

Hi @MikeH 

 

I don't work for Microsoft and this answer is just my personal experience and opinion.

 

That being said, MSIX is a relatively young technology compared to MSI and the entire Windows OS, it takes time to get it done properly.

 

1) I've hit this too but in a different way. The scenario you're presenting does not apply because all the files installed by an MSIX are read-only, so even if Notepad would "see" the file, you couldn't overwrite the installed version.

 

The problem I hit here was with apps that included a documentation file in the install folder, for example, an HTML that was failing to load within a browser (which of course is not part of the package container).  

 

Using the Package Support Framework, we could develop a generic fix for this type of problem. But as you said, it would have been nice to just work.

 

2) MSIX Hero and Hover (a free app we've built) use the built-in OS support to launch a process inside the container of a package, basically a silent execution of the PS cmdlet:

Invoke-CommandInDesktopPackage

If MSIX Hero or Hover can't get the job done you can try to run the cmdlet manually, using other params, since these tools only execute the cmdlet with the most command combination of params.

6) Digital signing does not guarantee the lack of virus inside your code, therefore AVs will continue to check this code and sometimes give us headaches with false-positive detections (even if the package is signed). Where the digital signature helps is with mand in the middle attacks and in enterprise infrastructures where most MSI/EXEs built internally were deployed without any digital signing (making it easier for many bad actors to inject a virus inside).

 

> don't write files into AppData.

 

Why? Do you have problems because this date is removed upon uninstall? How do you roam your app settings from the user profile home directory, in enterprise environments? (AppData has builtin roaming support)

As you probably know AppData is recommended to be used only for storing application data/settings, not the user data. For user data, you can use a library like My Documents as the default and let the user specify a different location. 

@Bogdan Mitrache Thanks for the tips. I tried Hover, thanks for releasing it, it's a nice app. I noticed it uses MSI ;) Actually the kind of no-fuss one-click install you've got with Hover is pretty much what I'd like to have, are you making that using WiX?, that's being made with your Advanced Installer product, right?

 

W.R.T. notepad the actual use case in my app is viewing or sending/attaching a log file. So read-only is fine. It looks like you guys developed a fixup that can modify command line arguments, is there a Win32 API to devirtualize file paths? I haven't yet experimented with the manifest feature that lets you expose files to other apps, perhaps that's the way to go.

 

Re: running inside the container. Do you or anyone else know how this PowerShell Cmdlet works internally? Again, is there a Win32 API for this? Or is running a PowerShell sub-process actually the official API for doing this kind of thing? Is there a way to make sub-processes reliably run in the same container by default? The docs suggest it should happen automatically but obviously that isn't the case. In my case, my app is running "cmd /c 'some command'" and it's cmd.exe that complains it can't find the directory, presumably because it's been started outside the container.

 

W.R.T. not using AppData - yeah, I know why AppData is there and it will cause problems not to use it because the files in question are actually mostly a large file cache. It's not really user data and the only reason to write it to the home dir is to escape the virtualization layer whilst still having a GUI install. So it won't roam well and I'm already looking for workarounds there, like maybe finding a cache directory under c:\windows or something like that. It needs to be somewhere sub-processes work because I invoke them inside this large private file tree.

 

Finally, with the virus scanner, I understand that malware authors can buy or steal signing keys, but then that rather suggests that the whole idea of revoking malware keys or using them for legal enforcement is a bust. If Microsoft want their operating system API to heuristically guess whether to allow basic low level operations or not, then it needs a top-class debugging and diagnostics experience. The app works fine when it's not signed and running outside of the installed version, presumably because it's a Java app and so perhaps the high-reputation JVM signature takes precedence. The moment I sign it, the app breaks in subtle ways that only show up during a full test cycle, without any information that it's done so, in ways that are difficult to debug. I will probably try signing it with an EV key next.

Hi @MikeH 

 

Yeah, the MSI package is built with Advanced Installer. You can build a similar package with the freeware edition and use it for as long as you want. This isn't a separate download, it's the same full version, the freeware features are included in the Simple project.

 

We've built a lightweight MSI because admins using the tool were asking us to provide something with the smallest footprint possible. Some even asked us for a ZIP because they don't want to use additional tools on their packaging/testing machines. So, we are testing this "ZIP deployment" for another tool now, the MSIX Troubleshooter, we'll see how this goes.

Of course, we evaluated packaging it as an MSIX but this just wasn't the best option for our users at that time. We will probably re-evaluate it.

 

For the command line arguments, we use our own launcher. It is a custom version that also integrates the PSF launcher from Microsoft. We had it before PSF was launched so we kept on improving that with what Microsoft released.

For accessing files from inside the package with an external process, like Notepad, we rely on the package support framework. Here is an example where a process from the container launches Notepad and gives it as param the file path from the container.  I don't have the details on this implementation as the colleague that worked on it is on vacation.

Re: running inside the container.  We don't know anything more than what is documented in the public docs. We don't use any Win32 API.
The only problem/limitation I had with it was that it could not launch explorer.exe inside the container, but in other cases, it usually worked.


Re: The moment I sign it, the app breaks. Have you tested the signed application without packaging it as an MSIX? It is strange for an app to crash just because it is digitally signed. The container, on the other hand, could be a source of headaches. We would all love a better debugging experience and I fully support your feedback to Microsoft, that is why I just added my feedback on the parts where I considered useful.

 

Thanks. I haven't tested signed-but-unpackaged, I'll do that once I finish my current pile of bugs.

I've found a workaround for the subprocess/VFS issues. If sub-processes are run in the redirected path then they seem to work properly, or at least cmd.exe does. So that leaves the question of how to know where the redirected path is.

My solution is to, at startup in native code, change the values of the LOCALAPPDATA and APPDATA environment vars (which my app uses) to be the output of SHGetKnownFolderPath with the KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET flag. This returns the path that Windows is redirecting writes to. By using this location directly instead of relying on the redirection, it appears to have fixed my issue. The nice thing about this is, it will automatically work for any program that uses the right environment variables in the process tree.

This preserves the VFS functionality whilst working around whatever bug is breaking things.