2
votes

An application I'm porting from Visual C++ 6.0 to the MFC version in VC++2008, does a lot of get-AfxGetMainWnd()-and-do-C-Style-casts on it like this:

  CUserInterfaceDoc *m_pDoc = (CUserInterfaceDoc*)((CFrameWnd *)AfxGetMainWnd())
                            ->GetActiveDocument();

When I try the steps above, simply converting to dynamic_cast<>, I find that the Main Window is no longer accessible via casting-to-a-CUserInterfaceDoc in my program. I think that perhaps MFC had abused casts in VC++ 6, or that the compiler did some magic.

Rewriting the above to use dynamic casts would go like this, and it ends up with a nil pointer, which trips the assert() conditions I wrote here, as I want it to:

CUserInterfaceDoc *m_pDoc;
CWnd * mainWnd = AfxGetMainWnd();
assert(mainWnd);
CFrameWnd *frmWnd = dynamic_cast<CFrameWnd *> (mainWnd);
assert(frmWnd);
m_pDoc = dynamic_cast<CUserInterfaceDoc *> (frmWnd);
assert(m_pDoc);

I'm assuming that everybody who has done this before will be able to say "Yeah, of course, they changed that casting behaviour...." but I can't find the documentation for it.

Where possible I'm changing my C++ classes to have a member variable (field) where I store
links to things that it was formerly finding by walking down from the "more or less global" AfxMainWnd().

It would help me to know what changed though, so I can understand what's going on above.

CUserInterfaceDoc is my application's MFC C++ Document class, it is a COleServerDoc which used to be "findable" at runtime with the gross C-style cast at the top.

The above cast still compiles in modern C++ but it's broken, probably due to the fact that the old VC++ 6 compiler did some kind of internal 'magic' with C-style casts that was against the ISO C++ standards. (That's purely a guess on my part).

My question is in two parts:

  1. What's the usual way to get at the CurrentDocument (CFrameWnd::CurrentDocument) from another class that currently has no reference to the main CFrameWnd and had been using the hack I show at the top of this question?

  2. What changed between the non-ISO-compliant VC++ 6 and the more-or-less ISO-compliant later C++ versions that would change the behaviour of the cast expressions above, or did the breakage occur due to MFC internal architecture changes?

My code change:

CUserInterfaceDoc * CMyBrowser::GetUserInterfaceDoc()
{
    CUserInterfaceDoc *m_pDoc;
    // formerly did this, which works in VC++6 and doesn't work anymore:
    //m_pDoc = (CUserInterfaceDoc*)((CFrameWnd *)AfxGetMainWnd())->GetActiveDocument();
    assert(m_pMainFrm);
    m_pDoc = dynamic_cast<CUserInterfaceDoc *> ( m_pMainFrm->GetActiveDocument() );
    assert(m_pDoc);
}
2

2 Answers

4
votes

If you are using MFC, you might as well just bite the bullet and use DYNAMIC_DOWNCAST which is an MFC defined macro for casting that's basically equivalent to dynamic_cast.

CFrameWnd* pFrm = DYNAMIC_DOWNCAST(CFrameWnd, AfxGetApp()->m_pMainWnd);

m_pDoc = DYNAMIC_CAST(CUserInterfaceDoc, m_pMainFrm->GetActiveDocument());
1
votes

In your first rewrite:

CUserInterfaceDoc *m_pDoc;
CWnd * mainWnd = AfxGetMainWnd();
assert(mainWnd);
CFrameWnd *frmWnd = dynamic_cast<CFrameWnd *> (mainWnd);
assert(frmWnd);
m_pDoc = dynamic_cast<CUserInterfaceDoc *> (frmWnd);
assert(m_pDoc);

... you simply missed the GetActiveDocument() call which was there in the C-Style cast. So it should work like this:

CUserInterfaceDoc *m_pDoc;
CWnd * mainWnd = AfxGetMainWnd();
assert(mainWnd);
CFrameWnd *frmWnd = dynamic_cast<CFrameWnd *> (mainWnd);
assert(frmWnd);    
m_pDoc = dynamic_cast<CUserInterfaceDoc *> (frmWnd->GetActiveDocument());
assert(m_pDoc);

DYNAMIC_DOWNCAST is soo oldschool and actually no longer required if you enable RTTI (which is on by default). See also: MFC DYNAMIC_DOWNCAST vs. dynamic_cast