The Paint.Net App-V 5 connection group solution

OK. So a simple solution for every connection group situation isn’t going to happen any time soon. See recent posts in this series “Who Are You”? and App-V 5 Script Error 534 and A collection (so far) of #APPV 5 Client file visibility and blocking information. But here is one situation that I solved, and maybe that might help you with others as well???

The requirement
You want to provide Paint.Net as a virtualized application package using App-V 5. You package it up and out it goes. Paint.Net supports plug-ins, and let’s say that you start getting requests for the plug-ins. You could update the original package in the sequencer and give everyone the plug-in, but this is App-V so connection groups are a possibility.

There are hundreds of these plug-ins, some having the same name so you can’t have both. And they are independently released so you never know when someone is going to need a new one. All of which makes this a good candidate for using connection groups so that you don’t impact users that don’t need the new or updated plugin.

The problem
Except that Paint.Net doesn’t see the plug-ins when you use connection groups.

Some background on plug-ins in general
Sometimes apps work together as separate processes, but I don’t call those plug-ins. I use the term plug-in to indicate a scenario where a product’s exe process actively supports the addition of extra code (or sometimes data) which is independently developed and released, typically by a third party, that is to be loaded by the application directly into its own windows process.

Note: wikipedia uses the term “add-on” as a category that includes “plug-in”, “skinning”, and “theme” customization of an app. It also defines a separate category for “extensions”, but those are pretty much plug-ins with a more complicated integration.

The extra code (or sometimes data) usually is in the form of a dll, but possibly other WinPE format files such as tlb.

The app supporting the plug-in can locate plug-ins dynamically using a scheme that the developer chooses. And sometimes the developer thinks he/she is smarter than he/she really is. The two most common choices are:

  • The app reads an app specific registry key where plugins are registered. The plug-in registration may be a simple REG_SZ entry added to this key, or may be a sub-key with additional data, but ultimately the plug-in installation must add the dll component to the file system and then register it in the registry so that the app loads it in. In this registration, you might find just the name of the dll, or a full path to it.
  • The app looks to a specific folder and just loads all of the dlls it finds. How the app locates that folder is unimportant to the developer, but how he writes that code to find them has a huge impact on connection groups. There might be a registry key containing the location of the plug-in folder. There might be a registry key containing the location of the install folder of the app, and the plug-in loading logic runs relative to that location. The app might use a location relative to the current working directory. The app might use a location relative to the folder of the where the primary exe was actually loaded into memory from, as read from the process block. The app might use a hard coded folder (I mean, who wouldn’t install to C:\Program Files\Paint.Net?).

Or the app might do something else bizarre that I haven’t run into yet.

Oh, and when an app loads a dll, it can provide a full path to the dll, or just give the name of the dll and let Windows find it. Most of the time, the latter method is used. When an exe is built and references are added to it’s import table to automatically load when the exe is loaded, it must use the latter method and we are used to seeing this behavior in procmon traces: Windows first looks in the current working directory, and then follows the path variable. A really good developer would register a AppPath for the executable to pre-pend the additional folders via a registry setting and use, but apparently there are no good developers out there. A developer writing an app to load in plug-in dlls after launch could choose to just ask for the dll by name, or could choose to provide the path.

What the developer tells us about Paint.Net and Plug-ins
For Paint.Net, plug-ins sometimes have actual installers, or more often you just get a dll file.

The developer of Paint.Net tells plug-in developers to just drop these dlls into a specific sub-folder where you installed the product. Most go into a sub-folder called “Effects”, but there are also plugins that should drop files into the “FileTypes” or “Resources” sub-folders. Paint.Net has no registry setting for the location, or for where it was installed, so we are talking about a situation more like choice 2 above.

Things that don’t work for Paint.Net and Plug-ins via Connection Groups

  • Original package is installed to the designated PVAD (whether the expected location in Program Files or something more creative). Create the plug-in package by declaring a different PVAD, Expand-to-local-system and drop the files under the original package’s folders, causing these files to be VFS’d.
  • Original package is installed to the designated PVAD (whether the expected location in Program Files or something more creative). Create the plug-in package by declaring the same PVAD as the original package, Expand-to-local-system and drop the files under the original package’s folders, causing these files to be PVAD’d.
  • Original package is installed to the folder other than the designated PVAD (whether the expected location in Program Files or something more creative) causing the files to be VFS’d. Create the plug-in package by declaring a different PVAD, Expand-to-local-system and drop the files under the original package’s folders, causing these files to be VFS’d.
  • Original package is installed to the folder other than the designated PVAD (whether the expected location in Program Files or something more creative) causing the files to be VFS’d. Create the plug-in package by declaring the same PVAD as the original package, Expand-to-local-system and drop the files under the original package’s folders, causing these files to be VFS’d.
  • Any tricks involving Pellucidity (merge-with-local or override-local) settings in any of the packages in any of those scenarios.
  • Any of those scenarios, using a trick to modify the shortcut current working directory. The shortcuts for apps reference the target exe under the C:\ProgramData\App-V\guid\guid\Root folder (or Root\VFS\… under that when a VFS style install was used in the main package), and by default use the containing folder as the current working directory. We can’t alter the target location, as it must point to the real exe, but we can make an edit in the DeploymentConfig file to change the current working directory. Unfortunately, the developer does not seem to use the current working directory to locate the plugin folder. And if it had worked, it only solves launch by shortcut and not by FTA.

To the best of my knowledge (because the developer doesn’t provide the specifics or the source code), the developer seems to be looking for the dlls using a location relative to the folder of the where the primary exe was actually loaded into memory from, as read from the process block (or as written in more modern .Net developer speak, the loaded assembly information using reflection), and then providing the full path to that dll to load it.

The exe will always be loaded from the C:\ProgramData location and (currently) there is nothing we can do to change that location. Each of the attempts above fail to overlay the plug-ins in a way that will be seen using that detection technique.

Something that did work
I had given up on connection groups with Paint.Net after struggling with it on and off over a period of months.

But then one day, while I was researching something else, I hit upon an idea. What if I sequence the plug-ins not like the sequencer environment, but like the client environment under App-V? Here is what works.

  1. Sequence Paint.Net. You can use either a PVAD or VFS style install of the main package, but to keep things simple let’s assume a PVAD style installation.
  2. Revert the Sequencer to a clean snapshot.
  3. Prior to sequencing, determine the package and package version guids of the main package. This information is located in the internal AppXManifest.xml file. I typically use the AppV_Manage tool to pull this information out for me. On the Publishing tab, select the package and expand the manifest in the lower window. The GUIDs are under the identity property of the manifest.
  4. Natively install Paint.Net to the C:\ProgramData\App-V\guid\guid\Root folder (or the actual VFS subfolder under that folder). In other words, make the sequencer look exactly like the client will look when the package is deployed!.
  5. Now start sequencing, but as a new application and not a plug-in. Set the PVAD to a C:\uniquename. (Not that it really needs to be unique). Install/Drop the plug-in dlls under that ProgramData location, which will then be VFS’d.
  6. Create your connection group and you are good to go!
    • The only problem with this technique is that you can never update the main application without having to redo the plugins. So maybe you just ask the user’s what plugins they need and stick with a single package with the plug-ins that you only update once a year.

By Tim Mangan

Tim is a Microsoft MVP, and a Citrix CTP Fellow. He is an expert in App-V and MSIX.