Lifetime issue of IDisposable unmanaged resources in a complex object graph?
- by stakx
This question is about dealing with unmanaged resources (COM interop) and making sure there won't be any resource leaks. I'd appreciate feedback on whether I seem to do things the right way.
Background:
Let's say I've got two classes:
A class LimitedComResource which is a wrapper around a COM object (received via some API). There can only be a limited number of those COM objects, therefore my class implements the IDisposable interface which will be responsible for releasing a COM object when it's no longer needed.
Objects of another type ManagedObject are temporarily created to perform some work on a LimitedComResource. They are not IDisposable.
To summarize the above in a diagram, my classes might look like this:
+---------------+ +--------------------+
| ManagedObject | <>------> | LimitedComResource |
+---------------+ +--------------------+
|
o IDisposable
(I'll provide example code for these two classes in just a moment.)
Question:
Since my temporary ManagedObject objects are not disposable, I obviously have no control over how long they'll be around. However, in the meantime I might have Disposed the LimitedComObject that a ManagedObject is referring to. How can I make sure that a ManagedObject won't access a LimitedComResource that's no longer there?
+---------------+ +--------------------+
| managedObject | <>------> | (dead object) |
+---------------+ +--------------------+
I've currently implemented this with a mix of weak references and a flag in LimitedResource which signals whether an object has already been disposed. Is there any better way?
Example code (what I've currently got):
LimitedComResource:
class LimitedComResource : IDisposable
{
private readonly IUnknown comObject; // <-- set in constructor
...
void Dispose(bool notFromFinalizer)
{
if (!this.isDisposed)
{
Marshal.FinalReleaseComObject(comObject);
}
this.isDisposed = true;
}
internal bool isDisposed = false;
}
ManagedObject:
class ManagedObject
{
private readonly WeakReference limitedComResource; // <-- set in constructor
...
public void DoSomeWork()
{
if (!limitedComResource.IsAlive())
{
throw new ObjectDisposedException();
// ^^^^^^^^^^^^^^^^^^^^^^^
// is there a more suitable exception class?
}
var ur = (LimitedComResource)limitedComResource.Target;
if (ur.isDisposed)
{
throw new ObjectDisposedException();
}
... // <-- do something sensible here!
}
}