How to implement a dispose pattern in C # when wrapping an Interop COM object?

My class contains an Interop object and calls a method on it that makes it select material. It also provides a method for freeing this stuff, so I expect me to call it in Dispose ():

class MyClass : IDisposable { private DllName.ComClassName comInstance; void SomeMethod() { comInstance = new DllName.ComClassName(); comInstance.AllocStuff(); } public void Dispose() { comInstance.FreeThatStuff(); } } 

Now I have to expand all this to follow the Dispose pattern. I have no other one-time or unmanaged resources for release, so assuming that comInstance is managed (isn't that what Interop does, wrap unmanaged resources into managed ones?), I think the template breaks up into:

 public void Dispose() { if (comInstance != null) { comInstance.FreeStuff(); comInstance = null; } } 

What is the leak if I do not explicitly call Dispose () on MyClass instances, which will lead to an incorrect Dispose structure? This means that comInstance must be uncontrollable, and the template breaks up into:

 public void Dispose() { DisposeComInstance(); GC.SuppressFinalize(this); } ~MyClass() { DisposeComInstance(); } private void DisposeComInstance() { if (comInstance != null) { comInstance.FreeStuff(); comInstance = null; } } 

EDIT:

  • To avoid cluttering my class with a full template, can I just close my class?
  • How do I know that ComClassName (and any class in general) is uncontrollable?
+5
c # interop dispose
source share
3 answers

Looks like you almost nailed it. I will return to the template where you have a secure virtual Disposing that takes a boolean parameter indicating whether managed items should be deleted. This way, someone behind you will continue to correctly implement IDisposable .

 public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } ~MyClass() { this.Dispose(false); } protected virtual void Dispose(bool disposing) { // if (disposing) // { // // Managed // } if (comInstance != null) { comInstance.FreeStuff(); comInstance = null; } // base.Dispose(disposing) if required } 
+2
source share

Ultimately, you need a template like this:

 public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ~MyClass() { Dispose(false); } private void Dispose(bool disposing) { if (disposing) { // Dispose of disposable objects here } // Other unmanaged cleanup here which will be called by the finalizer if (comInstance != null) { comInstance.FreeStuff(); comInstance = null; } // Call base dispose if inheriting from IDisposable class. base.Dispose(true); } 

For a great article on why, Implement the IDisposable and Dispose template correctly.

+3
source share

First, I agree that I suggest having a finalizer as a backup, but I try to ensure that it is never called by explicitly calling myClass.Dispose or through "use".

eg.

 var myClass = new MyClass() try { //do stuff } finally { myClass.Dispose(); } 

or

 using (var myClass = new MyClass()) { //do stuff } 

Marshall.ReleaseComObject

If you use a lot of COM objects, I also suggest that you use Mashall.ReleaseComObject (comObj) to explicitly clear RCW links.

So, code like this is offered elsewhere:

 if (comInstance != null) { comInstance.FreeStuff(); comInstance = null; } 

will become:

 if (comInstance != null) { comInstance.FreeStuff(); int count = Marshall.ReleaseComObject(comInstance); if (count != 0) { Debug.Assert(false, "comInstance count = " + count); Marshal.FinalReleaseComObject(comInstance); } comInstance = null; } 

When checking the return value, ReleaseComObject () is not strictly necessary; I like to check it to make sure everything is increasing or decreasing as expected.

Two dot rule

If you decide to use this, something you need to know about is that, for the proper release of your COM objects, code refactoring may be required. In particular, this is what I call a two-dot rule. Any line using COM objects containing 2 points requires close attention. For example,

 var name = myComObject.Address.Name; 

In this statement, we get a reference to the Address COM object, increasing its number of references to RCW, but we have no way to call ReleaseComObject. The best way to do this is to break it (try ... for clarification).

 var address = myComObject.Address; var name = address.Name; MyReleaseComObject(address); 

where MyReleaseComObject is a utility method that completes my check on count and FinalReleaseComObject () on top.

+3
source share

All Articles