Anti-Disregardation for COM in MSIX

It took more than 3 years for this to creep into my brain. 

Over the six years since the original release MSIX Microsoft has added schema extensions to handle situations that the original MSIX didn’t support. COM support is a very important area of these improvements.  But originally the documentation was either incomplete or wrong, and we weren’t sure what OS versions would support the changes.  I’ve worked with the documentation team to get them to fix a lot of the documentation.  But Microsoft’s own tooling, the MSIX Packaging Tool, still doesn’t use the extensions, disregarding the traditional application registration requests.

So, I started work in my tooling to use them.  Still, I found instances where even with the new extensions it was impossible to convert the ways in which some traditional apps registered their COM into the new schemas.  Over the holidays, I had a brain-storm on how to fix this, and am working on trying to get as much COM supported as possible, something I am calling “anti-disregardation”.  This blog post explains some of the detail…

When Microsoft created AppX/MSIX, they changed the way in which COM is integrated into the operating system when the application package is installed.  Where previously, installers/apps just wrote directly into the Windows Registry to register their integrations, the application package instead declares its needs in the AppXManifest file, and Microsoft takes care of the rest but in a different way. This is a good thing on one hand, and a bad thing on another.

The good thing is really about security.  For example, the explorer processes, which give us access to the file system and creates our “desktop”, previously had to load any and all shell extensions mentioned in the registry, including those under the current user hive which requires no special permissions to write to.  

The bad thing is that the overall design of how to describe the COM components is upside down in the AppXManifest, and Microsoft didn’t create an xml schema set that allows us to map everything done by applications in the past into the new schema set.

With COM, we can consider three types of registration pieces that are needed for integration between a COM server (not a windows service, but a piece of software that performs a service – often a dll but sometimes an exe) and a COM client (an application needing that service, whether a user app or the previously mentioned explorer process):

  • COM CLSID This is a GUID that defines the file that contains the COM server (or servers), plus additional information needed for it to be instantiated by the system when needed.
  • COM INTERFACE This is a different GUID that defines the identity of a COM service that can be called by a COM client. If you think about traditional programming, it is sort of like the COM CLSID defining the name of the dll and the COM interface defining the name of a function inside the dll.
  • COM TYPELIB Yet another GUID, you can think of the typelib as being the definition of the arguments to the function call. These are not always needed when both sides agree on a standard argument pattern to be used.

In the traditional shell extensions, a vendor would want to write a COM service to do something like provide a context menu.  So they would create a unique COM CLSID GUID for the dll that would house their extension, a unique COM INTERFACE GUID that may be called, and optionally one or more COM TYPELIB name to describe the arguments.  Then, under the file types desired, they would add registration of their shell extension with a setting of a well-known name or GUID defined for that shell extension type.

But the file pointed to by the COM CLSID may have more than one COM CLSID (maybe the vendor also has a preview handler), or even the same CLSID might have multiple COM INTERFACE implemented.  And a COM INTERFACE can have multiple COM TYPELIB (just as a function can have different arguments sets that you can call it with), or the same TYPELIB can be used by different COM INTERFACES.

The traditional method of defining these directly in the registry could be mapped like the following diagram for a Shell Extension:

FTA–>Shell Extension–>Interface GUID<–>CLSID GUID–>File

In the AppXManifest, Microsoft created a very different mapping. We can’t know what they were thinking, but I would guess that they wanted a design to help them break up and isolate COM pieces so that a bad piece of software couldn’t do something like take down the explorer process. In fact, we see that some things previously done directly by the explorer process are now separate processes today. But overall, the mapping is not capable of handling all of the use cases supported in the past, many of which are in use by existing software in the field.

What Microsoft structured in the AppXManifest for COM registration, even with the extensions, does provide what a developer would need to implement the COM integrations for Shell Extensions that Microsoft has made available to date. It just doesn’t cover the cases of many existing applications. Often, the culprit is a uniqueness rule that something must only be defined once. And the upside-down mapping used in the AppXManifest doesn’t let you do that.

For IT Pros, since we don’t have access to the source code to break apart the vendor implementation, this had led to applications that just don’t work under MSIX.

The epiphany came when I realized that I can make a copy of the vendor COM server file and place it in the package. Then I can avoid uniqueness rules when I run into them by having one use point to one file containing the COM components, and another file copy with the same components so that separate uses can have their own registration path. Even better, since MSIX uses single-instance storage, multiple copies of the same file only take up the space of one file on the client system.

[Note: the MSIX package is bigger but someday I might try making the copy a junction point instead. For now I’m starting with a safe method of making a copy as I’m concerned about the junction point and CIMfs.]

As a proof of this concept, I took a look at Adobe Reader, which has a problem if your PDF uses certain features. When you capture the app in the Microsoft MSIX Packaging Tool, they even sometimes warn us about the issue by placing a comment in the AppXManifest File as seen here just before the supported COM extensions are included:

Microsoft seems to usually add comments into the Manifest for the things it detects but cannot map, but unfortunately it does not add comments for things it never looks for in the package, so reading the comments isn’t a complete list of what might be missing!

But here it did.  And the issue in this case was a single COM component file that supported multiple Typelibs for different purposes, which ran afoul of a uniqueness requirement of the AppXManifest schemas.

By making a copy of the component dll file, I found that I complete the registration by registering each typelib to a different copy of the file.  And doing this solved the Adobe reader “AcroCEF” issues.

These changes, plus support for a number of Shell Extensions that needed COM registrations which need some of the new Schema extensions, will be the next release of TMEditX (4.1 or above).

While this won’t solve all COM issues, it is going to make a really big dent.  Some of the new extensions will require Windows 11, but as the installer will ignore those extensions on Windows 10, it does no harm.  Given Windows 10 forthcoming end-of-life, that is a problem that will solve itself!  

 

By Tim Mangan

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