1

I need to bind to a running COM instance (there can be many instances of what I am looking for, so I need to figure out which one is the right one by examinating the Running Object Table) Using CoClassCreate or the new operator with the class is no option for me.

The code I have almost works, except that it is not exception safe.

Is there a way to avoid the automatic Revoke that happens behind the scenes if the exception is thrown like in the code below? Once the application ends, the COM object gets removed from the ROT while the application that registered it is still running. I am pretty sure a Release() gets called automatically and as it was the last object referencing the COM, the ROT entry vanishes.

I would like to get a strong reference to an object in the ROT in an atomic and exception safe way.

All my attempts to use the moniker in combination with the method BindToObject were unsuccessful. Is there a problem with the implementation in .NET 4.0? The first call seems to work but subsequent calls all fail with Argument or invalidCast exception, even while using the Guid of IUnknown. To my knowledge this should always success or am I overlooking something?

Here is the best working code I could produce so far. I just miss the exception safety. The program is intended to run on its own and is interruptible (an exception can be thrown to interrupt the tread at any time). Leaving this unfinished business will cause me for sure some trouble if I cannot solve it cleanly.

    [DllImport("ole32.dll")]
    static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);

    // Returns the contents of the Running Object Table (ROT), where
    // open Microsoft applications and their documents are registered.
    private IEnumerable<T> GetRunningObjects<T>()
    {
        // Get the table.
        IBindCtx bc;
        if (CreateBindCtx(0, out bc) != 0 || bc == null)
            throw new ApplicationException("Can't create COM binding context in GetRunningObjects<T>");

        IRunningObjectTable runningObjectTable;
        bc.GetRunningObjectTable(out runningObjectTable);

        IEnumMoniker monikerEnumerator;
        runningObjectTable.EnumRunning(out monikerEnumerator);
        monikerEnumerator.Reset();

        // Enumerate and fill our nice dictionary.
        IMoniker[] monikers = new IMoniker[1];
        IntPtr numFetched = IntPtr.Zero;
        while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
        {
            object o = null;
            if (runningObjectTable.GetObject(monikers[0], out o) == 0 && o != null)
            {
                if (o is T)
                {
                    // TODO: If an exception is thrown here, then COM object gets revoked from ROT...
                    throw new Exception("Test");

                    // monikers[0].BindToObject does not work...
                    // there seems to be a weakness here.

                    // if found, create a strong reference to avoid auto-revokation in the ROT
                    IntPtr ptr = Marshal.GetIUnknownForObject(o);
                    Marshal.AddRef(ptr);

                    yield return (T)o;
                }
            }
        }
        Marshal.ReleaseComObject(bc);
    }

Additional note: This probably happens when the OutProc server registers the active object as a weak reference only. I have no influence on this, I am not the author of the server side... I need to bind to the running object without having the risk that the object can get revoked from the Running Object Table.

jdehaan
  • 19,700
  • 6
  • 57
  • 97
  • Throwing exception in your code shouldn't affect the ROT directly. However in your sample, if an exception is raised, all the COM references you use are still alive, waiting for next GC to happen. Have you tried to put try/finally(MarshalReleaseComObject) around your calls? If the program's supposed to run forever, you must do this. – Simon Mourier Mar 03 '13 at 10:33
  • Thanks for the hint, but this method uses 'yield' in C# for which the C# compiler automatically does the necessary stuff (also tested in debugger: the `Marshal.ReleaseComObject` is called on disposition of the iterator.). To be more specific I have this problem with IBM DOORS client objects. It seems to be due on how the server registers the object in the ROT, is there a way for me to find out what were the parameters of the registration for an object? – jdehaan Mar 03 '13 at 15:46
  • The yield will call Dispose on the enumerator, but not on all objects you get a reference in .NET if you don't put try/finally blocs around. Also, why don't you put a try/catch in your if (o is T) statement? – Simon Mourier Mar 03 '13 at 18:22
  • Ok, I'll improve this, but the problem I have is that there is one Resource that gets released once too often not the reverse :-) Is the contract for `IRunningObjectTable.GetObject` so that I need to release the object? For `IMoniker.BindToObject` I am sure it's the case as a `QueryInterface` is called behind the scenes. But I cannot manage to make it work. – jdehaan Mar 03 '13 at 18:43
  • 1
    Yes, GetObject does an AddRef, see here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms683841(v=vs.85).aspx – Simon Mourier Mar 05 '13 at 09:00
  • OMG... how could I overlook this doc. Thanks a lot at least this is clear now. I also figured out after looking for a while that the IRotViewer shows me information about the running table without getting the COM object I am interested into evicted. My own ROT viewer I implemented for demo purposes exposes this issue, using the ComTypes excepted for the binding context. I am on vacation from now on, ... After that I'll implement using PInvoke to bypass eventual wrapping stuff that is not visible to me and see if there is any difference. – jdehaan Mar 05 '13 at 16:28

0 Answers0