We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you two Citrix podcasts. Learn about 2020 trends and get answers to your biggest Citrix questions!Listen Now


CFormView parented by a CTabCtrl

YeahRight asked
Medium Priority
Last Modified: 2013-11-20
I am attempting to create a CTabCtrl and associate a CFormView with each tab to display various statistics.  So far it all works fine, I can launch the app, which creates a CView view, the CView view then creates a CTabctrl control and the control creates each of the associated CFormViews by creating a "CreateContext" object and calling thier "Create" member functions.

I have commented out the creation of all but one tab and its associated CFormView.  (just to make debugging easier)

I do not currently make any DestoryWindow() calls, but I have verified that the framework does call the DestroyWindow functions and destructors correctly.  The problem occurs just after the destructor for the first CFormView is called.

The problem is that when I exit the application I get an ASSERTION FAILED message stating that _BLOCK_TYPE_IS_INVALID(pHead->nBlockUse) which occurs in dbgheap.c.

Does a CFormView have to be parented by a FrameWnd?
Does a CFormView have to be associated with a CDocument?
Can anyone shed any light on the error message?

Some code sipets that might be useful are below (I have not yet made any modifications to the CFormView derived classes):

in the CView derived class' OnInitialUpdate,
      // TODO: Add your specialized code here and/or call the base class

      // Create the Tab Control
      CRect TabRect;

      if (!m_wndTabs.Create(dwStyle, TabRect, this, TAB_CONTROL_ID))
            TRACE0("Failed to create tab control\n");

In the CTabCtrl derived class' OnCreate handler:
      if (CTabCtrl::OnCreate(lpCreateStruct) == -1)
            return -1;

      // TODO: Add your specialized creation code here
      TC_ITEM TabCtrlItem;
      TabCtrlItem.mask = TCIF_TEXT;
          TabCtrlItem.cchTextMax = 0;
          TabCtrlItem.iImage = 0;

      CRect TabRect;

      // for now an temporary size adjustment
      TabRect.top += 30;
      TabRect.right -= 10;
      TabRect.bottom -=30;

      CCreateContext CreateContext;
      CreateContext.m_pNewViewClass = RUNTIME_CLASS(GENERAL_VIEW);
      CreateContext.m_pCurrentDoc = NULL;
      CreateContext.m_pNewDocTemplate = NULL;
      CreateContext.m_pLastView = NULL;
      CreateContext.m_pCurrentFrame = NULL;

                        WS_CHILD | WS_VISIBLE,

      TabCtrlItem.pszText = "General";
      InsertItem(0, &TabCtrlItem);

      return 0;
Watch Question

Since you don't provide a stack trace at the time of the assertion, and since you don't mention wihch version of VC++ on which operating system you're using, it's very difficult to give you the right answer on the first try.

What's _probably_ wrong is that you've done a bad job of managing your m_wndTabs member.  This member is a window, and you'll want to take care of its destruction.  When the view window that owns the m_wndTabs member is destroyed, the C++ view object will delete itself. Since the m_wndTabs window is a parent of that view window, the Windows window will still exist while the C++ object has been destroyed.  Eventually, the frame will shut down, and it will try to destroy the tab window object; but since the C++ object is gone, MFC gets sick.

Having a tab control parent any window is questionable.  Certainly, having a C++ object represent a window and contain its parent is very questionable and the root of your problem.

You can work around the problem without fixing your design by handling WM_DESTROY for your view. There, either call DestroyWindow() or Detach() on your m_wndTabs object.  I think you should revisit your design, though.

.B ekiM

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts


What I am attempting to do is utilize the same view to display various groupings of statistics.  This seems to be represented well by a tab control.  So on each tab "A" I want to display group "A" statistics.  So I figured that associating a view with each tab would be a logical implementation...

1. Does a view have to be parented by a frame?
2. Does a view have to be associated with a Doc?
3. Can you suggest a better design that I should consider?

FYI - here is the call stack from the VC++ 5.0 IDE running on Win95:

_free_dbg_lk(void * 0x0065021c, int 4) line 1017 + 82 bytes
_free_dbg(void * 0x0065021c, int 4) line 970 + 13 bytes
CObject::operator delete(void * 0x0065021c) line 44 + 12 bytes
GENERAL_VIEW::`scalar deleting destructor'(unsigned int 1) + 34 bytes
CView::PostNcDestroy() line 120 + 31 bytes
CWnd::OnNcDestroy() line 815
CWnd::OnWndMsg(unsigned int 130, unsigned int 0, long 0, long * 0x0063ef08) line 1795
CWnd::WindowProc(unsigned int 130, unsigned int 0, long 0) line 1555 + 30 bytes
AfxCallWndProc(CWnd * 0x0065021c {CObject}, HWND__ * 0x00000cfc, unsigned int 130, unsigned int 0, long 0) line 217 + 26 bytes
AfxWndProc(HWND__ * 0x00000cfc, unsigned int 130, unsigned int 0, long 0) line 371
AfxWndProcBase(HWND__ * 0x00000cfc, unsigned int 130, unsigned int 0, long 0) line 203 + 21 bytes
KERNEL32! bff73663()
KERNEL32! bff928e0()


After following your suggested educated guess as to the resolution of the problem without having all the facts, here is what I found:

When using DestroyWindow():

The calling order of the "OnDestroy()" and "destructors" remains unchanged and that is as follows:

CStatusView::OnDestory (which parents the m_tabWnd)
    CTabs::OnDestroy() (which parents the m_general_view)
                ASSERTION FAILURE

When using Detach():
I get another Assertion Failure on line 386 in WinCore.cpp

AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
      // special message which identifies the window as                 using AfxWndProc
      if (nMsg == WM_QUERYAFXWNDPROC)
            return 1;

      // all other messages route through message map
      CWnd* pWnd =                 CWnd::FromHandlePermanent(hWnd);

**** This assert failes ****      
             ASSERT(pWnd != NULL);

      ASSERT(pWnd->m_hWnd == hWnd);
      return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);

The call stack is as follows:

AfxWndProc(HWND__ * 0x000005d8, unsigned int 2, unsigned int 0, long 0) line 368 + 25 bytes
AfxWndProcBase(HWND__ * 0x000005d8, unsigned int 2, unsigned int 0, long 0) line 203 + 21 bytes
KERNEL32! bff73663()
KERNEL32! bff928e0()

Associating a view with a tab is very logical: making it a child of the tab control is not very lgoical at all.  The tab control window and the view window don't have any ownership relationship, and have no reason to be created as parent/child.

Putting an instance of a class into member data of a class when the member is actually a parent of the child class is harder to justify.

Your stack traces are a great step in the right direction: now it's time to use them to debug.  Which window is it that is handling the message before the assertion happens?   You can use Spy++ to figure out what the handle of the tab control is, and the handle of the window being destroyed will show as the first parameter to AfxWndProc() in the callstack you see when you break on the assertion.

What's m_general_view?

For such a small amount of points, I don't usually write code.  But you can add

CTabCtrl m_wndTabs;

to your view's instance data, and then overrde these functions to get what you want:

#define TAB_CONTROL_ID 0x1002

void CBoomView::OnInitialUpdate()

   // Create the Tab Control

   CRect TabRect;

   CWnd* pParent = GetParent();
   TabRect.right += 2;

   ASSERT(pParent != NULL && IsWindow(pParent->m_hWnd));

   if (!m_wndTabs.Create(dwStyle, TabRect, pParent, TAB_CONTROL_ID))
      TRACE0("Failed to create tab control\n");

      TC_ITEM TabCtrlItem;
      TabCtrlItem.mask = TCIF_TEXT;
      TabCtrlItem.cchTextMax = 0;
      TabCtrlItem.pszText = "General";

      m_wndTabs.InsertItem(0, &TabCtrlItem);

void CBoomView::OnWindowPosChanging(WINDOWPOS* lpwndpos)
   if (IsWindow(m_wndTabs))
      m_wndTabs.SetWindowPos(NULL, 0, 0, lpwndpos->cx, 30, SWP_NOZORDER | SWP_NOMOVE);

   lpwndpos->cy -= 30;      // arbitrary height
   lpwndpos->y += 30;

   SetWindowPos(NULL, 0, 31, lpwndpos->cx, lpwndpos->cy, SWP_NOZORDER | SWP_NOSENDCHANGING);

void CBoomView::OnDestroy()

Of course, you don't need that #define, I don't think.  And you need to remember to hook up the appropriate message map entries.

.B ekiM


I appreciate your help on this...unfortunately I am new to the Expert Exchange and do not have alot of points to offer just yet!

Back to the problem at hand...In your sample code, you have a View which creates tab control.  This is very similar to what I am doing except that I sub-classed the tab control.  But you do not create any additional views for each of the tabs.  What my code does, is to create a new CFormView for each tab.  So to answer you question (What's m_general_view?)...m_general_view is a CFormView derived class that displays a dialog as a result to the "General" Tab being selected.  I also have a m_sscop_view which is a CFormView derived class that displays a dialog as a result of the "SSCOP" tab being press...and so on.  That is why it seemed logical to me to have the tab control parent the CFormView views.

Anyway to answer your other question...(Which window is it that is handling the message before the assertion happens?)

The CFormView derived class "GENERAL_VIEW" (or the class type for m_general_view) is handling the message as the destruction order shows: The order again is as follows:

1. CStatusView::OnDestory
         (CStatusView is the SDI's CView derived class which          parents the m_tabWnd and is a child of CMainFrame)

2. CTabs::OnDestroy()
         (CTabs is a CTabCtrl derived class which parents the                   m_general_view and is a child of CStatusView)

3. GENERAL_VIEW::OnDestroy()
        (GENERAL_VIEW is a CFormView derived class and is the         base class for m_general_view and is a child of CTabs)


I used Spy++ to verify what was shown in the call stack above and that is that the the Window's window created based on the GENERAL_VIEW base calss was being destroyed at the time when the Assert Failure occured.  NOTE: the destructors for the CStatusView and the CTabs class were not yet called.



I have made modifications to resolve the assertion failure, but I am still not sure why they worked.

CTabs had as member classes each of the CFormViews that were responsible for displaying each page.  They were defined as:

GENERAL_VIEW m_general_view;

The modifications that I made were to change the above definition to:

GENERAL_VIEW *m_general_view;

and in CTabs constructor:

m_general_view = new GENERAL_VIEW();

and I obviously had to use m_general_view as a pointer now..

Two questions:
1. Why does this not present the same assertion failure?
2. Why do I not need to call "delete(m_general_view);" in the CTabs destructor.

1) Because you're not deleting the same memory twice.

2) Because CWnd does it for you when the window is destroyed; the window is destroyed automatically because it's a child of another window.

.B ekiM


I understand why the assert is no longer failing...that part is obvious.  What I don't understand is why, based on the code changes that I made, the attempt to delete the memory does not occur twice!

Also, wasn't CWnd destroying the window and deleteing the memory in my initial source code?

I'm sorry: I can only answer the questions that you ask, not the questions that you really mean.  You asked, exactly, "why does this not present the same assertion failure", and that's what I answered.

In your first attempt, the CView window was being destroyed twice; first, because MFC got a WM_POSTNCDESTROY message for it, and second because the destructor of the CTabCtrl-derived class you had called the destructor of the CView-derived class it had as member data.

The second attempt doesn't have the member instance--it just has a member pointer.  So, when the tab-derived class is destroyed, it doesn't destroy again the view-derived class.

Again, having member data that reprsents a parent window is not a very normal design, and I'd strongly recommend that you revisit it.

By the way, how much work will you want me to do before you decide to award some points?

.B ekiM


And here I thought that you were really interested in helping me...and all along you were just interested in the points...
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.


Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.