dynamic_cast of CWnd to CComboBox 在 OnSize 函数中产生一个空指针

dynamic_cast of CWnd to CComboBox is yeilding a null pointer in OnSize function

代码:

void CChristianLifeMinistryEditorDlg::OnSize(UINT nType, int cx, int cy)
{
    CResizingDialog::OnSize(nType, cx, cy);

    const CWnd* pFocus = GetFocus();
    CComboBox* pFocusCombo = nullptr;

    if (pFocus != nullptr)
    {
        if (pFocus->GetParent()->IsKindOf(RUNTIME_CLASS(CComboBox)))
        {
            pFocusCombo = dynamic_cast<CComboBox*>(GetFocus()->GetParent());
        }
    }

    for (CWnd* pWnd = GetWindow(GW_CHILD); pWnd != nullptr; pWnd = pWnd->GetNextWindow(GW_HWNDNEXT))
    {
        if (pWnd == pFocusCombo)
        {
            // TODO: Sadly, by now, the control has already got all the text selected.
            //pFocusCombo->SetEditSel(LOWORD(dwEditSel), HIWORD(dwEditSel));
        }
        else if (pWnd->IsKindOf(RUNTIME_CLASS(CComboBox)))
        {
            // This only works for combo boxes that are bound to controls
            auto* pCombo = dynamic_cast<CComboBox*>(pWnd);
            pCombo->SetEditSel(-1, -1);
        }
        else
        {
            CString strClassName;
            if (::GetClassName(pWnd->GetSafeHwnd(), strClassName.GetBuffer(_MAX_PATH), _MAX_PATH))
            {
                if (strClassName == _T("ComboBox"))
                {
                    auto* pCombo = (CComboBox*)pWnd;
                    //auto* pCombo = dynamic_cast<CComboBox*>(pWnd);
                    pCombo->SetEditSel(-1, -1);
                }
            }
            strClassName.ReleaseBuffer();
        }
    }

    if (m_pHtmlPreview != nullptr)
    {
        m_lblHtmlPreview.GetWindowRect(m_rctHtmlPreview);
        ScreenToClient(m_rctHtmlPreview);

        m_pHtmlPreview->MoveWindow(m_rctHtmlPreview);
    }
}

我显示上下文的整个函数。但我对这一点特别感兴趣:

CString strClassName;
if (::GetClassName(pWnd->GetSafeHwnd(), strClassName.GetBuffer(_MAX_PATH), _MAX_PATH))
{
    if (strClassName == _T("ComboBox"))
    {
        auto* pCombo = (CComboBox*)pWnd;
        //auto* pCombo = dynamic_cast<CComboBox*>(pWnd);
        pCombo->SetEditSel(-1, -1);
    }
}
strClassName.ReleaseBuffer();

在代码分析更新期间,我遇到过很多不得不更新 C 风格转换的情况。很多时候我可以使用 static_cast,但在某些情况下,编译器会告诉我应该使用 dynamic_cast.

然后我发现我的应用程序没有正常工作,在调试模式下将它隔离到这个位:

//auto* pCombo = (CComboBox*)pWnd;
auto* pCombo = dynamic_cast<CComboBox*>(pWnd);

事实证明,转换指针 pCombonull。然而,当我使用 C-Style cast 时,这从未发生过。结果我又回到了 C-Style 阵容。我看到了这个讨论()但是我看不出是这个原因(临时指针)。

在这种情况下我应该使用什么演员可以依靠?

C-style casting会尝试不同的c++ casting,可能会选择reinterpret_caststatic_cast。这是将 CWnd* 转换为 CComboBox*,例如:

CWnd* wnd = GetDlgItem(IDC_COMBO1);
CComboBox* combo = (CComboBox*)wnd;

一般来说,父 class 不能“总是”转换为子。这取决于是否为该 ID 创建了 CComboBox m_combobox;

A) m_combobox确实存在:

在这种情况下,我们的 wnd 可以引用 m_combobox

CWnd::GetDlgItem 等将 &m_combobox 转换为 CWnd*,他们传递它。

dynamic_cast 检查它并转换回 CComboBox*.

MFC 的 IsKindOf 将确认是否已创建 m_combobox

B) m_combobox 不存在:

在这种情况下,我们的 wndCWnd* 对象。 MFC 从未为该控件创建 CComboBox

dynamic_cast 测试,无法转换为 CComboBox*

如果 wnd 的 class 名称是 "ComboBox"

static_cast 有效


下面的代码应该没问题。在这种情况下,您可以根据需要跳过dynamic_cast,依靠static_cast。但如果可能,最好使用dynamic_cast

CComboBox* ptr = nullptr;
if (wnd->IsKindOf(RUNTIME_CLASS(CComboBox)))
    ptr = dynamic_cast<CComboBox*>(wnd);
if(!ptr)
{
    CString classname;
    ::GetClassName(wnd->m_hWnd, classname.GetBuffer(_MAX_PATH), _MAX_PATH);
    classname.ReleaseBuffer();
    if(classname == L"ComboBox")
        ptr = static_cast<CComboBox*>(wnd);
}