0

I have a C# assembly that is COM-visible. It in turn communicates with a 3rd-party COM application. My problem is that when I release the reference to my C# assembly from VBA, the C# assembly keeps the 3-rd party application open.

' Call assembly from VBA
Dim asm : Set asm = CreateObject("MyCSharpAssembly") 

' Get a managed object exposed by the assembly
' managedObject communicates with a 3-rd party COM application
Dim managedObject : Set managedObject = asm.GetManagedObject()

' When I release managedObject from VBA, the 3rd party application stays open.
Set managedObject = Nothing

I have tried implementing the IDisposable pattern in the managedObject and explicitly release the COM object. That works, but I still have to explicitly call the Dispose method from VBA. Just setting the reference to managedObject to Nothing is not enough. This is easily forgotten by VBA coders, who may assume setting the object to Nothing is enough.

Is there any way I can code around that from the C# perspective, or do I have to stick with the explicit Dispose?

Rno
  • 784
  • 1
  • 6
  • 16
  • Your VBA code makes the C# object eligible for garbage collection. Which, if that happens, also finalizes the RCWs that it uses to talk to the 3rd party component. The "if that happens" clause is the rub, the garbage collector only runs when it needs to. You'd have to help with, say, another C# object that exposes a method that calls GC.Collect(). – Hans Passant Mar 07 '18 at 00:14
  • There is a desperado approach in [this answer](https://stackoverflow.com/a/6905172/17034), no real idea how much trouble that could cause. The typical problem doing this is that it is hard to find all interface references and missing just one of them is an undebuggable fail whale. Otherwise the reason why GC.Collect is better than Marshal.ReleaseComObject. – Hans Passant Mar 07 '18 at 00:20
  • Thank you for your input Hans. Have been reading up on this issue including the article your suggested (and articles related) for a day or two now, and my head is buzzing. A lot of their content is above for my expertise level. However, from you first comment I got that a destructor (finalizer) is not called when an object is dereferenced in VBA - and that that is up to the GC. That does not solve my problem but gives me confidence I am not completely crazy. I will stick to my requierement of explicitly 'closing' objects after use for now. – Rno Mar 08 '18 at 23:05

1 Answers1

-1

You say you implemented the disposal pattern but did you implement the full pattern, not just the public Dispose method, in particular the one with the finalizer, e.g. something similar to below?

~ComplexResourceHolder(){  
    Dispose(false);  
}

Dispose pattern

Please note that this is still non-deterministic; the finalizer may run much much later than the point when VBA consumers sets it to Nothing, but at least it will eventually release the object. If you MUST have a deterministic release, then we need to rethink the approach.

One possible but simplistic option that may simplify the operation is to only take reference to the 3rd party COM component within each of your public method and release it at the end. That might work as long the typical usage don't normally call several methods on same object in succession.

The other simple option is to allow the VBA consumer assign the 3rd party COM object to the managed object so VBA can then manage the lifetime of that component, rather than the .NET.

There may be other options but they will be quite challenging to implement and won't be as foolproof.

this
  • 1,406
  • 11
  • 23
  • I am confident I implemented the Dispose pattern properly. Thanks for the other suggestions, but they are not an option in the scenario I am trying to develop. – Rno Mar 07 '18 at 12:12