Search code examples
comregistrymsix

How to query for installed "packaged COM" extension points


I work on a plugin-based application that is currently scanning the Windows registry for compatible COM servers that expose certain "Implemented Categories" entries. This works well for "regular" COM servers installed through MSI installers.

However, I'm now facing a problem with COM servers installed through MSIX installers that expose COM extension points through the "Packaged COM" catalog as described in https://blogs.windows.com/windowsdeveloper/2017/04/13/com-server-ole-document-support-desktop-bridge/ . These COM servers can still be instantiated through CoCreateInstance, but RegOpenKey/RegEnumKey searches aren't able to detect their presence.

I'm not sure how to approach this problem. The best outcome would be some sort of Windows API for querying the "Packaged COM" catalog for installed COM servers that I can run in addition to the registry search. However, I don't know if that even exist? I'm also open for other suggestions, as long as they still allows my application to dynamically detect the presence of new COM-based plugins.


Solution

  • Answering myself with sample code to query all installed COM servers, including the "Packaged COM" catalog, using ICatInformation::EnumClassesOfCategories. Based on suggestion by Aditi_Narvekar:

    #include <atlstr.h>
    #include <vector>
    
    static void CHECK(HRESULT hr) {
        if (FAILED(hr))
            abort(); // TODO: More graceful error handling
    }
    
    /** Return COM classes that implement any of the provided "Implemented Categories". */
    inline std::vector<CLSID> GetClassesWithAnyOfCategories(std::vector<CATID> impl_categories) {
        CComPtr<ICatInformation> cat_search;
        CHECK(cat_search.CoCreateInstance(CLSID_StdComponentCategoriesMgr));
    
        CComPtr<IEnumGUID> class_list;
        CHECK(cat_search->EnumClassesOfCategories((ULONG)impl_categories.size(), impl_categories.data(), -1, nullptr, &class_list));
    
        std::vector<CLSID> app_clsids;
        app_clsids.reserve(64);
        for (;;) {
            CLSID cur_cls = {};
            ULONG num_read = 0;
            CHECK(class_list->Next(1, &cur_cls, &num_read));
            if (num_read == 0)
                break;
    
            // can also call ProgIDFromCLSID to get the ProgID
            // can also call OleRegGetUserType to get the COM class name
    
            app_clsids.push_back(cur_cls);
        }
    
        return app_clsids;
    }