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.