分类: WINDOWS
2007-01-18 09:11:06
Q : | MFC返回的临时对象指针成因? | ||
主要解答者: | QunKangLi | 提交人: | QunKangLi |
感谢: | oldforest、yu_hl、QunKangLi | ||
审核者: | happyparrot | 社区对应贴子: | |
A : | |||
msdn在介绍GetDlgItem的时候,明确指出: The returned pointer may be temporary and should not be stored for later use. 如果真是这样那我不惨了!在我的一个软件中,因为常常要使切换按钮的可用与不可用以及改变按钮文字,所以我用DetDlgItem把它们存了起来,用的时候直接到数组里面取,这样的话这个软件不是犯了一个重大的错误了?可是用这么久也没出错。 另外,它也是说可能是临时的,那一定在某种情况下不是临时的,高手能不能解释一下这个问题? --------------------------------------------------------------- // Most Windows objects are represented with a HANDLE, including // the most important ones, HWND, HDC, HPEN, HFONT etc. // We want C++ objects to wrap these handle based objects whenever we can. // Since Windows objects can be created outside of C++ (eg: calling // ::CreateWindow will return an HWND with no C++ wrapper) we must // support a reasonably uniform mapping from permanent handles // (i.e. the ones allocated in C++) and temporary handles (i.e. // the ones allocated in C, but passed through a C++ interface. // We keep two dictionaries for this purpose. The permanent dictionary // stores those C++ objects that have been explicitly created by // the developer. The C++ constructor for the wrapper class will // insert the mapping into the permanent dictionary and the C++ // destructor will remove it and possibly free up the associated // Windows object. // When a handle passes through a C++ interface that doesn't exist in // the permanent dictionary, we allocate a temporary wrapping object // and store that mapping into the temporary dictionary. // At idle time the temporary wrapping objects are flushed (since you better // not be holding onto something you didn't create). // 在CWinThread::OnIdle里调用AfxUnlockTempMaps,AfxUnlockTempMaps会释放temporary maps. 所以不要保留GetDlgItem等返回的临时指针,可以直接保存HWND objects,然后CWnd::FromHandle获取临时指针来用。 --------------------------------------------------------------- 临时包装对象会在空闲时间删除.在同一函数中多次使用是没用问题的,不同函数中使用先前保存下来的指针就有可能出错,特别是对话框初使化时候存下来的指针. 在MFC层次上的函数使用CWnd对象,而本地Windows代码(API)使用句柄.如:当Windows系统调用一个窗口过程时将传递一个HWND参数,而MFC本身的消息机制使用CWnd类;为了更好更高效的实现,MFC需要与Windows系统合作,将句柄与CWnd对象进行关联---它用CHandleMap完成关联. CHandleMap有两个CMapPtrToPtr的成员变量:m_permanentMap(永久映射表,程序运行过程中对象/句柄之间的关系),m_temporaryMap(临时映射表,在消息存在的过程中才存在).永久表保存显式创建的CWnd对象,当对象创建时将在永久目录下插入一个条目,在CWnd::OnNcDestrory时删除对应条目.但是Windows有时会传入某些不是由开发人员显式创建的窗口的句柄,此时MFC会分配一个临时对象来包装这个句柄并将它们的映射保存到临时映射表中,这些临时对象会在空闲时间被删除并移走相应的临时映射表条目.类似的MFC对象与Windows句柄的映射表有: m_pmapHWND: 窗口句柄与CWnd对象 m_pampHMENU: 菜单句柄与CMenu对象 m_pmapHDC: 设备环境句柄与CDC对象 m_pmapHGDIOBJ: GDI句柄与CGDI对象 m_mapHIMAGELIST: 图像链表句柄到CImageList对象 当给定句柄,如HWND时,MFC简单调用CWnd* PASCAL CWnd::FromHandle(HWND hWnd), 此函数内部使用CHandleMap::FromHandle(hWnd)获取相关联的CWnd对象.在CHandleMap::FromHandle(h)内部(源代码在WinHand.cpp),先使用CObject* pObject = LookupPermanent(h); if (pObject != NULL)return pObject; 检查永久表;如永久表中不存在,使用pObject = LookupTemporary(h)) != NULL检查临时表,都不存在时,使用pTemp = = m_pClass->CreateObject();if (pTemp == NULL)AfxThrowMemoryException();m_temporaryMap.SetAt((LPVOID)h, pTemp);创建临时对象并将其插入到临时表中去,同时返回该临时对象. void CHandleMap::RemoveHandle(HANDLE h)的注释说明临时对象将在空闲时由OnIdel释放: // remove only from permanent map -- temporary objects are removed // at idle in CHandleMap::DeleteTemp, always! 如果想不自动释放临时对象,使用void AFXAPI AfxLockTempMaps()/BOOL AFXAPI AfxUnlockTempMaps(BOOL bDeleteTemps)进行锁定. HANDLE是如HWND,HDC等的统称 HANDLE:句柄,是Windows用来表示对象的(不是C++的对象),HWND是其中一种,HWND是HANDLE,但HANDLE不只是HWND,更具体的查MSDN |