Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS General Discussion » Memory leak? Collapse All
Subject Author Date
Chris Mason Jun 5, 2008 - 4:08 AM

Hi.


We have a memory leak within our project, which seems to be concerned with the CExtPageContainerWnd class. We are running v2.60 of prof uis.


Here are some more details.


The CExtPageContainerWnd is used to contain an unknown number of a CExtToolControlBar (well bars derived from), where the needed details are gathered from an xml file. Therefore these bars have to be created dynamically;


CRuntimeClass *pRTC = RUNTIME_CLASS( CAppToolControlBar);

        CAppToolControlBar *pBar = DYNAMIC_DOWNCAST(CAppToolControlBar, pRTC->CreateObject());       



        if (!pBar->Create(cGroupName, &m_wndAppPageContainer, NULL))

        {

            TRACE0( "Failed to create resizable bar" );

            return false;

        }           


This bar is then placed inside the CExtPageContainerWnd;


m_wndAppPageContainer.PageInsert(pBar->GetSafeHwnd(),-1, cGroupName);   


Then CExtToolBarButtons are added to the bar. Still fine here.


This all works fine. However, our problem starts when we call CExtPageContainerWnd::PageExpand at the end of our initialisation function. When this method is called the memory of the program increases at 4kb per second until it crashes our program and then others that we may be running on the same system.


We have found that if you click the left mouse button on any of the toolbars then the leak will stop.


Our inclination is that it has something to do with us using runtime created toolbars, as looking at your Page_Container example this problem does not occur.


If you need more information, such as the source files for the affected class, or maybe even a small example project, please don’t hesitate to ask.


Cheers.


 


 


 


 


 


 

Chris Mason Jun 9, 2008 - 3:16 AM

Thanks for your help, but it was the OnAppUpdateCommand function which was the problem. I had forgotten to add the following code:


FindClose(hFindExe);

FindClose(hFindScript);

FindClose(hFindOther);

Technical Support Jun 6, 2008 - 1:26 PM

Using the FindFirstFile() Win32 API without a final invocation of the FindClose() Win32 API causes memory leaks. This also relates to similar C runtime APIs.

Besides this function is very heavy. The command updating method should be as light as possible. We mean they should return as soon as possible. Otherwise your application will work quite slow.

Chris Mason Jun 6, 2008 - 9:11 AM

Edit:


 


After some more experimentation we have found that it is the OnAppUpdateCommand(CCmdUI *pCmdUI) function that is causing the problem:


void CAppControlBar::OnAppUpdateCommand(CCmdUI *pCmdUI)

{

    UINT m_iButtonID = (pCmdUI->m_nID) - 2100; // 2100 being the start of the range used

                                                                                            //to identify the appcontrolbar commands



    CString m_cButtonID = g_PseudoAppClasses.GetAt(m_iButtonID);



    //Need to remove the %DATS% in order to use the GetDatsDirectory function

    int m_iDatsDirectoryUsed = m_cButtonID.Find("%DATS%");



    if (m_iDatsDirectoryUsed > -1)

    {

        int n = m_cButtonID.Replace("%DATS%","");

    }



    WIN32_FIND_DATA findFileData;

    HANDLE          hFindExe;

    HANDLE            hFindScript;

    HANDLE            hFindOther;


    //Here we search for whether the .exe or script associated with the command is installed, and

    //to disable/grey out the button if the desired object is not installed.

    hFindExe = FindFirstFile((CDatsUtil::GetDatsDirectory()+"\\bin\\"+m_cButtonID), &findFileData);

    hFindScript = FindFirstFile((CDatsUtil::GetDatsDirectory()+"\\bin\\Scripts\\"+m_cButtonID), &findFileData);

    hFindOther = FindFirstFile((CDatsUtil::GetDatsDirectory()+m_cButtonID), &findFileData);



    if (hFindExe == INVALID_HANDLE_VALUE && hFindScript == INVALID_HANDLE_VALUE && hFindOther == INVALID_HANDLE_VALUE)

    {

        pCmdUI->Enable(FALSE);



        return;

    }              



    pCmdUI->Enable(TRUE);



}


This function is used as our product has options that a customer may not purchase, so these options need to be disabled. We scan the installation folder of our program to see if these have been installed and enable the buttons accordingly. So it may not be a prof-uis problem at all.

Chris Mason Jun 6, 2008 - 8:49 AM

This is a project I’ve taken on from someone who has left, and let’s just say that the previous programmer wasn’t the biggest fan of commenting code!


The buttons are custom painted, and as far I can tell the code is identical to that found in your funnybars example.


// ---------------------------------------------

// class CAppToolButton

// ---------------------------------------------



IMPLEMENT_DYNAMIC( CAppToolButton, CExtBarButton );



CAppToolButton::CAppToolButton(

    CExtToolControlBar * pBar,

    UINT nCmdID,

    LPCTSTR strFunnyText,

    double lfHotPercent

    ) : CExtBarButton( pBar, nCmdID, 0 )

    , m_strFunnyText( (strFunnyText==NULL) ? _T("") : strFunnyText )

    , m_sizeFunnyTextCalc( 0, 0 )

    , m_sizeFunnyTextWell( 0, 0 )

    , m_lfHotPercent( lfHotPercent )

{

    m_cMouseDownPosition = CPoint(-1, -1);

}



CSize CAppToolButton::CalculateLayout(

    CDC & dc,

    CSize sizePreCalc,

    BOOL bHorz

    )

{

    ASSERT_VALID( this );

    CSize _size = CExtBarButton::CalculateLayout( dc, sizePreCalc, bHorz );

    ASSERT( !IsSeparator() );



    if( ! m_strFunnyText.IsEmpty() )   

    {

        CFont * pOldFont = dc.SelectObject( &g_PaintManager->m_FontNormal );

        CRect rcMeasured( 0, 0, 0, 0 );

        dc.DrawText( m_strFunnyText, &rcMeasured, DT_LEFT|DT_CALCRECT );

        m_sizeFunnyTextWell = rcMeasured.Size();

        m_sizeFunnyTextWell.cx += __EXT_TB_BUTTON_TEXT_MARGIN*10;

        static CString strMeasureTemplate( _T("AapqWZz\nAapqWZz") );

        dc.DrawText( strMeasureTemplate, &rcMeasured, DT_LEFT|DT_CALCRECT );

        dc.SelectObject( pOldFont );

        m_sizeFunnyTextCalc = rcMeasured.Size();

        m_sizeFunnyTextCalc.cx += __EXT_TB_BUTTON_TEXT_MARGIN*2;

        m_sizeFunnyTextCalc.cx = max( m_sizeFunnyTextWell.cx, m_sizeFunnyTextCalc.cy );

        m_sizeFunnyTextCalc.cy = max( m_sizeFunnyTextWell.cy, m_sizeFunnyTextCalc.cy );

        m_sizeFunnyTextCalc.cx += __EXT_TB_BUTTON_INNER_MARGIN*2;

        _size.cx = max( m_sizeFunnyTextCalc.cx, _size.cx );

        _size.cy += m_sizeFunnyTextCalc.cy + 4;

    }



    m_ActiveSize = _size;

    return m_ActiveSize;

}



void CAppToolButton::PaintCompound(CDC & dc,

                                     bool bPaintParentChain,

                                     bool bPaintChildren,

                                     bool bPaintOneNearestChildrenLevelOnly

                                     )

{

    ASSERT_VALID( this );

    ASSERT( dc.GetSafeHdc() != NULL );

    CExtToolControlBar * pBar = GetBar();

    ASSERT_VALID( pBar );

    if( (m_pCtrl != NULL) && (!m_bVertDocked || GetCtrlVisibleVertically()) )

        return;

    ASSERT( ! IsSeparator() );

    if( ! IsPaintAble( dc ) )

        return;

    if( AnimationClient_StatePaint( dc ) )

        return;

    if( bPaintParentChain )

        PaintParentChain( dc );



    CRect rcArea( m_ActiveRect );

    rcArea.DeflateRect( __EXT_TB_BUTTON_INNER_MARGIN, __EXT_TB_BUTTON_INNER_MARGIN );

    if(        (!IsVisible())

        ||    rcArea.right <= rcArea.left || rcArea.bottom <= rcArea.top

        ||    (GetStyle() & TBBS_HIDDEN) != 0

        ||    (!dc.RectVisible(&m_ActiveRect))

        )

        return;

    bool bPushed = IsPressed() ? true : false;

    bool bEnabled = IsDisabled() ? false : true;

    bool bHover =

        (    IsHover()

        && (! CExtToolControlBar::g_bMenuTracking )

        && (! CExtPopupMenuWnd::IsMenuTracking() )

        ) ? true : false;

    bool bIndeterminate = IsIndeterminate() ? true : false;

    CExtCmdIcon * pIcon = GetIconPtr();

    if( m_lfHotPercent >= -1.0 && pIcon != NULL && (! pIcon->IsEmpty() ) )

    {

        ASSERT( m_lfHotPercent <= 1.0 );

        if( m_iconCold.IsEmpty() )

        {

            m_iconCold.m_bmpNormal = pIcon->m_bmpNormal;

            COLORREF clrIconAlphaAccent =

                pBar->PmBridge_GetPM()->GetIconAlphaColor();

            if( clrIconAlphaAccent != COLORREF(-1L) )

            {

                double H = 0.0, S = 0.0, L = 0.0;

                CExtBitmap::stat_RGBtoHSL( clrIconAlphaAccent, &H, &S, &L );

                S = 0.3;

                clrIconAlphaAccent = CExtBitmap::stat_HLStoRGB( H, L, S );

            } // if( clrIconAlphaAccent != COLORREF(-1L) )

            m_iconCold.m_bmpNormal.MakeMono( clrIconAlphaAccent );

            m_iconCold.m_bmpNormal.AdjustHLS(

                COLORREF(-1L),

                COLORREF(-1L),

                0.0,

                m_lfHotPercent,

                0.0

                );

            m_iconCold.m_bmpNormal.AdjustAlpha( -0.5 );

        } // if( m_iconCold.IsEmpty() )

        if( (! bHover) && (! bPushed) && (! m_iconCold.IsEmpty() )  )

            pIcon = &m_iconCold;

    } // if( m_lfHotPercent >= -1.0 && pIcon != NULL && (! pIcon->IsEmpty() ) )

    CExtPaintManager::PAINTPUSHBUTTONDATA _ppbd(

        this, /*bHorz*/true, rcArea, NULL, pIcon,

        true, bHover, bPushed, bIndeterminate, bEnabled,

        true, false, false, CExtPaintManager::__ALIGN_HORIZ_CENTER, NULL,

        (    IsAbleToTrackMenu()

        && (!m_pBar->IsKindOf(RUNTIME_CLASS(CExtMenuControlBar)))

        ) ? true : false,

        0, ( bEnabled && (!bHover) && (!bPushed) )

        );

    if( m_bSeparatedDropDown )

    {

        _ppbd.m_bSeparatedDropDown = true;

        if(        (m_bDropDownHT || CExtToolControlBar::g_bMenuTracking)

            &&    bPushed

            )

            _ppbd.m_bPushedDropDown = true;

    } // if( m_bSeparatedDropDown )

    g_PaintManager->PaintPushButton( dc, _ppbd );

    CRect rcText( rcArea );

    rcText.DeflateRect( __EXT_TB_BUTTON_OUTER_MARGIN, __EXT_TB_BUTTON_OUTER_MARGIN+__EXT_TB_BUTTON_INNER_MARGIN  );

    rcText.top = rcText.bottom - m_sizeFunnyTextCalc.cy;

    rcText.OffsetRect(

        ( m_sizeFunnyTextCalc.cx - m_sizeFunnyTextWell.cx ) / 2,

        ( m_sizeFunnyTextCalc.cy - m_sizeFunnyTextWell.cy ) / 2

        );

    if( bPushed )

        rcText.OffsetRect( g_PaintManager->GetPushedOffset() );

    CFont fontTmp;

    CFont * pOldFont = NULL;

    COLORREF clrText =

        bEnabled

        ? (    ( m_lfHotPercent >= -1.0f )

        ? g_PaintManager->GetColor(

        (bHover || bPushed) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT,

        this

        )

        : dc.GetNearestColor(

        (bHover || bPushed) ? ( RGB(255,64,64) ) : ( RGB(64,64,255) )

        )

        )

        : g_PaintManager->GetColor( COLOR_3DSHADOW, this )

        ;

    if( bEnabled && bHover || bPushed )

    {

        LOGFONT lf;

        VERIFY( g_PaintManager->m_FontNormal.GetLogFont(&lf) );

        lf.lfUnderline = TRUE;

        VERIFY( fontTmp.CreateFontIndirect(&lf) );

        dc.SelectObject( &fontTmp );

    }

    else

        dc.SelectObject( &g_PaintManager->m_FontNormal );

    int nOldBkMode = dc.SetBkMode( TRANSPARENT );

    COLORREF clrTextOld = dc.SetTextColor( clrText );

    dc.DrawText( m_strFunnyText, &rcText, DT_CENTER|DT_VCENTER );

    dc.SetTextColor( clrTextOld );

    dc.SetBkMode( nOldBkMode );

    dc.SelectObject( pOldFont );



    if( bPaintChildren )

        PaintChildren( dc, bPaintOneNearestChildrenLevelOnly );

}



void CAppToolButton::OnClick(CPoint point, bool bDown)

{

    if( bDown )

    {

        m_cMouseDownPosition = point;

    }



    __super::OnClick(point, bDown);

}




 


We also have a CExtToolControlBar derived class where OnQuerytMultiRowLayout() is overridden. This class also has the DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE macros implemented.


The Code for the main toolbar CAppControlBar class has the following code:


 


// ---------------------------------------------

// class CAppControlBar

// ---------------------------------------------



CAppControlBar::CAppControlBar()

{

    m_bCanDragButtons = true;



    g_PseudoAppMap[".exe"]        = CPseudoCommand(this, &CAppControlBar::OnApplicationRun);

    g_PseudoAppMap[".dbs"]        = CPseudoCommand(this, &CAppControlBar::OnScriptRun);

}



CAppControlBar::~CAppControlBar()

{

    for( int i=0; i<m_cToolBarHooks.GetSize(); i++ )

    {

        delete m_cToolBarHooks[i];

    }



    m_cToolBarHooks.RemoveAll();

}



BEGIN_MESSAGE_MAP(CAppControlBar, CExtControlBar)

    ON_WM_CREATE()

END_MESSAGE_MAP()



int CAppControlBar::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

    if (__super::OnCreate(lpCreateStruct) == -1)

        return -1;



    // Create the page container toolbar



    if(!m_wndAppPageContainer.Create(this))

    {

        TRACE0("Failed to create application toolbar\n");

        return -1;      // fail to create

    }



    // The owner of this toolbar and any subsequent bars should

    // be the CFrameWnd (CMainFrame)



    m_wndAppPageContainer.SetOwner(GetParent());

   

    Initialise();



    return 0;

}



bool CAppControlBar::Initialise()

{

    CString cPath = CDatsUtil::GetDatsDirectory() + "\\Lib\\Menus\\App.xml";

    UINT nCmdID   = APPCONTROLBAR_COMMAND_BASE;

   

    CXmlDocument cXMLFile;

    cXMLFile.Load(cPath);



    CXmlNode     cNode(cXMLFile.AsNode());

    CXmlNodeList cGroups(cNode.FindNodes("/MenuItem/MenuItem"));

   



    for(int i=0; i<cGroups.Count(); i++)

    {

        CXmlNode cGroup(cGroups.Node(i));

        CString  cGroupName(cGroup.GetValue("id"));



        // Add the toolbar

       

        CRuntimeClass *pRTC = RUNTIME_CLASS( CAppToolControlBar);

        CAppToolControlBar *pBar = STATIC_DOWNCAST(CAppToolControlBar, pRTC->CreateObject());       



        if (!pBar->Create(cGroupName, &m_wndAppPageContainer, NULL))

        {

            TRACE0( "Failed to create resizable bar" );

            return false;

        }   



       



        m_wndAppPageContainer.PageInsert(pBar->GetSafeHwnd(),-1, cGroupName);       



        CAppToolControlBarHook *pHook = new CAppToolControlBarHook(pBar, this);

        m_cToolBarHooks.Add(pHook);



        // NB: Prof-UIS toolbar owners should

        //     i)  Be the command target

        //     ii) Not be control bar’s



        pBar->SetOwner(GetParent());

        pBar->SetBarStyle(CBRS_ALIGN_LEFT);   

       



        // Add items to control bar



        CXmlNodeList cItems(cGroup.FindNodes("MenuItem"));



        for(int j=0; j<cItems.Count(); j++)

        {

            CXmlNode cItem(cItems.Node(j));

           

            CExtBitmap cExtBitmap;

            COLORREF   crBackground = RGB(255,0,255);

            CString    cFileName("");

           

            cFileName    = CDatsUtil::GetDatsDirectory()

                    + "\\Lib\\Applications\\Png\\"

                            + cItem.GetValue("icon");



            if(CreateButtonBitmap(cFileName, cExtBitmap, crBackground))

            {

                InsertButton(pBar, nCmdID++, cItem, cExtBitmap, crBackground);   

            }

        }



        //make sure that the buttons are displayed vertically by forcing them to wrap

        if (pBar->GetButtonsCount() <= 3)

        {

            pBar->SetMultiRow(false);

        }

        else

        {

            int iRowNumber = (pBar->GetButtonsCount() / 2) - 1;   



            pBar->SetMultiRow(true);



            pBar->GetButton(iRowNumber)->SetWrap( CAppToolButton::__EVT_VERT);

            pBar->GetButton(iRowNumber)->SetWrap( CAppToolButton::__EVT_HORZ);

            pBar->GetButton(iRowNumber)->SetWrap( CAppToolButton::__EVT_FLOAT);

            pBar->GetButton(iRowNumber)->SetVertDocked(true);

            pBar->GetButton(iRowNumber)->SetNoRotateVerticalLayout(true);

        }

           

    }

    m_wndAppPageContainer.PageExpand(0);



    return true;

}



bool CAppControlBar::InsertButton(CExtToolControlBar *pBar, UINT nCmdID, CXmlNode& node, CExtBitmap& bmp, COLORREF crTransparent)

{

    CString cProfileName = g_CmdManager->ProfileNameFromWnd(m_hWnd);



    // Add the command name and set the commands user data to point to it



    CExtCmdItem cExtCmd(nCmdID);

    int iIndex = g_PseudoAppClasses.Add(node.GetValue("class"));

    g_PseudoAppID.Add(node.GetValue("id"));

    cExtCmd.m_nLParamUserData = iIndex;



    // Add the dynamically allocated command id into the command manager



    g_CmdManager->CmdSetup(

        cProfileName,

        cExtCmd,

        true,

        NULL);



    // Change the commands icon to the one supplied



    g_CmdManager->CmdSetIcon(

        cProfileName,

        nCmdID,

        bmp,

        crTransparent,

        NULL);



    // Finally, Insert the button



    //pBar->InsertButton( -1, cExtCmd.m_nCmdID, NULL );



    CAppToolButton * pTBB = new CAppToolButton(pBar, nCmdID,

                                (LPCTSTR)node.GetValue("label"),-2.0);

   

    pBar->InsertSpecButton(-1, pTBB);   

    return true;

}



bool CAppControlBar::CreateButtonBitmap(LPCTSTR lpszPath, CExtBitmap& bmp, COLORREF crBackground)

{

    // Use GdiPlus to load the png image (seems to be the easiest way to

    // load a .png file into an HBITMAP compatible class)



    // Convert path to wchar



    WCHAR wszFileName[256];

    MultiByteToWideChar( CP_ACP, 0, lpszPath,

    strlen(lpszPath)+1, wszFileName,  

    sizeof(wszFileName)/sizeof(wszFileName[0]) );



    // Load it



    Bitmap *pOrigBitmap  = Bitmap::FromFile(wszFileName);



    // Work out target size to maintain its aspect ratio



    CSize cTargetSize;

    Rect  buttonRect(0,0,32,32);

    int   iWidth  = pOrigBitmap->GetWidth();

    int   iHeight = pOrigBitmap->GetHeight();

   

    if(iWidth*buttonRect.Height > iHeight*buttonRect.Width)

    {

        cTargetSize.cx = buttonRect.Width;

        cTargetSize.cy = buttonRect.Width * iHeight / iWidth;

    }

    else

    {

        cTargetSize.cy = buttonRect.Height;

        cTargetSize.cx = buttonRect.Height * iWidth / iHeight;

    }



    // Adjust button rect to maintain aspect and create the initial button bitmap



    buttonRect = Rect(0,0,cTargetSize.cx, cTargetSize.cy);

    Bitmap *pButtonBitmap = (Bitmap *)pOrigBitmap->GetThumbnailImage(buttonRect.Width, buttonRect.Height);



    // Prepare a mask the same size as the bitmap (we need to draw onto a white background

    // to preseve the way semi-transparent colours look in the worksheet)



    Color   backColor(GetRValue(crBackground), GetGValue(crBackground), GetBValue(crBackground));

    Bitmap *pMask = new Bitmap(pButtonBitmap->GetWidth(), pButtonBitmap->GetHeight());



    for(int y=0; y<(int)pMask->GetHeight(); y++)

    {

        for(int x=0; x<(int)pMask->GetWidth(); x++)

        {

            Color c; pButtonBitmap->GetPixel(x,y,&c);



            if(c.GetA() > 0)

            {

                pMask->SetPixel(x,y,Color(255,255,255,255));

            }

            else

            {

                pMask->SetPixel(x,y,backColor); 

            }

        }

    }



    // Create the graphics object



      CBitmapDC bitmapDC(CDC::FromHandle(::GetDC(::GetDesktopWindow())), buttonRect.Width, buttonRect.Height);

    Graphics *pGraphics = Graphics::FromHDC(bitmapDC);



    // Fill with background (transparent) colour



    SolidBrush brush(backColor);

    pGraphics->FillRectangle(&brush, buttonRect);

     

    // Draw the mask and the image into the graphics object



    pGraphics->DrawImage(pMask, buttonRect);

    pGraphics->DrawImage(pButtonBitmap, buttonRect);



    // Finally create the proper bitmap



    bmp.FromBitmap(bitmapDC.GetHBITMAP());



    return true;

}



void CAppControlBar::OnApplicationRun()

{

    CString m_cAppID = AfxGetApp()->GetAppID(); //Get the name of the .exe to open



    CString cDirectory = CDatsUtil::GetDatsDirectory()

                    + "\\bin\\" + m_cAppID; //point to the default directory for these kinds of things



    ShellExecute(0, "open", cDirectory, 0, 0, SW_SHOW); //Open the exe



}



void CAppControlBar::OnScriptRun()

{

    CString m_cAppID = AfxGetApp()->GetAppID(); //Get the name of the script to open



    int m_iIsScriptDefault = m_cAppID.Find(":"); //Test to see if the script is in the default location



    int m_iIsScriptInDats = m_cAppID.Find("%DATS%"); //Test to see if %DATS% has been used



    if (m_iIsScriptDefault > -1 || m_iIsScriptInDats > -1)

    {

        AfxGetApp()->RunScript(m_cAppID);// Open the script with the path provided

    }

    else

    {

        CString cDirectory = CDatsUtil::GetDatsDirectory()

                    + "\\bin\\Scripts\\" + m_cAppID;



        AfxGetApp()->RunScript(cDirectory); //Open the script with the default path

    }

}



void CAppControlBar::OnAppUpdateCommand(CCmdUI *pCmdUI)

{

    UINT m_iButtonID = (pCmdUI->m_nID) - 2100; // 2100 being the start of the range used

                                            //to identify the appcontrolbar commands



    CString m_cButtonID = g_PseudoAppClasses.GetAt(m_iButtonID);



    //Need to remove the %DATS% in order to use the GetDatsDirectory function

    int m_iDatsDirectoryUsed = m_cButtonID.Find("%DATS%");



    if (m_iDatsDirectoryUsed > -1)

    {

        int n = m_cButtonID.Replace("%DATS%","");

    }



    WIN32_FIND_DATA findFileData;

    HANDLE          hFindExe;

    HANDLE            hFindScript;

    HANDLE            hFindOther;



    //Here we search for whether the .exe or script associated with the command is installed, and

    //to disable/grey out the button if the desired object is not installed.

    hFindExe = FindFirstFile((CDatsUtil::GetDatsDirectory()+"\\bin\\"+m_cButtonID), &findFileData);

    hFindScript = FindFirstFile((CDatsUtil::GetDatsDirectory()+"\\bin\\Scripts\\"+m_cButtonID), &findFileData);

    hFindOther = FindFirstFile((CDatsUtil::GetDatsDirectory()+m_cButtonID), &findFileData);



    if (hFindExe == INVALID_HANDLE_VALUE && hFindScript == INVALID_HANDLE_VALUE && hFindOther == INVALID_HANDLE_VALUE)

    {

        pCmdUI->Enable(FALSE);

        return;

    }                   



    pCmdUI->Enable(TRUE);





}




 


Now after taking your advise in regards to commenting out the code we found that the memory leak is stopped when pBar->InsertSpecButton(-1, pTBB);   is not called within the following function.


bool CAppControlBar::InsertButton(CExtToolControlBar *pBar, UINT nCmdID, CXmlNode& node, CExtBitmap& bmp, COLORREF crTransparent)

{

    CString cProfileName = g_CmdManager->ProfileNameFromWnd(m_hWnd);



    // Add the command name and set the commands user data to point to it



    CExtCmdItem cExtCmd(nCmdID);

    int iIndex = g_PseudoAppClasses.Add(node.GetValue("class"));

    g_PseudoAppID.Add(node.GetValue("id"));

    cExtCmd.m_nLParamUserData = iIndex;



    // Add the dynamically allocated command id into the command manager



    g_CmdManager->CmdSetup(

        cProfileName,

        cExtCmd,

        true,

        NULL);



    // Change the commands icon to the one supplied



    g_CmdManager->CmdSetIcon(

        cProfileName,

        nCmdID,

        bmp,

        crTransparent,

        NULL);



    // Finally, Insert the button



    //pBar->InsertButton( -1, cExtCmd.m_nCmdID, NULL );



    CAppToolButton * pTBB = new CAppToolButton(pBar, nCmdID,

                                (LPCTSTR)node.GetValue("label"),-2.0);

   

    pBar->InsertSpecButton(-1, pTBB);   

    return true;

}


 


 

Technical Support Jun 6, 2008 - 1:22 PM

Please let us know, if you are closing application in debug session, then whether the Output window in your Visual Studio contains dump information describing memory leaks? If yes, then you should be able to double click on the strings in the memory leaks dump text and you should see the source code points where the leaked memory was allocated. If no, then we suspect your code allocates command items in the command manager and does not release them. In this case command items are released at shutdown. If our guess is correct, then you should know that the 65534 number is maximal count of commands which can be allocated in one command manager’s profile.

We also suspect the CAppControlBar::CreateButtonBitmap() method can be source of memory leaks. It looks like it allocates some objects dynamically and we have no idea where they become released. The 4096 bytes looks like too big size for 16x16 icon with 32-BPP (4-bytes) because 16 * 16 * 4 = 1024. But 32 * 32 * 4 = 4096.

In any case, you can comment parts of your code for detecting situation when the memory will start growing. You can start from removing the image loading code and provide toolbar buttons with empty icons.

Technical Support Jun 5, 2008 - 11:57 AM

Prof-UIS toolbars have never had such memory leak problems. We need more details about your toolbars. Do you use some custom painting code or some timer based code which creates dynamically instantiated objects and potentially do not release them? Before you spend your time for creating a test project, you can try to comment out some parts of your code related to your toolbar class(es) and try to find out when leaking stops.