C#:从运行对象表中获取COM对象

时间:2017-06-03 09:31:49

标签: c# com moniker running-object-table

我正在使用来自第三方COM Server的API的项目。 COM服务器是一个本地服务器(进程外exe),我无法控制它。

我试图从runnin对象表中访问COM对象,以便在每个应用程序实例启动的COM对象的几个实例之间进行选择:

private static List<object> GetRunningInstances(string progId) {
    // get Running Object Table ...
    IRunningObjectTable Rot = null;
    GetRunningObjectTable(0, out Rot);
    if (Rot == null)
        return null;

    // get enumerator for ROT entries
    IEnumMoniker monikerEnumerator = null;
    Rot.EnumRunning(out monikerEnumerator);

    if (monikerEnumerator == null) return null;

    monikerEnumerator.Reset();

    List<object> instances = new List<object>();

    IntPtr pNumFetched = new IntPtr();
    IMoniker[] monikers = new IMoniker[1];

    while (monikerEnumerator.Next(1, monikers, pNumFetched) == 0) {
        IBindCtx bindCtx;
        CreateBindCtx(0, out bindCtx);

        if (bindCtx == null) continue;

        Guid clsid = Type.GetTypeFromProgID(progId).GUID;

        string displayName;
        Guid monikerClsid;
        Guid riid = Marshal.GenerateGuidForType(typeof(IApplication));
        object obj;

        monikers[0].GetDisplayName(bindCtx, null, out displayName);
        monikers[0].GetClassID(out monikerClsid);

        if (displayName.IndexOf(clsid.ToString(), StringComparison.OrdinalIgnoreCase) > 0) {
            //monikers[0].BindToObject(bindCtx, null, ref unkid, out obj);
            Rot.GetObject(monikers[0], out obj);
            instances.Add((IApplication)obj);
        }
    }
}

如果我启动目标应用程序的两个实例,则ROT转储显示相应COM对象的两个实例(此处名为IApplication),因为GetDisplayName显示在注册表中注册的接口IApplication的正确clsid。

问题是我从Rot.GetObject获取的对象被描述为System .__ ComObject,并且无法转换为IApplication(InvalidCastException,因为QueryInterface因E_NOINTERFACE而失败),即使它们的名字对象描述了正确的clsid ......

我尝试将它以编程方式投射到我项目中的每个可用类型,只是为了查看,但唯一的成功是将其转换为System .__ ComObject ...

我也尝试使用IMoniker.BindToObject而不是Rot.GetObject,但这一次,当我提供相应的接口GUID时,我得到一个FileNotFound异常。当我为IUnknown提供riid时BindToObject工作,但它给了我一个System .__ ComObject我不能演员(回到原点!)。

有关信息,在ROT转储中,我还可以显示与目标应用程序中的打开项目对应的文件名字对象,但我也无法从此创建COM对象。

任何人都知道如何从ROT中正确检索对象?

谢谢, 问候。

编辑:

经过几天的关注并重新审视这个问题后,我发现名字的显示名称中的CLSID与我想要的完全相同但是有两位数字(测试索引结果是错)。

在仔细阅读了clsid后,事实证明,名字对象的显示名称中的clsid是IApplication的coclass的clsid,而不是IApplication的clsid。

我尝试将对象转换为&#34; ApplicationClass&#34; (前面提到的coclass)但这给了我一个例外。我得到的额外信息(法语)可以翻译如下:不可能将__ComObject包装器实例转换为另一个类,但只要底层的com组件支持接口IID的QueryInterface调用,它就可以将这些实例转换为接口

关于如何从这里开始的任何想法?

1 个答案:

答案 0 :(得分:0)

如果您仍然有问题,我现在就不知道了,但是既然还没有答案,我想分享一下我在处理ROT上的多个COM接口的几周研究之后所做的工作。

我已经从ROT中获取了所有对象,并将它们存储在哈希表中,以确保通过键/值实现唯一性,但是您可以根据需要将其调整为列表。 您只需要获取运行对象的值即可添加列表。

public static Hashtable GetRunningObjectTable()
{
    Hashtable result = new Hashtable();

    IntPtr numFetched = new IntPtr();
    IRunningObjectTable runningObjectTable;
    IEnumMoniker monikerEnumerator;
    IMoniker[] monikers = new IMoniker[1];

    GetRunningObjectTable(0, out runningObjectTable);
    runningObjectTable.EnumRunning(out monikerEnumerator);
    monikerEnumerator.Reset();

    while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
    {
        IBindCtx ctx;
        CreateBindCtx(0, out ctx);

        string runningObjectName;
        monikers[0].GetDisplayName(ctx, null, out runningObjectName);

        object runningObjectVal;
        runningObjectTable.GetObject(monikers[0], out runningObjectVal);

        result[runningObjectName] = runningObjectVal;
    }

    return result;
}

之后,您可以将存储在列表中的对象转换为COM接口。例如,下面我还分享了适用于Rhapsody COM接口的方式;

Hashtable runningObjects = GetRunningObjectTable();
IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator();
while (rotEnumerator.MoveNext())
{
    string candidateName = (string)rotEnumerator.Key;
    if (!candidateName.StartsWith("Rhapsody"))
        continue;

    rhapsody.RPApplication app = rotEnumerator.Value as rhapsody.RPApplication;

    if (app == null)
        continue;

    // Do your stuff app (com object) in here..
}