如何查询已安装的 "packaged COM" 个扩展点

How to query for installed "packaged COM" extension points

我正在开发一个基于插件的应用程序,该应用程序目前正在扫描 Windows 注册表以查找公开某些“已实施类别”条目的兼容 COM 服务器。这适用于通过 MSI 安装程序安装的“常规”COM 服务器。

但是,我现在遇到了通过 MSIX 安装程序安装的 COM 服务器的问题,这些安装程序通过 https://blogs.windows.com/windowsdeveloper/2017/04/13/com-server-ole-document-support-desktop-bridge/ 中所述的“打包 COM”目录公开 COM 扩展点。这些 COM 服务器仍然可以通过 CoCreateInstance 实例化,但是 RegOpenKey/RegEnumKey 搜索无法检测到它们的存在。

我不确定如何解决这个问题。最好的结果是某种 Windows API 用于查询已安装 COM 服务器的“打包 COM”目录 我可以 运行 另外到注册表搜索。但是,我不知道那是否存在?我也愿意接受其他建议,只要它们仍然允许我的应用程序动态检测是否存在新的基于 COM 的插件。

请忽略此答案。根据下面的 ICatInformation::EnumClassesOfCategories,有一个更好的答案。

用示例代码回答自己以查询已安装 COM 服务器的“打包 COM”目录。根据@SimonMourier 的建议。

using System.Collections.Generic;
using System.IO;

/** Use Target Framework Moniker as described in https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/desktop-to-uwp-enhance */
class PackagedComScan {
    static void Main(string[] args) {
        var packageManager = new Windows.Management.Deployment.PackageManager();
        // this call require the "packageQuery" capability if called from a UWP app (add <rescap:Capability Name="packageQuery" /> to the appxmanifest)
        IEnumerable<Windows.ApplicationModel.Package> my_packages = packageManager.FindPackagesForUser("");

        foreach (var package in my_packages) {
            try {
                ParseAppxManifest(package.InstalledLocation.Path + @"\AppxManifest.xml");
            } catch (FileNotFoundException) {
                // Installed package missing from disk. Can happen after deploying UWP builds from Visual Studio.
            }
        }
    }

    static void ParseAppxManifest(string manifest_path) {
        var doc = new System.Xml.XmlDocument();
        using (var fs = new FileStream(manifest_path, FileMode.Open, FileAccess.Read, FileShare.Read))
            doc.Load(fs);

        var nsmgr = new System.Xml.XmlNamespaceManager(doc.NameTable);
        nsmgr.AddNamespace("a", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); // default namespace
        nsmgr.AddNamespace("com", "http://schemas.microsoft.com/appx/manifest/com/windows10");

        // detect exported COM servers
        var nodes = doc.SelectNodes("/a:Package/a:Applications/a:Application/a:Extensions/com:Extension/com:ComServer/com:ExeServer/com:Class/@Id", nsmgr);
        foreach (System.Xml.XmlNode node in nodes)
            System.Console.WriteLine("Exported COM CLSID: {0}", node.Value);
    }
}

这确实有点特别,因为它依赖于解析 AppxManifest.xml 文件。不过,它似乎完成了工作。请注意,在沙盒 AppContainer 进程中运行的 UWP 应用程序似乎只有 一些 AppxManifest.xml 文件的读取权限,而不是全部。因此,该代码 仅适用于“常规”Win32 或 .Net 进程

使用示例代码回答自己以查询所有已安装的 COM 服务器,包括“Packaged COM”目录,使用 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;
}