Memory leak when using COM library

G

Guest

We have found a memory leak when using a COM library with a C# application.
The leak appears only if the C# application is compiled with the /optimize
flag. It goes away when the C# application is compiled with the /debug flag.
The COM library is coded to fire asynchronous events, which are handled by
the C# application. It is in the firing and handling of these events that
the leak occurs.

Why does the optimized application leak while the debug application does
not?
Is there a fix available?
Is there another way to do asynchronous event handling that will circumvent
this problem?

Attached is a small test case that recreates the problem. The only code of
real interest in the COM library is the event firing code. If the problem
that we see is in the COM library, it would have to be in this piece of code.
However, this code is more or less the output of a wizard, it is pretty
standard stuff. The C# application is also trivial, with the only item of
any real interest being the event handler. Once again, the code has been
pared down to the point of being trivial. Any insights into asynchronous
event handling through an interop assembly is appreciated.

Thanks,
Giovanni Boschi
Sonic Software Corporation
 
G

Guest

I have a simple test driver that reproduces it, but I haven't found a way to
attach files to these posts - if the full test code and library would help
and there's an email address I can send it to, let me know.

Thanks for your help,
G.

This is the C# code that appears to leak:

amespace SonicExample
{
/// <summary>
///
/// </summary>
public class MessageListener : CSLEAKTESTVS6Lib.IJMSMessageListener
{

private int msgCount = 0;

public MessageListener()
{
}
/// <summary>
/// This called by the JMSCom frame work when a mesage is received
/// </summary>
/// <param name="pJMSMessage"></param>
[MTAThread]
public void onMessage(CJMSMessage pJMSMessage)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(pJMSMessage);
pJMSMessage = null;
GC.Collect();
++msgCount;
if ((msgCount % 1000) == 0)
Console.WriteLine ("Message {0}", msgCount);
}
}
}

This is the code, inside the COM library, that registers and fires
callbacks, respectively - this is fairly boilerplate stuff and works
correctly if the COM client is C++, or C# compiled as debug.

// register the callback
if (pListener != NULL)
{
hr = AtlAdvise (thisUnknown,
listenerUnknown,
IID_IJMSMessageListener,
&m_MessageListenerCookie);
}

// The callback routine
// messageDelivery and Fire_onMessage get called repeatedly.
int CJMSTopicSubscriber::messageDelivery()
{
IJMSMessage *jmsMessage = NULL;
HRESULT hr = S_OK;

// It has been verified that jmsMessage does not leak
jmsMessage = new CComObject<CJMSMessage>();
if (jmsMessage != NULL)
{
// If all went well return the newly created JMS COM object
jmsMessage->AddRef();
}
else
{
fprintf (stderr, _T("Failed to create JMS Message COM Object") );
return -1;
}

if (SUCCEEDED(hr))
{
hr = Fire_onMessage(jmsMessage);
if (FAILED(hr))
{
fprintf (stderr, _T("Failed to send asynch message") );
return -1;
}

jmsMessage->Release();
jmsMessage = NULL;
}
else
{
fprintf (stderr, _T("Failed to create COM message wrapper") );
return -1;
}


return 0;
}

// Fire event.
HRESULT Fire_onMessage(IJMSMessage * pMessage)
{
HRESULT ret;
T* pT = static_cast<T*>(this);
int nConnectionIndex;
int nConnections = m_vec.GetSize();

for (nConnectionIndex = 0; nConnectionIndex < nConnections;
nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IJMSMessageListener* pIJMSMessageListener =
reinterpret_cast<IJMSMessageListener*>(sp.p);

if (pIJMSMessageListener != NULL)
ret = pIJMSMessageListener->onMessage(pMessage);
}

return ret;
}






Nicholas Paldino said:
Giovanni,

Can you post the test case?


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Giovanni Boschi said:
We have found a memory leak when using a COM library with a C#
application.
The leak appears only if the C# application is compiled with the /optimize
flag. It goes away when the C# application is compiled with the /debug
flag.
The COM library is coded to fire asynchronous events, which are handled by
the C# application. It is in the firing and handling of these events that
the leak occurs.

Why does the optimized application leak while the debug application does
not?
Is there a fix available?
Is there another way to do asynchronous event handling that will
circumvent
this problem?

Attached is a small test case that recreates the problem. The only code
of
real interest in the COM library is the event firing code. If the problem
that we see is in the COM library, it would have to be in this piece of
code.
However, this code is more or less the output of a wizard, it is pretty
standard stuff. The C# application is also trivial, with the only item of
any real interest being the event handler. Once again, the code has been
pared down to the point of being trivial. Any insights into asynchronous
event handling through an interop assembly is appreciated.

Thanks,
Giovanni Boschi
Sonic Software Corporation
 
W

Willy Denoyette [MVP]

Is this the whole C# program?
[MTAThread]
public void onMessage(CJMSMessage pJMSMessage)

The MTAThread attribute can only be applied to the Main entry of an
executable program.

Please post complete samples that can be compiled and that illustrate the
problem.
Also give us some more details on how you define a leak, what tools have you
used and what kind of memory are you talking about.

Willy.

Giovanni Boschi said:
I have a simple test driver that reproduces it, but I haven't found a way
to
attach files to these posts - if the full test code and library would help
and there's an email address I can send it to, let me know.

Thanks for your help,
G.

This is the C# code that appears to leak:

amespace SonicExample
{
/// <summary>
///
/// </summary>
public class MessageListener : CSLEAKTESTVS6Lib.IJMSMessageListener
{

private int msgCount = 0;

public MessageListener()
{
}
/// <summary>
/// This called by the JMSCom frame work when a mesage is received
/// </summary>
/// <param name="pJMSMessage"></param>
[MTAThread]
public void onMessage(CJMSMessage pJMSMessage)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(pJMSMessage);
pJMSMessage = null;
GC.Collect();
++msgCount;
if ((msgCount % 1000) == 0)
Console.WriteLine ("Message {0}", msgCount);
}
}
}

This is the code, inside the COM library, that registers and fires
callbacks, respectively - this is fairly boilerplate stuff and works
correctly if the COM client is C++, or C# compiled as debug.

// register the callback
if (pListener != NULL)
{
hr = AtlAdvise (thisUnknown,
listenerUnknown,
IID_IJMSMessageListener,
&m_MessageListenerCookie);
}

// The callback routine
// messageDelivery and Fire_onMessage get called repeatedly.
int CJMSTopicSubscriber::messageDelivery()
{
IJMSMessage *jmsMessage = NULL;
HRESULT hr = S_OK;

// It has been verified that jmsMessage does not leak
jmsMessage = new CComObject<CJMSMessage>();
if (jmsMessage != NULL)
{
// If all went well return the newly created JMS COM object
jmsMessage->AddRef();
}
else
{
fprintf (stderr, _T("Failed to create JMS Message COM Object") );
return -1;
}

if (SUCCEEDED(hr))
{
hr = Fire_onMessage(jmsMessage);
if (FAILED(hr))
{
fprintf (stderr, _T("Failed to send asynch message") );
return -1;
}

jmsMessage->Release();
jmsMessage = NULL;
}
else
{
fprintf (stderr, _T("Failed to create COM message wrapper") );
return -1;
}


return 0;
}

// Fire event.
HRESULT Fire_onMessage(IJMSMessage * pMessage)
{
HRESULT ret;
T* pT = static_cast<T*>(this);
int nConnectionIndex;
int nConnections = m_vec.GetSize();

for (nConnectionIndex = 0; nConnectionIndex < nConnections;
nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IJMSMessageListener* pIJMSMessageListener =
reinterpret_cast<IJMSMessageListener*>(sp.p);

if (pIJMSMessageListener != NULL)
ret = pIJMSMessageListener->onMessage(pMessage);
}

return ret;
}






Nicholas Paldino said:
Giovanni,

Can you post the test case?


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Giovanni Boschi said:
We have found a memory leak when using a COM library with a C#
application.
The leak appears only if the C# application is compiled with the
/optimize
flag. It goes away when the C# application is compiled with the /debug
flag.
The COM library is coded to fire asynchronous events, which are handled
by
the C# application. It is in the firing and handling of these events
that
the leak occurs.

Why does the optimized application leak while the debug application
does
not?
Is there a fix available?
Is there another way to do asynchronous event handling that will
circumvent
this problem?

Attached is a small test case that recreates the problem. The only
code
of
real interest in the COM library is the event firing code. If the
problem
that we see is in the COM library, it would have to be in this piece of
code.
However, this code is more or less the output of a wizard, it is pretty
standard stuff. The C# application is also trivial, with the only item
of
any real interest being the event handler. Once again, the code has
been
pared down to the point of being trivial. Any insights into
asynchronous
event handling through an interop assembly is appreciated.

Thanks,
Giovanni Boschi
Sonic Software Corporation
 

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