Ohmmms...
Here are some findings. When enable m_bAutoDestroyWindow, the code will be run ....
BOOL CMFCBaseTabCtrl::RemoveTab(int iTab, BOOL bRecalcLayout)
{
if (iTab < 0 || iTab >= m_iTabsNum)
{
TRACE(_T("RemoveTab: illegal tab number %d\n"), iTab);
return FALSE;
}
if (m_iTabsNum == 1)
{
RemoveAllTabs();
return TRUE;
}
CMFCTabInfo* pTab = (CMFCTabInfo*) m_arTabs [iTab];
ASSERT_VALID(pTab);
if (m_pToolTip->GetSafeHwnd() != NULL)
{
m_pToolTip->DelTool(this, pTab->m_iTabID);
}
m_arTabs.RemoveAt(iTab);
m_iTabsNum --;
if (m_bAutoDestroyWindow)
{
ASSERT_VALID(pTab->m_pWnd);
pTab->m_pWnd->DestroyWindow();
}
delete pTab;
int iActiveTab = m_iActiveTab;
if (m_iActiveTab >= iTab)
{
if (m_bActivateLastVisibleTab)
{
GetLastVisibleTab(iActiveTab);
}
else
{
for (int i = m_iTabsNum - 1; i >= 0; --i)
{
CMFCTabInfo* pNextActiveTab = (CMFCTabInfo*) m_arTabs [i];
ASSERT_VALID(pNextActiveTab);
if (i < iTab && iActiveTab >= 0 && iActiveTab < m_iTabsNum)
{
break;
}
if (pNextActiveTab->m_bVisible)
{
iActiveTab = i;
}
}
}
m_iActiveTab = -1;
}
OnChangeTabs();
if (bRecalcLayout)
{
RecalcLayout();
if (iActiveTab != -1)
{
if (m_bActivateLastActiveTab &&(m_iLastActiveTab != -1))
{
int iLastActiveTab = m_iLastActiveTab;
if (iTab < m_iLastActiveTab)
{
iLastActiveTab = m_iLastActiveTab -1;
}
int iTabToActivate = -1;
GetFirstVisibleTab(iLastActiveTab, iTabToActivate);
SetActiveTab(iTabToActivate);
}
else
{
int iTabToActivate = -1;
GetFirstVisibleTab(iActiveTab, iTabToActivate);
SetActiveTab(iTabToActivate);
}
FireChangeActiveTab(m_iActiveTab);
}
}
return TRUE;
}
Here the code block should be noticed.
if (m_bAutoDestroyWindow)
{
ASSERT_VALID(pTab->m_pWnd);
pTab->m_pWnd->DestroyWindow();
}
It destroy the view window in the tab control. If the view is the last view which tab number is the biggest in the tab. It will cause problem.
The assertion is caused by ....
void CMFCTabCtrl::OnPaint()
{
CPaintDC dc(this);
CMemDC memDC(dc, this);
CDC* pDC = &memDC.GetDC();
dc.GetClipBox(&m_rectCurrClip);
COLORREF clrDark;
COLORREF clrBlack;
COLORREF clrHighlight;
COLORREF clrFace;
COLORREF clrDarkShadow;
COLORREF clrLight;
CBrush* pbrFace = NULL;
CBrush* pbrBlack = NULL;
CMFCVisualManager::GetInstance()->GetTabFrameColors(this, clrDark, clrBlack, clrHighlight, clrFace, clrDarkShadow, clrLight, pbrFace, pbrBlack);
ASSERT_VALID(pbrFace);
ASSERT_VALID(pbrBlack);
CRect rectClient;
GetClientRect(&rectClient);
CBrush* pOldBrush = pDC->SelectObject(pbrFace);
ENSURE(pOldBrush != NULL);
CPen penDark(PS_SOLID, 1, clrDark);
CPen penBlack(PS_SOLID, 1, clrBlack);
CPen penHiLight(PS_SOLID, 1, clrHighlight);
CPen* pOldPen = (CPen*) pDC->SelectObject(&penDark);
ENSURE(pOldPen != NULL);
const int nTabBorderSize = GetTabBorderSize();
CRect rectTabs = rectClient;
if (m_location == LOCATION_BOTTOM)
{
rectTabs.top = m_rectTabsArea.top;
}
else
{
rectTabs.bottom = m_rectTabsArea.bottom;
}
pDC->ExcludeClipRect(m_rectWndArea);
BOOL bBackgroundIsReady = CMFCVisualManager::GetInstance()->OnEraseTabsFrame(pDC, rectClient, this);
if (!m_bDrawFrame && !bBackgroundIsReady)
{
pDC->FillRect(rectClient, pbrFace);
}
CMFCVisualManager::GetInstance()->OnEraseTabsArea(pDC, rectTabs, this);
CRect rectFrame = rectClient;
if (nTabBorderSize == 0)
{
if (m_location == LOCATION_BOTTOM)
{
rectFrame.bottom = m_rectTabsArea.top + 1;
}
else
{
rectFrame.top = m_rectTabsArea.bottom - 1;
}
if (m_bFlat)
{
pDC->FrameRect(&rectFrame, pbrBlack);
}
else
{
pDC->FrameRect(&rectFrame, pbrFace);
}
}
else
{
int yLine = m_location == LOCATION_BOTTOM ? m_rectTabsArea.top : m_rectTabsArea.bottom;
if (!m_bFlat)
{
if (m_location == LOCATION_BOTTOM)
{
rectFrame.bottom = m_rectTabsArea.top;
}
else
{
rectFrame.top = m_rectTabsArea.bottom;
}
}
if (m_bFlatFrame)
{
CRect rectBorder(rectFrame);
if (m_bFlat)
{
if (m_location == LOCATION_BOTTOM)
{
rectBorder.bottom = m_rectTabsArea.top + 1;
}
else
{
rectBorder.top = m_rectTabsArea.bottom - 1;
}
}
rectFrame.DeflateRect(1, 1);
if (m_bDrawFrame && !bBackgroundIsReady && rectFrame.Width() > 0 && rectFrame.Height() > 0)
{
pDC->PatBlt(rectFrame.left, rectFrame.top, nTabBorderSize, rectFrame.Height(), PATCOPY);
pDC->PatBlt(rectFrame.left, rectFrame.top, rectFrame.Width(), nTabBorderSize, PATCOPY);
pDC->PatBlt(rectFrame.right - nTabBorderSize - 1, rectFrame.top, nTabBorderSize + 1, rectFrame.Height(), PATCOPY);
pDC->PatBlt(rectFrame.left, rectFrame.bottom - nTabBorderSize, rectFrame.Width(), nTabBorderSize, PATCOPY);
if (m_location == LOCATION_BOTTOM)
{
pDC->PatBlt(rectFrame.left, m_rectWndArea.bottom, rectFrame.Width(), rectFrame.bottom - m_rectWndArea.bottom, PATCOPY);
}
else
{
pDC->PatBlt(rectFrame.left, rectFrame.top, rectFrame.Width(), m_rectWndArea.top - rectFrame.top, PATCOPY);
}
}
if (m_bFlat)
{
pDC->SelectObject(&penBlack);
pDC->MoveTo(rectFrame.left + nTabBorderSize, yLine);
pDC->LineTo(rectFrame.right - nTabBorderSize, yLine);
}
pDC->Draw3dRect(&rectBorder, clrFace, clrFace);
if (GetTabsHeight() == 0)
{
pDC->Draw3dRect(&rectBorder, clrFace, clrFace);
}
else
{
if (m_bDrawFrame)
{
pDC->Draw3dRect(&rectBorder, clrDark, clrDark);
}
if (!m_bIsOneNoteStyle)
{
int xRight = rectBorder.right - 1;
if (!m_bDrawFrame)
{
xRight -= nTabBorderSize;
}
if (m_location == LOCATION_BOTTOM)
{
pDC->SelectObject(&penBlack);
pDC->MoveTo(rectBorder.left, rectBorder.bottom - 1);
pDC->LineTo(xRight, rectBorder.bottom - 1);
}
else
{
pDC->SelectObject(&penHiLight);
pDC->MoveTo(rectBorder.left, rectBorder.top);
pDC->LineTo(xRight, rectBorder.top);
}
}
}
}
else
{
if (m_bDrawFrame)
{
pDC->Draw3dRect(&rectFrame, clrHighlight, clrDarkShadow);
rectFrame.DeflateRect(1, 1);
pDC->Draw3dRect(&rectFrame, clrLight, clrDark);
rectFrame.DeflateRect(1, 1);
if (!bBackgroundIsReady && rectFrame.Width() > 0 && rectFrame.Height() > 0)
{
pDC->PatBlt(rectFrame.left, rectFrame.top, nTabBorderSize, rectFrame.Height(), PATCOPY);
pDC->PatBlt(rectFrame.left, rectFrame.top, rectFrame.Width(), nTabBorderSize, PATCOPY);
pDC->PatBlt(rectFrame.right - nTabBorderSize, rectFrame.top, nTabBorderSize, rectFrame.Height(), PATCOPY);
pDC->PatBlt(rectFrame.left, rectFrame.bottom - nTabBorderSize, rectFrame.Width(), nTabBorderSize, PATCOPY);
if (m_location == LOCATION_BOTTOM)
{
pDC->PatBlt(rectFrame.left, m_rectWndArea.bottom, rectFrame.Width(), rectFrame.bottom - m_rectWndArea.bottom, PATCOPY);
}
else
{
pDC->PatBlt(rectFrame.left, rectFrame.top, rectFrame.Width(), m_rectWndArea.top - rectFrame.top, PATCOPY);
}
if (m_bFlat)
{
pDC->SelectObject(&penBlack);
pDC->MoveTo(rectFrame.left + nTabBorderSize, yLine);
pDC->LineTo(rectFrame.right - nTabBorderSize, yLine);
}
if (nTabBorderSize > 2)
{
rectFrame.DeflateRect(nTabBorderSize - 2, nTabBorderSize - 2);
}
if (rectFrame.Width() > 0 && rectFrame.Height() > 0)
{
pDC->Draw3dRect(&rectFrame, clrDarkShadow, clrHighlight);
}
}
else
{
rectFrame.DeflateRect(2, 2);
}
}
}
}
if (m_bTopEdge && m_location == LOCATION_TOP)
{
pDC->SelectObject(&penDark);
pDC->MoveTo(rectClient.left, m_rectTabsArea.bottom);
pDC->LineTo(rectClient.left, rectClient.top);
pDC->LineTo(rectClient.right - 1, rectClient.top);
pDC->LineTo(rectClient.right - 1, m_rectTabsArea.bottom);
}
CFont* pOldFont = pDC->SelectObject(m_bFlat ? &m_fntTabs : &afxGlobalData.fontRegular);
ENSURE(pOldFont != NULL);
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(afxGlobalData.clrBtnText);
if (m_rectTabsArea.Width() > 5 && m_rectTabsArea.Height() > 5)
{
CRect rectClip = m_rectTabsArea;
rectClip.InflateRect(1, nTabBorderSize);
CRgn rgn;
rgn.CreateRectRgnIndirect(rectClip);
for (int i = m_iTabsNum - 1; i >= 0; i--)
{
CMFCTabInfo* pTab = (CMFCTabInfo*) m_arTabs [i];
ASSERT_VALID(pTab);
if (!pTab->m_bVisible)
continue;
m_iCurTab = i;
if (i != m_iActiveTab)
{
pDC->SelectClipRgn(&rgn);
if (m_bFlat)
{
pDC->SelectObject(&penBlack);
DrawFlatTab(pDC, pTab, FALSE);
}
else
{
Draw3DTab(pDC, pTab, FALSE);
}
}
}
if (m_iActiveTab >= 0)
{
pDC->SetTextColor(afxGlobalData.clrWindowText);
CMFCTabInfo* pTabActive = (CMFCTabInfo*) m_arTabs [m_iActiveTab];
ASSERT_VALID(pTabActive);
m_iCurTab = m_iActiveTab;
pDC->SelectClipRgn(&rgn);
if (m_bFlat)
{
pDC->SelectObject(&m_brActiveTab);
pDC->SelectObject(&m_fntTabsBold);
pDC->SetTextColor(GetActiveTabTextColor());
pDC->SelectObject(&penBlack);
DrawFlatTab(pDC, pTabActive, TRUE);
const int xLeft = max( m_rectTabsArea.left + 1, pTabActive->m_rect.left + 1);
if (pTabActive->m_rect.right > m_rectTabsArea.left + 1)
{
CPen penLight(PS_SOLID, 1, GetActiveTabColor());
pDC->SelectObject(&penLight);
if (m_location == LOCATION_BOTTOM)
{
pDC->MoveTo(xLeft, pTabActive->m_rect.top);
pDC->LineTo(pTabActive->m_rect.right, pTabActive->m_rect.top);
}
else
{
pDC->MoveTo(xLeft, pTabActive->m_rect.bottom);
pDC->LineTo(pTabActive->m_rect.right, pTabActive->m_rect.bottom);
}
pDC->SelectObject(pOldPen);
}
}
else
{
if (m_bIsActiveTabBold)
{
if (!IsMDITabGroup() || m_bIsActiveInMDITabGroup)
{
pDC->SelectObject(&afxGlobalData.fontBold);
}
}
Draw3DTab(pDC, pTabActive, TRUE);
}
}
pDC->SelectClipRgn(NULL);
}
if (!m_rectTabSplitter.IsRectEmpty())
{
pDC->FillRect(m_rectTabSplitter, pbrFace);
CRect rectTabSplitter = m_rectTabSplitter;
pDC->Draw3dRect(rectTabSplitter, clrDarkShadow, clrDark);
rectTabSplitter.DeflateRect(1, 1);
pDC->Draw3dRect(rectTabSplitter, clrHighlight, clrDark);
}
if (m_bFlat && m_nTabsHorzOffset > 0)
{
pDC->SelectObject(&penDark);
const int xDivider = m_rectTabsArea.left - 1;
if (m_location == LOCATION_BOTTOM)
{
pDC->MoveTo(xDivider, m_rectTabsArea.top + 1);
pDC->LineTo(xDivider, m_rectTabsArea.bottom - 2);
}
else
{
pDC->MoveTo(xDivider, m_rectTabsArea.bottom);
pDC->LineTo(xDivider, m_rectTabsArea.top + 2);
}
}
if (!m_rectResize.IsRectEmpty())
{
pDC->FillRect(m_rectResize, pbrFace);
pDC->SelectObject(&penDark);
if (m_ResizeMode == RESIZE_VERT)
{
pDC->MoveTo(m_rectResize.left, m_rectResize.top);
pDC->LineTo(m_rectResize.left, m_rectResize.bottom);
}
else
{
pDC->MoveTo(m_rectResize.left, m_rectResize.top);
pDC->LineTo(m_rectResize.right, m_rectResize.top);
}
}
pDC->SelectObject(pOldFont);
pDC->SelectObject(pOldBrush);
pDC->SelectObject(pOldPen);
if (memDC.IsMemDC())
{
dc.ExcludeClipRect(m_rectWndArea);
}
}
Here please take care of the following codes
if (m_iActiveTab >= 0)
{
pDC->SetTextColor(afxGlobalData.clrWindowText);
CMFCTabInfo* pTabActive = (CMFCTabInfo*) m_arTabs [m_iActiveTab];
ASSERT_VALID(pTabActive);
CMFCTabInfo* pTabActive = (CMFCTabInfo*) m_arTabs [m_iActiveTab];
will cause the bug. m_iActiveTab tells the acitive tab. But now it was deleted. so it will cause one out of range of the array assertion.
Now my workarroud is to destroy the view windows out of here. But destroy it in the sample application after RemoveView() like this.
RemoveView(nTab);
But I think it may be not a good way to deal with this bug. Any comments?