CFormView parented by a CTabCtrl

Posted on 1997-06-04
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 += 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;
Question by:YeahRight
  • 6
  • 4
LVL 11

Accepted Solution

mikeblas earned 130 total points
ID: 1301966
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


Author Comment

ID: 1301967
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()


Author Comment

ID: 1301968
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()

NAS Cloud Backup Strategies

This article explains backup scenarios when using network storage. We review the so-called “3-2-1 strategy” and summarize the methods you can use to send NAS data to the cloud

LVL 11

Expert Comment

ID: 1301969
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


Author Comment

ID: 1301970
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.


Author Comment

ID: 1301971

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.

LVL 11

Expert Comment

ID: 1301972
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

Author Comment

ID: 1301973
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?
LVL 11

Expert Comment

ID: 1301974
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


Author Comment

ID: 1301975
And here I thought that you were really interested in helping me...and all along you were just interested in the points...

Featured Post

Simplifying Server Workload Migrations

This use case outlines the migration challenges that organizations face and how the Acronis AnyData Engine supports physical-to-physical (P2P), physical-to-virtual (P2V), virtual to physical (V2P), and cross-virtual (V2V) migration scenarios to address these challenges.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
string initialization in java 11 114
Modbus - whats the maximum I can store in one register? 4 80
unix example issues 18 91
Is there a simple front-end menu system. 9 90
Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Nobody understands Phishing better than an anti-spam company. That’s why we are providing Phishing Awareness Training to our customers. According to a report by Verizon, only 3% of targeted users report malicious emails to management. With compan…

831 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