Solved

Memory Leak

Posted on 2003-12-07
6
1,595 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
6 Comments
 
LVL 12

Expert Comment

by:vascov
Comment Utility
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
Comment Utility
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
Comment Utility
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
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 1

Expert Comment

by:Tobber
Comment Utility
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
Comment Utility
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
Comment Utility
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

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Programmer's Notepad is, one of the best free text editing tools available, simply because the developers appear to have second-guessed every weird problem or issue a programmer is likely to run into. One of these problems is selecting and deleti…
Jaspersoft Studio is a plugin for Eclipse that lets you create reports from a datasource.  In this article, we'll go over creating a report from a default template and setting up a datasource that connects to your database.
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…

744 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

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now