DataMekanix Home Docking Windows Made Easy


home / articles / how to verify the bar state info


 

How to verify the bar state info

by Cristi Posea

The Problem

If you use CFrameWnd's LoadBarState()/SaveBarState() to remember the positions of the control bars, you may run into this while developing your app - if you change the bar IDs, or simply remove a bar (it is no longer created in CMainFrame::OnCreate()), your app will crash.

Why? Because in the LoadBarState() internals, there is a portion of code which retrieves the bar ID, calls CMainFrame::GetControlBar() and if the pointer returned is NULL (there is no controlbar created with this ID) it will assert. In Release builds, the ASSERT line is not compiled, so it goes further and tries to update the m_nMRUWidth member of the bar, causing a protection error. Nasty.

Well, since you are a MFC veteran, you already know how to fix this:
1. Open the registry, and delete the whole key storing your app's profile.
2. Or, open your favorite text editor, and remove the profile section from the .ini file.
3. Or, simpler, comment the LoadBarState() call in CMainFrame::OnCreate(), run the app (allowing the SaveBarState() to update the profile), then uncomment it back.

What about this? - You already shipped to billions of customers the CoolApp 1.0 with 2 toolbars. Now it's time for an upgrade - you will deliver CoolApp 2.0, which has only one big toolbar, or you changed the toolbar IDs. The new version will crash on a billion computers because of old profile settings, and you get a billion calls for tech support. Uh-oh!

But I have this supper install package which verifies the existing profile or builds a brand new one, you'll say. Congratulations, and keep up the good work! This article is not for you, but you may read it anyway.

The Solution

1. Create a new member function in CMainFrame, like this:

    BOOL VerifyBarState(LPCTSTR lpszProfileName);

2. Add the function body to mainfrm.cpp:

BOOL CMainFrame::VerifyBarState(LPCTSTR lpszProfileName)
{
    CDockState state;
    state.LoadState(lpszProfileName);

    for (int i = 0; i < state.m_arrBarInfo.GetSize(); i++)
    {
        CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
        ASSERT(pInfo != NULL);
        int nDockedCount = pInfo->m_arrBarID.GetSize();
        if (nDockedCount > 0)
        {
            // dockbar
            for (int j = 0; j < nDockedCount; j++)
            {
                UINT nID = (UINT) pInfo->m_arrBarID[j];
                if (nID == 0) continue; // row separator
                if (nID > 0xFFFF)
                    nID &= 0xFFFF; // placeholder - get the ID
                if (GetControlBar(nID) == NULL)
                    return FALSE;
            }
        }
        
        if (!pInfo->m_bFloating) // floating dockbars can be created later
            if (GetControlBar(pInfo->m_nBarID) == NULL)
                return FALSE; // invalid bar ID
    }

    return TRUE;
}

3. Finally, change the LoadBarState() call in this way:

    if (VerifyBarState(_T("MyApp")))
        LoadBarState(_T("MyApp"));

Optionally, you can add an else branch, in which you clean up or remove the profile settings.

Have fun!


Copyright 1998-2010 DataMekanix. All rights reserved.