4

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?

IssamTP
  • 2,408
  • 1
  • 25
  • 48

3 Answers3

5

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.

xMRi
  • 14,982
  • 3
  • 26
  • 59
  • It seems, at least from my poor english, that you and @yupengzhang gave same answer, but you came first. I appreciated also the yu's answer because of examples and code. – IssamTP Nov 16 '17 at 10:32
  • I have a combo member variable. Code analysis said to use the dynamic instead of static. But for this combo the returned cast is null. This does not happen with c style cast. So what’s the solution so that my cast will work no matter what? Static? Reinterpret? – Andrew Truckle Oct 29 '21 at 04:36
  • What cast di you use the MFC DYNAMIC_CAST? Or MFC STATIC_CAST? Show a view code lines.... – xMRi Nov 03 '21 at 07:21
4

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.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Dean Seo
  • 5,486
  • 3
  • 30
  • 49
  • "_but this operation causes a crash for using null pointer, that means that cast have failed_" is not addressed. – zett42 Nov 16 '17 at 09:49
  • @zett42 `dynamic_cast` crashes sometimes on MFC, which was built when there was no `dynamic_cast`. A crash almost always comes from undefined behaviors, which is hard to dig in on SO, when the OP's development environment such as the version of Visual Studio, and OS, and also how MFC binaries were linked, is uncertain. But I'm sure it's just another undefined behavior after all. – Dean Seo Nov 16 '17 at 10:12
  • I usually do DDX_Control, but in this case I didn't want to add a member variable because I only need the access to it in OnInitDialog. – IssamTP Nov 16 '17 at 10:18
  • @zett42 Btw, think of my first sentence quoted `''`. :) – Dean Seo Nov 16 '17 at 10:27
  • @DeanSeo no, I haven't: I tend to not use MS's macros unless it's absolutely necessary. – IssamTP Nov 16 '17 at 10:34
  • The actual reason for the failed cast is explained by @xMRi. It's not because of undefined behaviour. – zett42 Nov 16 '17 at 10:57
  • @zett42 Yeah, probably I misunderstood the OP's question somewhat. So apparently the crash was from dereferencing a dangling pointer, not during `dynamic_cast`. I thought there was a crash in the middle of `dynamic_cast`. – Dean Seo Nov 16 '17 at 10:59
  • No, the crash comes from dereferencing a `NULL` pointer, because of the failed downcast. `GetDlgItem()` only returns a pointer to a temporary `CWnd` object. There is no `CEdit` object, so the downcast fails. – zett42 Nov 16 '17 at 11:02
  • @zett42 Exactly. So `dynamic_cast` was doing fine after all. – Dean Seo Nov 16 '17 at 11:03
  • Yes, I had no doubt about the correct behaviour of dynamic_cast, I was just wondering why. – IssamTP Nov 16 '17 at 13:33
  • As side note: I tried to used DYNAMIC_DOWNCAST macro: CButton* pMFCDynCast = DYNAMIC_DOWNCAST(CButton, GetDlgItem(IDC_CHK_K3C_COLORIFOGLIOPIENO)); and the result is still NULL, while the result is correct if I downcast a BCG item. – IssamTP Nov 16 '17 at 13:49
2
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.

  • Even if the `CWnd` is not of `CComboBox`, how is that a good enough reason for `dynamic_cast` to crash, instead of returning `nullptr`? – Dean Seo Nov 16 '17 at 10:17
  • 1
    @DeanSeo careful: I wrote that program crashes because of *using* a nullptr, so MFC returns nullptr correctly. I didn't checked nullptr in my program because I know exactly that IDC_COMBO1 is actually a CComboBox. yupengzhang is probably right. – IssamTP Nov 16 '17 at 10:24