1
votes

I have an ASP.NET 2.0 application under IIS that has the functionnality of exporting some data to a software called HFM (Oracle Hyperion Financial Management). To perform that export, the .net application uses an API based on COM objects provided by the HFM client (the client is installed on the same machine that the server, etc.)

My problem is that the API provides a method to connect to the HFM server but not to disconnect.
The documentation says that to disconnect, the application must call the method Marshal.ReleaseComObject() on each COM object created. But there are a lot of complex actions performed and I am not able to release all created object.
So my application doesn't disconnect.

I've noticed that when I replace the ASP.NET application dll files (which seems to reinit the objects instanciated by .NET), the application automatically disconnects.

I tried several times to call :

GC.Collect();
GC.WaitForPendingFinalizers();

But the problem remains. I'm looking for a way to be sure that ANY object created, even COM objects are released. I tried with Marshal.FinalReleaseComObject() but it's not better.

As I put a lock() on that section, there is always at most one user on that part so I can even use hardcore techniques to release the COM objects.

Is there a way to know which object or at least the type of the object that has not been released ?

Thanks for your help.

3

3 Answers

2
votes

Can you put a generic wrapper type that implements IDisposable around each of these COM instances?

Then you can call Marshal.FinalReleaseComObject (or loop on Marshal.ReleaseComObject checking the refcount) within the Dispose method, and only instantiate the COM object via a using statement that references your wrapper type.

Another benefit of doing this wrapper trick is that you can use the CLR profiler to inspect which of your type instances haven't yet been disposed, thus answering your last question.

1
votes

But there are a lot of complex actions performed and I am not able to release all created object.

There's your problem.

You're looking for an easy way out. But you're working with COM. The only easy way out you get is when your process/appdomain is shut down.

1
votes

It is absolutely important that you set the variable to Null/Nothing after calling Marshal.ReleaseComObject or Marshal.FinalReleaseComObject.

Dim cn As ADODB.Connection
cn = New ADODB.Connection
cn.Open(_cnnstr)
cn.Execute(sbDML.ToString)
cn.Close()
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(cn)
cn = Nothing '!!!IMPORTANT!!! - Do not remove, MUST be explicitly called!

In addition, I have noted that creating another method helps avoid problems with dealing with local COM objects (variables placed on the stack). For instance, if you have one method that does everything, wrap it within another method, add a call to garbage collection, then call the wrapper.

Public Sub MainMethod
    '...
    Marshal.FinalReleaseComObject(foo)
    foo = Nothing '!!!IMPORTANT!!! - Do not remove, MUST be explicitly called!
End Sub

Public Sub WrapperMethod
    Call MainMethod
    GC.Collect()
End Sub

I believe the extra call, with the creation of an addition stack for the local variables, forces the objects to be fully collected quicker and causes the COM objects to be released fully.

I have been following this pattern for a couple of years now, and I have not had any problems since.