C# COM object not being released

T

techie

I have a VC++ client that creates a COM object that is implemented in C#.
The problem is the COM object doesn't get released, so I get a memory leak.

I have no problems creating the COM object and getting a pointer to the
required interface (IATImportFilter) in the client:

COleDispatchDriver* resultInterface = new COleDispatchDriver;
if ( resultInterface == NULL ) return FALSE;
COleException oOleException;
BOOL success = resultInterface->CreateDispatch( progID, &oOleException );

IUnknown *p;
if ( resultInterface->m_lpDispatch->QueryInterface( *pRequiredInterface,
(void**)&p ) != S_OK )

where prodID ("SI.ValidatorImport") is the name of COM object.



My C# COM object class is declared like this:

[ClassInterface(ClassInterfaceType.None)]
[ProgId("SI.ValidatorImport")]
public class ValidatorImport : IATImportFilter
{

The object is being released in the client like this:

((IUnknown*) pIF)->Release();
pIF = NULL;

The call to Release() on the interface does not actually destroy the object.
It doesn't call the destructor of my COM object. I tried putting a
breakpoint in there and even added a MessageBox() call but the program just
doesn't go in there.

Not sure why this happening. Since every interface supports IUnkown,
Release() should release the COM object. The COM object is only created
once.

I'm new to C# so I would appreciate some help.
 
M

Mattias Sjögren

The call to Release() on the interface does not actually destroy the object.
It doesn't call the destructor of my COM object.

Since it's a managed object it will eventually be cleaned up by the
garbage collector.



Mattias
 
S

Steve Walker

techie said:
The call to Release() on the interface does not actually destroy the object.
It doesn't call the destructor of my COM object. I tried putting a
breakpoint in there and even added a MessageBox() call but the program just
doesn't go in there.

Not sure why this happening. Since every interface supports IUnkown,
Release() should release the COM object. The COM object is only created
once.

As I understand it, when you call Release(), you're calling it on a
COM-callable wrapper, not on the object itself. The COM callable wrapper
reference counts as you would expect a COM object to. When all
references are released, it releases its own reference to the object you
wrote in C#, which is then available for garbage collection. This will
happen at some point, but not necessarily when you expect it to. So, you
don't get deterministic finalisation of your C# object. You *may* get DF
of the wrapper, the documentation I've seen isn't clear on whether it is
destroyed immediately you are done with it It's pinned on a non GC'd
heap, again AIUI, so presumably it is destroyed immediately in the
normal COM fashion.

You probably don't have a memory leak, you more than likely have an
unreferenced object sat waiting to be GC'd.
 
T

techie

Steve Walker said:
You probably don't have a memory leak, you more than likely have an
unreferenced object sat waiting to be GC'd.

Is there any I can force GC? Maybe if my class also implements IDisposable
I can call dispose from my client?
 
T

techie

Mattias Sjögren said:
Since it's a managed object it will eventually be cleaned up by the
garbage collector.

The call to Release() on my interface pointer returns 0 - the reference
count on the interface - which indicates the COM object should have been
released or at least queued to be released by the GC. When VC++ debugger
halts it reports a lot of memory leaks which worries me.

Can I assume the COM object is released by the GC eventually? It must
happen some time after my VC++ client closes. Is there any way of forcing
the GC to delete my object immediately from my client?
 
M

Mattias Sjögren

The call to Release() on my interface pointer returns 0 - the reference
count on the interface - which indicates the COM object should have been
released or at least queued to be released by the GC. When VC++ debugger
halts it reports a lot of memory leaks which worries me.

The COM callable wrapper will be deleted, but the managed object is
just eligible for GC.

Can I assume the COM object is released by the GC eventually?
Yes


It must happen some time after my VC++ client closes.

It will happen the next time GC runs, or at process shutdown if the
executable calls CorExitProcess.

Is there any way of forcing
the GC to delete my object immediately from my client?

Yes, with the GC class, but that doesn't mean it's a good idea to do
so.


And implementing IDisposable as you suggested in the other thread will
not affect GC, only let have deterministic cleanup of unmanaged
resources.



Mattias
 
T

techie

My VC++ 6 client doesn't make any GC.Collect() call. Its legacy code and I
don't want to change it.

I put a breakpoint in the destructor of my C# COM object class. I then ran
the C# project with the Start Application set to my VC++ client. At no
point did the program ever break into my breakpoint which implies the object
never got deleted. Why is that? Doesn't the GC run every now and then?
Does it only run when the memory usage of the machine reaches a high level?

In this particular solution, my C# COM object will be created via a C++ COM
object. A C# web service will create the C++ COM object. After I'm
finished with the COM object I can call GC.Collect() which will force
garbage collection. I'm just concerned my web server will run out of
memory. This is an important project and there are going to be a large
number of users.
 
W

Willy Denoyette [MVP]

Object "Finalizers" (also called destructor) are executed by/on the
finalizer thread, at some point in time, after the GC has run for the second
time after the object became eligible for collecting.
Note however that when your object has been instantiated in a STA thread,
this thread needs to pump the window message queue, failing to do so will
block the finalizer thread.
So first thing you should do is; check the apartment you are running in, if
it's a STA change it to MTA, and make sure your object is thread safe.

Willy.
 
T

techie

My VC++ 6 client is a normal .exe application. No MTA here. Could this be
reason for my memory leak?

However, my C++ COM object, which can be created from a ASP.NET web page, is
MTA. This COM object in turn will create my new C# object. I will test if
the destructor is called in this case.
 
W

Willy Denoyette [MVP]

Even 'normal' applications must initialize the thread to enter an apartment
before they can create COM instances and use their interfcaces. Question
is - what do you mean with a normal exe? Does it initialize the thread by
calling CoInitialize(Ex) to enter an STA or an MTA? And more importantly,
does it pump messages (all windows programs pump the message queue as part
of their UI message loop, console style applications don't pump however).

I'm not sure what you mean with your C++ object is MTA, COM objects can have
a 'threadingmodel' apartment, both and free, if no threadingmode is
specified the object lives in an OLE supplied host apartment.
'apartment' requires a STA to live in, 'both' can live in a STA as well as a
MTA while 'free' requires a MTA.
MTA initialized threads don't need to pump messages.

If your C++ COM is marked 'apartment' in the registry it must run in a
asp.net application with <%@ page aspcompat=true %> attribute set.

Willy.
 
S

Steve Walker

techie said:
Is there any I can force GC?

You can, but you probably shouldn't. The algorithm for garbage
collection itself is well documented. I can't find similar documentation
for the *scheduling* of garbage collection, but I get the impression
from its behaviour that it works on the principle that when there is
plenty of memory still free, CPU cycles should not be wasted on
recovering what has been used.
 
T

techie

I tried changing the 'normal' AfxOleInit() call to CoInitializeEx( NULL,
COINIT_MULTITHREADED) but it made no difference.

Coming from a C++ background I am used to all allocated memory being
released when an application closes down. If my main application is in C# I
can call Dispose() on the object but I can't in this case.
 
W

Willy Denoyette [MVP]

It's getting confusing, it would suggest you explain in detail your exact
scenario.
I see you called AfxOleInit() initially, that means the application is a MFC
application right? But what kind of application is it, a windows application
or something else, or simply put - does it have a UI?
If it has a UI you MUST not change the threads apartment to MTA, you must
keep the call to AfxOleInit.

Second, what kind of COM object are you creating from this C++ application;
1- a native C++ COM object, or
3- a native COM object creating a .NET COM object, or
2- a .NET COM object .

If it's 1 or 2, and the native COM object's 'threadingmodel' is set to
'Apartment', the object will be created on a STA thread no matter how you
initialize the (main) thread. And that thread must pump the message queue,
that's why I asked if it's a windows type client (having a UI). In case 2,
both objects will be created in the same apartment - so it's the native COM
objects who determines the apartment type.
In case 3, the 'COM' object will live in the creators apartment irrespective
it's type, but I can't stress it enough, - STA threads must pump messages,
and UI threads do have a message pump, non UI threads don't!

Note that in .NET the memory will also be released when the application
closes down.
I'm also not clear why you have a destructor defined for your "COM" class,
what is it used for?
A general rule is to avoid finalizers, unless your object is disposable
(implements IDisposable) and the destructor is only used as a safety net.

Willy.
 
T

techie

The main application is an MFC Windows application with a UI. The main MFC
application creates a COM object. The COM object is a C# .NET COM object.
Therefore, my COM object is a STA thread? How do I get my COM object to
pump the message queue?
 
J

J.Marsch

It sounds to me as if a quick primer on how the garbage collector works
might be useful to you. Here's a pretty good article:
http://msdn.microsoft.com/msdnmag/issues/1100/gci/

In general:
Avoid calling GC.Collect() in production code -- trying to manually control
the GC will usually do more harm than good. (GC.Collect() can be useful in
testing scenarios, though).

Avoid using a Finalizer (destructor in C#) -- only use them if you need to
release some kind of unmanaged resource, like a handle.

IDisposable probably isn't what you think it is. You use it to provide a
deterministic way to call the finalizer (but you probably don't want to use
a finalizer).

Here's some additional reading:
http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=88e62cdf-5919-4ac7-bc33-20c06ae539ae

A whole catalog of links:
http://blogs.msdn.com/clyon/archive/2004/09/14/229477.aspx
 
W

Willy Denoyette [MVP]

techie said:
The main application is an MFC Windows application with a UI. The main
MFC
application creates a COM object. The COM object is a C# .NET COM object.
Therefore, my COM object is a STA thread? How do I get my COM object to
pump the message queue?

Ok, its a MFC windows application creating a .NET COM object.
1. You don't have to pump messages, like I said this is handled by the UI
thread.
2. You COM object will live in the same STA thread as the UI.
3. COM objects are released when you call Release() on their inferfaces.
4. The .NET object will be collected when the GC runs (for objects without
destructors), or when the finalizer runs (for objects that have destructors
but not implementing IDisposable). Note the the finalizer runs on a
finalizable object after the second sweep of the GC. If this object is the
sole .NET COM object in the process chances are that the GC never runs, so
the finalizer will not run either, in that case the finalizer will run when
the process shuts down.
So I suggest you apply the Disposing pattern and remove the finalizer
(destructor).

Willy.
 
T

techie

Can you please check my code for the Disposing pattern. I'm still getting a
memory leak. Dispose nevers gets called. I've removed the destructor
(~Import).

[ClassInterface(ClassInterfaceType.None)]
[ProgId("SI.Import")]
public class Import :IImportFilter, IDisposable
{
Session m_Session;
Content m_Content;
bool disposed = false;
..
..
public void Dispose()
{
Dispose(true);
}

protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
ComHelper.SafeReleaseComObject(m_Content);
ComHelper.SafeReleaseComObject(m_Session);
}
disposed = true;
}
}

}

There are two unmanaged COM objects that need to be destroyed.
..
 
W

Willy Denoyette [MVP]

The CCW will not call Dispose() automatically when it's ref. count reaches
0, your client has to call Dispose() explicitly just like any other managed
client should do, I guess you don't call Dispose() from the client.
Also note that calling Dispose won't free the "Import" 'managed COM'
object, this is done at the next GC run (non-deterministically), I hope this
is not the 'memory leak' your are referring to - this is not a memory leak
but the normal behavior.
I also suppose that SafeReleaseComObject is used to eagerly release other
unmanaged resources, if not you shouldn't implement IDisposable at all.

Willy.

techie said:
Can you please check my code for the Disposing pattern. I'm still getting
a
memory leak. Dispose nevers gets called. I've removed the destructor
(~Import).

[ClassInterface(ClassInterfaceType.None)]
[ProgId("SI.Import")]
public class Import :IImportFilter, IDisposable
{
Session m_Session;
Content m_Content;
bool disposed = false;
..
..
public void Dispose()
{
Dispose(true);
}

protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
ComHelper.SafeReleaseComObject(m_Content);
ComHelper.SafeReleaseComObject(m_Session);
}
disposed = true;
}
}

}

There are two unmanaged COM objects that need to be destroyed.
.

Willy Denoyette said:
Ok, its a MFC windows application creating a .NET COM object.
1. You don't have to pump messages, like I said this is handled by the UI
thread.
2. You COM object will live in the same STA thread as the UI.
3. COM objects are released when you call Release() on their inferfaces.
4. The .NET object will be collected when the GC runs (for objects
without
destructors), or when the finalizer runs (for objects that have destructors
but not implementing IDisposable). Note the the finalizer runs on a
finalizable object after the second sweep of the GC. If this object is
the
sole .NET COM object in the process chances are that the GC never runs,
so
the finalizer will not run either, in that case the finalizer will run when
the process shuts down.
So I suggest you apply the Disposing pattern and remove the finalizer
(destructor).

Willy.
 
T

techie

OK, I will call Dispose() from my MFC client. Thanks for your help so far.
Is actually possible to QueryInterface for IDisposable?

If I have an ASP.NET application, instead of my MFC client, that creates a
C++ COM object which in turn creates my C# COM object do I have to implement
the IDisposable interface? Isn't this necessary for the GC to destroy all
unreferenced objects? Or do I just call Dispose() explicitly like in the
MFC client?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top