?
Solved

Memory Leak

Posted on 2003-12-07
6
Medium Priority
?
1,620 Views
Last Modified: 2013-12-14
Hello all,
   I'm having a problem with a memory leak in a COM object that I can not identify.  From a VB app I create a new instance of the Request object and set a value to the SubjectCompanyName property.  That is the extent of the test application.  After VB6.exe is closed the following memory leaks appear.  When attempting to declare a CRequest::~CRequest() destructor the compiler generates an error indicating that the destructor already has a body.  So a couple of questions come to mind

1. Can I be assured that any constructor code that allocates memory will be freed by the destructor even though I can not define it?
2. How do I resolve this memory leak?

Info: AfxDllCanUnloadNow returning S_OK
Info: AfxDllCanUnloadNow returning S_OK
Info: AfxDllCanUnloadNow returning S_OK
Info: AfxDllCanUnloadNow returning S_OK
Info: AfxDllCanUnloadNow returning S_OK
Info: AfxDllCanUnloadNow returning S_OK
Detected memory leaks!
Dumping objects ->
{78} normal block at 0x03814F10, 12 bytes long.
 Data: <Tx          > 54 78 1C 00 00 00 00 00 01 00 00 00
{77} normal block at 0x03814EC8, 12 bytes long.
 Data: <,x          > 2C 78 1C 00 00 00 00 00 01 00 00 00
{76} normal block at 0x03814E80, 12 bytes long.
 Data: < x          > 04 78 1C 00 00 00 00 00 01 00 00 00
{75} normal block at 0x03814E38, 12 bytes long.
 Data: <4           > 34 CD 1B 00 00 00 00 00 01 00 00 00
{74} normal block at 0x03814DF0, 12 bytes long.
 Data: <            > EC A7 1B 00 00 00 00 00 01 00 00 00
{73} normal block at 0x03814DA8, 12 bytes long.
 Data: <            > EC D6 1B 00 00 00 00 00 01 00 00 00
{72} normal block at 0x03814D60, 12 bytes long.
 Data: <            > FC 17 19 00 00 00 00 00 01 00 00 00
{71} normal block at 0x03814CF8, 40 bytes long.
 Data: <  s   s     `M  > F0 BA 73 03 8C BA 73 03 00 00 00 00 60 4D 81 03
{70} normal block at 0x03814CB0, 12 bytes long.
 Data: < L   L      > B0 4C 81 03 B0 4C 81 03 CD CD CD CD
{69} normal block at 0x03814C58, 28 bytes long.
 Data: <  s D s         > 80 BB 73 03 44 BB 73 03 00 00 00 00 CC CD CD CD
{68} normal block at 0x03814C10, 12 bytes long.
 Data: < L   L      > 10 4C 81 03 10 4C 81 03 CD CD CD CD
{67} normal block at 0x03814BB8, 28 bytes long.
 Data: <  s D s         > 80 BB 73 03 44 BB 73 03 00 00 00 00 CC CD CD CD
Object dump complete.
The thread 0xFC8 has exited with code 0 (0x0).
The thread 0xB04 has exited with code 0 (0x0).
The thread 0xFE8 has exited with code 0 (0x0).
The thread 0xFB4 has exited with code 0 (0x0).
The program 'C:\Program Files\Microsoft Visual Studio\VB98\VB6.EXE' has exited with code 0 (0x0).

**********************************************************************************
**********************************************************************************

Below is the code


*************************************
Request.cpp
*************************************
CRequest::CRequest()
{
      CComObject<CAttributes>::CreateInstance(&m_SubjectAttributes);
      CComObject<CAddress>::CreateInstance(&m_SubjectAddress);
}

STDMETHODIMP CRequest::get_SubjectCompanyName(BSTR *pVal)
{
      //*pVal = m_SubjectCompanyName.copy();
      *pVal = m_SubjectCompanyName;

      return S_OK;
}

************************************
Request.h
************************************
class ATL_NO_VTABLE CRequest :
      public CComObjectRootEx<CComSingleThreadModel>,
      public CComCoClass<CRequest, &CLSID_Request>,
      public ISupportErrorInfo,
      public IDispatchImpl<IRequest, &IID_IRequest, &LIBID_Client>
{
public:
      CRequest();


DECLARE_REGISTRY_RESOURCEID(IDR_REQUEST)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CRequest)
      COM_INTERFACE_ENTRY(IRequest)
      COM_INTERFACE_ENTRY(IDispatch)
      COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()

// ISupportsErrorInfo
      STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IRequest
public:
      STDMETHOD(get_SubjectCompanyName)(/*[out, retval]*/ BSTR *pVal);
      STDMETHOD(put_SubjectCompanyName)(/*[in]*/ BSTR newVal);
      
private:
      BSTR m_SubjectCompanyName;
      CComObject<CAttributes> *m_SubjectAttributes;
      CComObject<CAddress> *m_SubjectAddress;
};

Thanks all.
0
Comment
Question by:edc
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
6 Comments
 
LVL 12

Expert Comment

by:vascov
ID: 9893070
You don't show the implementation of put_subject...

Don't forget that when your object dies, someone has to make sure that the memory owned by the BSTR is freed...

Use CComBSTR if you can...

HTH
0
 
LVL 1

Author Comment

by:edc
ID: 9893518
Hi HTH,
   Thanks for the info.  I have changed my BSTR to CComBSTRs, and that it the better way to do it.  After some monkeying around I found that the culprits are infact the instanciations of the other COM objects used in this com object.

CRequest::CRequest()
{
     CComObject<CAttributes>::CreateInstance(&m_SubjectAttributes);
     CComObject<CAddress>::CreateInstance(&m_SubjectAddress);
}

When I commented out these lines the memory leaks dissappear.  Since I instanciate these in the constructor, normally I would clean them up in the destructor, but the constructor seems to be implemented somewhere else, and I can't write a destructor in my code.  Any recommendations on how to fix this area?  I would prefer not to move them to the function level, but I won't rule that out.

Thanks much.
0
 
LVL 13

Expert Comment

by:SteH
ID: 9895390
With VC++6.0 SP5 I get this behaviour from time to time using DLLs. The allocation done insided the dll is not deallocated before the main app ends. Therefor a memory leak is reported. But inside the DLL it might be deallocated only at a later time than the reporting. It could be the same for you since the allocation numbers {67} ... {78} are relatively low.

When including
static struct _test
{
    _test()
    {
        _CrtSetBreakAlloc (78);  
    }
   
    ~_test()
    {
    }
} test1;
in your cpp file of the main app you might be able to stop when the allocation of that memory happens and see whether this memory is deallocated only later.


0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 1

Expert Comment

by:Tobber
ID: 9896017
Hi.

Since you inherit from CComObjectRootEx you can override CComObjectRootEx::FinalRelease(). There you can realese your m_SubjectAttributes and m_SubjectAddress pointers.

You should also move the creation of your aggregated objects from the constructor to an override of FinalConstruct().

Good luck.

/T
0
 
LVL 1

Author Comment

by:edc
ID: 9896558
Fantastic.  Thanks Tobber.  One question...  I used delete in the FinalRelease() to destroy the COM objects created in FinalConstruct().  It took care of the memory leaks, and exited without a problem, but is this the correct way to destroy COM objects after using ::CreateInstance() ?

Thanks
0
 
LVL 1

Accepted Solution

by:
Tobber earned 500 total points
ID: 9902419
Hi edc.

>is this the correct way to destroy COM objects after using ::CreateInstance()?

Well, COM objects destroy themselves. All users of a COM object (clients) call AddRef() to increase the objects reference count. This is done under the surface once for each of your aggregated objects when you call ::CreateInstance(). The VB client who creates the Request instance also calls AddRef() through the IUnknown interface from which CRequest inherits.

So, once the VB client has created an instance of the Request COM object all three, CRequest, CAttributes and CAddress, have reference counts equal to 1.

When a client is done using a COM object it should call Release() on the object. This was done by VB on the Request instance. When Request instance's reference count becomes zero it no longer needs its CAttributes and CAddress members, thus it should call Release() on both of them.

Your code should look something like this:

HRESULT CRequest::FinalConstruct()
{
  // Called by COM framework when the CRequest reference count goes from 0 to 1.
  HRESULT hRes = CComObject<CAttributes>::CreateInstance(&m_SubjectAttributes);
  if (hRes != S_OK)
    return hRes;
   
  return CComObject<CAddress>::CreateInstance(&m_SubjectAddress);
}

void CRequest::FinalRelease()
{
  // Called by COM framework when the CRequest reference count goes from 1 to 0.
  if (m_SubjectAddress != 0)
    m_SubjectAddress->Release();
   
  if (m_SubjectAttributes != 0)
    m_SubjectAttributes->Release();
}


Look at CComObject::Release() in atlcom.h and you'll see the following:
STDMETHOD_(ULONG, Release)()
{
  ULONG l = InternalRelease();
  if (l == 0)
    delete this;
  return l;
}

Thus, if the reference count is zero it deletes itself. Bye bye memory leaks!


Hope this suffice as an answer to your question.

/T
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article will show you some of the more useful Standard Template Library (STL) algorithms through the use of working examples.  You will learn about how these algorithms fit into the STL architecture, how they work with STL containers, and why t…
C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

719 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question