I have rephrased this question.
When .net objects are exposed to COM Clients through COM iterop, a CCW (COM Callable Wrapper) is created, this sits between
To my knowledge, the GC already provides support for what you are trying to do. It is called Finalization. In a purely managed world, best practice is to avoid Finalization, as it has some side effects that can negatively impact the performance and operation of the GC. The IDisposable interface provides a clean, managed way of bypassing object finalization and providing cleanup of both managed and unmanaged resources from within managed code.
In your case, you need to initiate cleanup of a managed resource once all unmanaged references have been released. Finalization should excel at solving your problem here. The GC will finalize an object, always, if a finalizer is present, regardless of how the last references to a finalizable object were released. If you implement a finalizer on your .NET type (just implement a destructor), then the GC will place it in the finalization queue. Once the GC collection cycle is complete, it will process the finalization queue. Any cleanup work you perform in your destructor will occur once the finalization queue is processed.
It should be noted that if your finalizable .NET type contains references to other .NET objects which in turn require finalization, you could invoke a lengthy GC collection, or some of the objects may survive for longer than they would without finalization (which would mean they survive a collection and reach the next generation, which is collected less frequently.) However, if the cleanup work of your .NET objects that use CCW's is not time sensitive in any fashion, and memory usage is not a huge issue, some extra lifetime shouldn't matter. It should be noted that finalizable objects should be created with care, and minimizing or eliminating any class instance level references to other objects can improve your overall memory management via the GC.
You can read more about finalization in this article: http://msdn.microsoft.com/en-us/magazine/bb985010.aspx. While it is a rather old article from back when .NET 1.0 was first released, the fundamental architecture of the GC is unchanged as of yet (the first significant changes to the GC will be arriving with .NET 4.0, however they are related more to concurrent GC execution without freezing the application threads than changes to its fundamental operation.)
.Net framework works differently, see:
The .NET Framework provides memory management techniques that differ from the way memory management worked in a COM-based world. The memory management in COM was through reference counting. .NET provides an automatic memory management technique that involves reference tracing. In this article, we'll take a look at the garbage collection technique used by the Common Language Runtime CLR.
nothing to be done
[EDITED] more one round...
Take a look at this alternative Importing a Type Library as an Assembly
As you yourself said using CCW you can access reference-counte in traditional COM fashion.
[EDITED] Persistence is a virtue
You know WinAPIOverride32? With it you can capture and study how it works.
Another tool that can help is Deviare COM Spy Console.
This will not be easy.
Good luck.
As far as I'm aware, the best coverage of this subject is in the book The .NET and COM Interoperability Handbook By Alan Gordon, and that link should go to the relevant page in Google Books. (Unfortunately I don't have it, I went for the Troelsen book instead.)
The guidance there implies that there isn't a well-defined way of hooking into the Release
/reference counting in the CCW. Instead the suggestion is that you make your C# class disposable, and encourage your COM clients (in your case the VBScript authors) to call Dispose
when they want deterministic finalisation to occur.
But happily there is a loophole for you because your clients are late-binding COM clients, because VBScript uses IDispatch
to make all calls to objects.
Suppose your C# classes were exposed via COM. Get that working first.
Now in ATL/C++ create a wrapper class, using the ATL Simple Object wizard, and in the options page choose Interface: Custom instead of Dual. This stops the wizard putting in its own IDispatch
support.
In the class's constructor, use CoCreateInstance to magic up an instance of your C# class. Query it for IDispatch
and hold onto that pointer in a member.
Add IDispatch
to the wrapper class's inheritance list, and forward all four methods of IDispatch
straight through to the pointer you stashed away in the constructor.
In the FinalRelease
of the wrapper, use the late binding technique (Invoke
) to call the Dispose
method of the C# object, as described in the Alan Gordon book (on the pages I linked to above).
So now your VBScript clients are talking via the CCW to the C# class, but you get to intercept the final release and forward it to the Dispose
method.
Make your ATL library expose a separate wrapper for each "real" C# class. You'll probably want to use inheritance or templates to get good code reuse here. Each C# class you support should only require a couple of lines in the ATL wrapping code.
Why don't shift paradigm. What about to create your own aggregate around exposed and extend with notification methods. It even can be done in .Net not only by ATL.
EDITED: Here is some link that may be describe some another way(http://msdn.microsoft.com/en-us/library/aa719740(VS.71).aspx). But following steps explains my idea above.
Create create new .Net class that implements your legacy interface(ILegacy), and new interface (ISendNotify) with single method:
interface ISendNotify
{
void SetOnDestroy(IMyListener );
}
class MyWrapper : ILegacy, ISendNotify, IDisposable{ ...
Inside the MyClass create instance of your real legacy object, and delegate all calls from MyClass to this instance. This is an aggregation. So lifetime of aggregate now depends on MyClass. Since MyClass is a IDisposable now you can intercept when instance is deleted, so you can send notification by IMyListener
EDIT2: Taken there (http://vb.mvps.org/hardcore/html/countingreferences.htm) simplest impl of IUnknown with sending event
Class MyRewritten
...
Implements IUnknown
Implements ILegacy
...
Sub IUnknown_AddRef()
c = c + 1
End Sub
Sub IUnknown_Release()
c = c - 1
If c = 0 Then
RaiseEvent Me.Class_Terminate
Erase Me
End If
End Sub