IssamTP
IssamTP

Reputation: 2440

MFC Classes and C++ style casts

I'm wondering if I haven't fully understood C++ casts versus old C-Style cast. In MFC I have this method:

CWnd * GetDlgItem(UINT uResId);

I'm expecting that a CComboBox (or CEdit), which is derived from CWnd, requires this kind of cast:

dynamic_cast<CComboBox *>(GetDlgItem(IDC_COMBO1));
// for CEdit:
dynamic_cast<CEdit *>(GetDlgItem(IDC_EDIT1));

but this operation causes a crash for using null pointer, that means that cast have failed. Using:

reinterpret_cast<CComboBox *>(GetDlgItem(IDC_COMBO1));
// for CEdit:
reinterpret_cast<CEdit *>(GetDlgItem(IDC_EDIT1));

fixes the problem, but I'm disappointed. What am I missing?

Upvotes: 4

Views: 1755

Answers (3)

Dean Seo
Dean Seo

Reputation: 5713

I'm wondering if I haven't fully understood C++ casts versus old C-Style cast. In MFC I have this method:

Probably you do understand the difference well, but MFC had been released before the RTTI in the C++ standard, having its own support for RTTI, which doesn't meet the standard way.

So alternatively, you could use DYNAMIC_DOWNCAST instead as follows:

DYNAMIC_DOWNCAST(CEdit, GetDlgItem(IDC_EDIT1));

The common practice for this, however, is not to cast, but to create a member variable that represents your MFC control, using DDX_Control, which you can easily accomplish by doing Right Click, and selecting Add Variable... or through MFC Class Wizard.

EDIT

So I misunderstood an essential part of the OP's question about when the crash occurs. The crash is because of dereferencing nullptr, the valid result of dynamic_cast, not the dynamic_cast itself.

@xMRi answers why it crashes in detail.

Upvotes: 4

yupengzhang
yupengzhang

Reputation: 54

class A {
public:
    A() {};

    virtual ~A(){}
};

class B : public A {
public:
    B() {};
    virtual ~B() {}
};

int main()
{

    A* a = new A();
    B* b = dynamic_cast<B*>(a);
    // b is 0
    return 0;
}

As in winocc.cpp

CWnd* CWnd::GetDlgItem(int nID) const
{
    ASSERT(::IsWindow(m_hWnd));

    if (m_pCtrlCont == NULL)
        return CWnd::FromHandle(::GetDlgItem(m_hWnd, nID));
    else
        return m_pCtrlCont->GetDlgItem(nID);
}

and wincore.cpp

CWnd* PASCAL CWnd::FromHandle(HWND hWnd)
{
    CHandleMap* pMap = afxMapHWND(TRUE); //create map if not exist
    ASSERT(pMap != NULL);
    CWnd* pWnd = (CWnd*)pMap->FromHandle(hWnd);

    pWnd->AttachControlSite(pMap);

    ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);
    return pWnd;
}

When CHandleMap contains the object is CWnd not a CComboBox or any other derived class type, it will not ok by using dynamic_cast to cast down.

Upvotes: 2

xMRi
xMRi

Reputation: 15375

The problem is that GetDlgItem may return a temporary CWnd* pointer.

If the window is a CWnd derived class and the window is created with CWnd::Create(Ex) or the window is subclassed, RTTI will work.

When the window is created by Windows (due to a dialog template) and the Window isn't subclassed by the MFC (with DDX_Control ow CWnd::SubclassWindow), GetDlgItem just returns a temporary CWnd*, with CWnd::FromHandle. This window handle is always of the base type CWnd.

If you want to check if this window is really a Edit control you can use CWnd::GetClassName. Casting to a CEdit* is safe and convenient because a CEdit control communicates with it's HWND counterpart just via Window messages. So this works for all basic integrated window classes.

Upvotes: 5

Related Questions