1

I'm working a project that uses API from a third party COM Server. The COM Server is a local server (out of process exe) on which I have no control.

I'm trying to access COM objects from the runnin object table to choose between the several instances of COM objects started with each instance of the application :

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);
        }
    }
}

If I start two instances of the target application, the ROT dump shows two instances of the corresponding COM object (named IApplication here) as GetDisplayName shows the right clsid for the interface IApplication registered in the registry.

The problem is that the objects I get from Rot.GetObject are described as System.__ComObject and cannot be cast to IApplication (InvalidCastException because QueryInterface failed with E_NOINTERFACE) even if their moniker describe the right clsid...

I tried casting it progammatically to every available type in my project just to see but the only success is casting it to System.__ComObject...

I also tried using IMoniker.BindToObject instead of Rot.GetObject but this time, I get a FileNotFound Exception when I provide the corresponding interface GUID. BindToObject works when I provide the riid for IUnknown but it gives me a System.__ComObject I can't cast (back to square one !).

For information, in the ROT dump, I can also shows a file moniker corresponding to an open project in the target app but I cannot create a COM object from this either.

Anyone has an idea about how to correctly retrieve the objects from the ROT ?

Thanks, Regards.

EDIT :

After a few days out and a new eye on this problem, I discovered the CLSID in the moniker's display name is not the exact same one I want but has two digits off (the indexof test turns out to be wrong).

After carefully reading the clsid's it turns out that the clsid in the moniker's displayname is the clsid of the coclass of IApplication and not the clsid of IApplication.

I tried casting the object to "ApplicationClass" (the aforedmentionned coclass) but this gives me an exception. The additional info I get (in french) could translate as follows : Impossible to cast __ComObject wrapper instances to another class but it's possible to cast these instances to interfaces as long as the underlying com component supports QueryInterface calls for the interface IID.

Any idea on how to proceed from here ?

nrdev
  • 49
  • 1
  • 4
  • The code is odd, but if you make it past the IndexOf() check then you must have found the right object. Which leaves but few reasons for E_NOINTERFACE. You either have a versioning problem, using the wrong definition of IApplication. Or the proxy/stub for the server is not registered properly. You can verify the latter with Regedit.exe, navigate to HKLM\Software\Wow6432Node\Classes\Interface key and locate the typeof(IApplication).Guid value. With the assumption that this is 32-bit code, that could be a mismatch too. Asking for assistance from the server author is highly recommended. – Hans Passant Jun 03 '17 at 10:24
  • Thanks for your answer, unfortunately, everything in the registry seems to check out. I tried to change the app's referenced assembly to the typelib provided in the proxy/stub entry in the registry but it doesn't change the result. For the IndexOf, it's because the DisplayName of the moniker is not only the plain CSLID but looks like "CLSID : {000...}" and I didn't want to depend on the possible other forms of DisplayName. – nrdev Jun 08 '17 at 16:48

1 Answers1

0

I don't now if you still have a problem but since there is no answer yet I want to share what I've done after couple of weeks research about handling multiple COM Interfaces on ROT.

I've fetched all the objects from ROT and store them in hashtable to ensure uniqueness via Key/Value but you can adjust it to a List as you prefer on question. You just need to fetch running object value to add your List.

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;
}

After that you can cast the object stored in your List to your COM Interface. For instance, I've also shared my way which works for Rhapsody COM Interface in below;

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..
}
Ardahan Kisbet
  • 641
  • 1
  • 13
  • 33