搞了这么久的MFC开发,发现对GetDlgItem很无知,请教大家一下
假设我当前有一个对话框,这个对话框上有一个button控件,其ID为IDC_BUTTON1,那么,在这个对话框的OnInitDialog函数中,我们可以通过GetDlgItem(IDC_BUTTON1)来访问这个button控件。
现在的问题是:这个函数返回CWnd*, 我用typeid(*GetDlgItem(IDC_BUTTON1))来分析其真正的类型,居然发现是 class CWnd
也就是说,这个函数返回的CWnd*指针实际指向的仍然是一个CWnd对象,而不是一个Button对象,具体地说,就是:
CButton *pButton = dynamic_cast<CButton *>(GetDlgItem(IDC_BUTTON1));
pButton一直为NULL。
所以想问一下,这是为什么?为什么这个指针还是指向一个CWnd对象呢?为什么它不像多态性特征那样指向一个子类CButton对象呢?
[解决办法]
MFC维护一个窗口句柄到窗口类实例的Map,当你自己的代码生成(动态或者静态)了一个CWnd或者CWnd派生类的实例,并且将之与某个窗口句柄关联起来的时候(例如直接Create或者Attach或者通过DDX_CONTROL),MFC就会把你的这个实例的指针与窗口句柄放到这个Map里。
每当MFC需要返回一个CWnd指针的时候(例如GetFocus、GetParent等等),它先通过API获得所需的窗口句柄,然后再去这个Map里查找对应的类实例指针,如果找到,那么就把这个指针返回。此时这个指针究竟是什么类型,那是你自己的代码设定的(你当时关联这个窗口句柄的时候用的什么类型的窗口类实例,那就是什么类型,因为这个指针指向你自己的实例)。如果它在这个Map里查不到,那么MFC就会统一地临时生成一个CWnd实例,并与这个窗口句柄关联起来,然后把这个CWnd实例指针返回给你。此时,这个实例当然就是CWnd类型的,不论你的窗口本身究竟是个button、checkbox还是MDI Frame。显然,在这种情况下,是不能将该指针强制转换为CWnd的派生类的。
[解决办法]
- C/C++ code
inline CWnd* CWnd::GetDlgItem(int nID) const{ ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::GetDlgItem(m_hWnd, nID));}CWnd* PASCAL CWnd::FromHandle(HWND hWnd){ CHandleMap* pMap = afxMapHWND(TRUE); //create map if not exist ASSERT(pMap != NULL); CWnd* pWnd = (CWnd*)pMap->FromHandle(hWnd);#ifndef _AFX_NO_OCC_SUPPORT pWnd->AttachControlSite(pMap);#endif ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd); return pWnd;}