Solved

Memory Leak

Posted on 2003-12-07
6
1,607 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
Industry Leaders: 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 125 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

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!

Question has a verified solution.

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

Suggested Solutions

IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

710 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