我们在调用CWnd::GetDlgItem()函数时,MSDN告诉我们:The returned pointer may be temporary and should not be stored for later use.
中文意思就是:返回的指针可能是临时的并且最好不要保存起来放到以后用。
猜测:返回的指针既然可能是临时的,那么可能是非临时的(永久的),最好不要保存起来放到以后用(有时候可以保存起来)
源码面前,了无秘密。让我们深入MFC源代码去看个究竟。
 
先随便建立一个Dialog程序,然后在窗体上拉一个按钮,添加按钮事件,在按钮事件里写上如下代码:GetDlgItem(IDC_BUTTON1);然后给这行代码加上断点。好,我们开始进去看看,运行程序,按下按钮,程序就停在刚才的断点出,然后F11进去。
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);
}
 
再跟踪到红色代码中去:
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;
}
 
再一次跟踪进入红色函数里去:
CHandleMap* PASCAL afxMapHWND(BOOL bCreate)
{
   AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
   if (pState->m_pmapHWND == NULL && bCreate)
   {
   BOOL bEnable = AfxEnableMemoryTracking(FALSE);
#ifndef _AFX_PORTABLE
   _PNH pnhOldHandler = AfxSetNewHandler(&AfxCriticalNewHandler);
#endif
   pState->m_pmapHWND = new CHandleMap(RUNTIME_CLASS(CTempWnd),
   offsetof(CWnd, m_hWnd));
 
#ifndef _AFX_PORTABLE
   AfxSetNewHandler(pnhOldHandler);
#endif
   AfxEnableMemoryTracking(bEnable);
   }
   return pState->m_pmapHWND;
}
 
看一下AFX_MODULE_THREAD_STATE的定义:
// AFX_MODULE_THREAD_STATE (local to thread *and* module)
class AFX_MODULE_THREAD_STATE : public CNoTrackObject
{
public:
   AFX_MODULE_THREAD_STATE();
   virtual ~AFX_MODULE_THREAD_STATE();
 
   // current CWinThread pointer
   CWinThread* m_pCurrentWinThread;
 
   // list of CFrameWnd objects for thread
   CTypedSimpleList<CFrameWnd*> m_frameList;
 
   // temporary/permanent map state
   DWORD m_nTempMapLock; // if not 0, temp maps locked
   CHandleMap* m_pmapHWND;
   CHandleMap* m_pmapHMENU;
   CHandleMap* m_pmapHDC;
   CHandleMap* m_pmapHGDIOBJ;
   CHandleMap* m_pmapHIMAGELIST;
 
   // thread-local MFC new handler (separate from C-runtime)
   _PNH m_pfnNewHandler;
 
#ifndef _AFX_NO_SOCKET_SUPPORT
   // WinSock specific thread state
   HWND m_hSocketWindow;
#ifdef _AFXDLL
   CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapSocketHandle;
   CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapDeadSockets;
   CEmbeddedButActsLikePtr<CPtrList> m_plistSocketNotifications;
#else
   CMapPtrToPtr* m_pmapSocketHandle;
   CMapPtrToPtr* m_pmapDeadSockets;
   CPtrList* m_plistSocketNotifications;
#endif
#endif
};
 
看一下黄色的那一行代码,很明显,MFC说临时的map可以被锁定,先记住就可以。
看一下黄色下面的代码:
   CHandleMap* m_pmapHWND;
   CHandleMap* m_pmapHMENU;
   CHandleMap* m_pmapHDC;
   CHandleMap* m_pmapHGDIOBJ;
   CHandleMap* m_pmapHIMAGELIST;
很明显,这里放了一些临时的映射,包括HWND到CWnd,HDC到CDC的,等等。
并且,MFC除了这些临时的映射表之外,还有永久的映射表。
也就是说,GetDlgItem以及FromHandle等函数返回的CWnd以及CDC等指针是可以保存的,不管是临时的map还是永久 map中的,我们都可以安全的保存,前提是把临时的map锁定,那么不管怎么样,返回的指针可以在任何时候都是安全的。默认的,MFC是在空闲时间里把临 时map里的东西清空掉的。
 
刚才说到在空闲时间MFC会把临时的map删除掉,我们在刚才那个按钮事件里添上如下代码:
AfxGetApp()->OnIdle(1);
 
然后运行并跟踪到如下函数:
BOOL CWinApp::OnIdle(LONG lCount)
{
   if (lCount <= 0)
   {
   CWinThread::OnIdle(lCount);
 
   // call doc-template idle hook
   POSITION pos = NULL;
   if (m_pDocManager != NULL)
   pos = m_pDocManager->GetFirstDocTemplatePosition();
 
   while (pos != NULL)
   {
   CDocTemplate* pTemplate = m_pDocManager->GetNextDocTemplate(pos);
   ASSERT_KINDOF(CDocTemplate, pTemplate);
   pTemplate->OnIdle();
   }
   }
   else if (lCount == 1)
   {
   VERIFY(!CWinThread::OnIdle(lCount));
   }
   return lCount < 1; // more to do if lCount < 1
}
 
再跟踪进上面的红色函数中,整个函数如下:
BOOL CWinThread::OnIdle(LONG lCount)
{
   ASSERT_VALID(this);
 
#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
   // check MFC's allocator (before idle)
   if (_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_CHECK_ALWAYS_DF)
   ASSERT(AfxCheckMemory());
#endif
 
   if (lCount <= 0)
   {
   // send WM_IDLEUPDATECMDUI to the main window
   CWnd* pMainWnd = m_pMainWnd;
   if (pMainWnd != NULL && pMainWnd->m_hWnd != NULL &&
   pMainWnd->IsWindowVisible())
   {
   AfxCallWndProc(pMainWnd, pMainWnd->m_hWnd,
   WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0);
   pMainWnd->SendMessageToDescendants(WM_IDLEUPDATECMDUI,
   (WPARAM)TRUE, 0, TRUE, TRUE);
   }
   // send WM_IDLEUPDATECMDUI to all frame windows
   AFX_MODULE_THREAD_STATE* pState = _AFX_CMDTARGET_GETSTATE()->m_thread;
   CFrameWnd* pFrameWnd = pState->m_frameList;
   while (pFrameWnd != NULL)
   {
   if (pFrameWnd->m_hWnd != NULL && pFrameWnd != pMainWnd)
   {
   if (pFrameWnd->m_nShowDelay == SW_HIDE)
   pFrameWnd->ShowWindow(pFrameWnd->m_nShowDelay);
   if (pFrameWnd->IsWindowVisible() ||
   pFrameWnd->m_nShowDelay >= 0)
   {
   AfxCallWndProc(pFrameWnd, pFrameWnd->m_hWnd,
   WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0);
   pFrameWnd->SendMessageToDescendants(WM_IDLEUPDATECMDUI,
   (WPARAM)TRUE, 0, TRUE, TRUE);
   }
   if (pFrameWnd->m_nShowDelay > SW_HIDE)
   pFrameWnd->ShowWindow(pFrameWnd->m_nShowDelay);
   pFrameWnd->m_nShowDelay = -1;
   }
   pFrameWnd = pFrameWnd->m_pNextFrameWnd;
   }
   }
   else if (lCount >= 0)
   {
   AFX_MODULE_THREAD_STATE* pState = _AFX_CMDTARGET_GETSTATE()->m_thread;
   if (pState->m_nTempMapLock == 0)
   {
  // free temp maps, OLE DLLs, etc.
   AfxLockTempMaps();
   AfxUnlockTempMaps();
   }
   }
 
#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
   // check MFC's allocator (after idle)
   if (_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_CHECK_ALWAYS_DF)
   ASSERT(AfxCheckMemory());
#endif
 
 
   return lCount < 0; // nothing more to do if lCount >= 0
}
 
怎么样,当了老半天的间谍了,总算能看出点门道了吧?上面3行红色的代码说的很清楚,这里会把临时的map释放掉。
两个函数的代码如下:
AfxLockTempMaps();
AfxUnlockTempMaps();
 
void AFXAPI AfxLockTempMaps()
{
   AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
   ++pState->m_nTempMapLock;
}
 
BOOL AFXAPI AfxUnlockTempMaps(BOOL bDeleteTemps)
{
   AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
   if (pState->m_nTempMapLock != 0 && --pState->m_nTempMapLock == 0)
   {
   if (bDeleteTemps)
   {
   if (bDeleteTemps != -1)
   {
   // allow COM libraries to be freed
   CWinThread* pThread = AfxGetThread();
   if (pThread != NULL && pThread->m_lpfnOleTermOrFreeLib != NULL)
   (*pThread->m_lpfnOleTermOrFreeLib)(FALSE, FALSE);
   }
 
   // clean up temp objects
   pState->m_pmapHGDIOBJ->DeleteTemp();
   pState->m_pmapHDC->DeleteTemp();
   pState->m_pmapHMENU->DeleteTemp();
   pState->m_pmapHWND->DeleteTemp();
   pState->m_pmapHIMAGELIST->DeleteTemp();
   }
 
#ifndef _AFX_PORTABLE
   CWinApp* pApp = AfxGetApp();
   _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
   // restore safety pool after temp objects destroyed
   if (pApp != NULL &&
   (pThreadState->m_pSafetyPoolBuffer == NULL ||
   _msize(pThreadState->m_pSafetyPoolBuffer) < pApp->m_nSafetyPoolSize) &&
   pApp->m_nSafetyPoolSize != 0)
   {
   // attempt to restore the safety pool to its max size
   size_t nOldSize = 0;
   if (pThreadState->m_pSafetyPoolBuffer != NULL)
   {
   nOldSize = _msize(pThreadState->m_pSafetyPoolBuffer);
   free(pThreadState->m_pSafetyPoolBuffer);
   }
 
   // undo handler trap for the following allocation
   BOOL bEnable = AfxEnableMemoryTracking(FALSE);
   pThreadState->m_pSafetyPoolBuffer = malloc(pApp->m_nSafetyPoolSize);
   if (pThreadState->m_pSafetyPoolBuffer == NULL)
   {
   TRACE1("Warning: failed to reclaim %d bytes for memory safety pool./n",
   pApp->m_nSafetyPoolSize);
   // at least get the old buffer back
   if (nOldSize != 0)
   {
   //get it back
   pThreadState->m_pSafetyPoolBuffer = malloc(nOldSize);
   ASSERT(pThreadState->m_pSafetyPoolBuffer != NULL);
   }
   }
   AfxEnableMemoryTracking(bEnable);
   }
#endif // !_AFX_PORTABLE
   }
 
   // return TRUE if temp maps still locked
   return pState->m_nTempMapLock != 0;
}
 
看到上面黄色的代码了吧,这时候你还敢保存GetDlgItem或者FromHandle返回的指针吗?
刚才你不是说可以锁定临时map的吗?是的,可以用AfxLockTempMaps锁定,但是必须与AfxUnlockTempMaps配对使用,其内部有一个计数变量。MSDN上没这个函数,最好不要用它,否则除了什么问题我可不保证哦J
 
永久的map存放的应该就是我们显示创建的例如CWnd此类的对象了。
其实很多函数如FromHandle、FindWindow等函数都是用到了这种技术,所以MSDN必定有这样类似这样的一句话:The returned pointer may be temporary and should not be stored for later use.

FromHandle临时对象一探究竟的更多相关文章

  1. 与临时对象的斗争(上)ZZ

    C++ 是一门以效率见长的语言(虽然近来越来越多的人“不齿”谈及效率,我深以为不然,在某一次的程序编写中不对效率锱铢必较并不意味意味着我们就不应该追求更多的更好的做法).总之吧,相比起其它语言,程序员 ...

  2. 【编程篇】C++11系列之——临时对象分析

    /*C++中返回一个对象时的实现及传说中的右值——临时对象*/ 如下代码: /**********************************************/ class CStuden ...

  3. SQL Server 内置函数、临时对象、流程控制

    SQL Server 内置函数 日期时间函数 --返回当前系统日期时间 select getdate() as [datetime],sysdatetime() as [datetime2] getd ...

  4. 认识C++中的临时对象temporary object 分类: C/C++ 2015-05-11 23:20 137人阅读 评论(0) 收藏

    C++中临时对象又称无名对象.临时对象主要出现在如下场景. 1.建立一个没有命名的非堆(non-heap)对象,也就是无名对象时,会产生临时对象. Integer inte= Integer(5); ...

  5. C++中临时对象的学习笔记

    http://www.cppblog.com/besterChen/category/9573.html 所属分类: C/C++/STL/boost  在函数调用的时候,无论是参数为对象还是返回一个对 ...

  6. C++ 临时对象

    1.什么是临时对象? swap方法中,常常定义一个temp对象,这个temp对象不是临时对象,而是局部对象.这里所说的临时对象是不可见的,在原代码中是看不到的. 2.为什么会产生临时对象? a.客户期 ...

  7. 【M19】了解临时对象的来源

    1.首先,确认什么是临时对象.在swap方法中,建立一个对象temp,程序员往往把temp称为临时对象.实际上,temp是个局部对象.C++中所谓的临时对象是不可见的,产生一个non-heap对象,并 ...

  8. STL——临时对象的产生与运用

    所谓临时对象,就是一种无名对象.它的出现如果不在程序员的预期之下(例如任何pass by value操作都会引发copy操作,于是形成一个临时对象),往往造成效率上的负担.但有时候刻意制造一些临时对象 ...

  9. [转] C++中临时对象及返回值优化

    http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...

随机推荐

  1. 2. HTML常用标签

    相信大家常常会打开浏览器搜索一些内容或者浏览一些网站,在浏览器的页面上会呈现很多内容,但是具体的形式无非就是图片.文字以及链接(可以点击进入另一个页面的特殊文字),其中文字承载着巨大的作用,传递着各种 ...

  2. SHOPEX快递物流单号查询插件

    本SHOPEX快递物流单号跟踪插件提供国内外近2000家快递物流订单单号查询服务例如申通快递.顺丰快递.圆通快递.EMS快递.汇通快递.宅急送快递.德邦物流.百世快递.汇通快递.中通快递.天天快递等知 ...

  3. ON DUPLICATE KEY UPDATE 插入or更新

    mean:若数据表中存在以相同主键的记录,就更新该条记录.否则就插入一条新的记录. 单条:INSERT INTO tablename (`field1`,`field2`) VALUES(value1 ...

  4. STM32(11)——DMA

    简介: DMA:Direct Memory Access,直接存储器访问.DMA传输数据从一个地址空间复制到另外一个地址空间.当CPU初始化这个传输动作,传输动作本身就是DMA控制器来实现和完成.典型 ...

  5. Python递归二分法

    # lst = [4, 56, 178, 253, 625, 1475, 2580, 3574, 15963] # 时间复杂度. n# # 让用户输入一个数n. 判断这个n是否出现在lst中# n = ...

  6. 北京Uber优步司机奖励政策(3月2日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  7. 北京Uber优步司机奖励政策(12月18日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  8. CF 643 E. Bear and Destroying Subtrees

    E. Bear and Destroying Subtrees http://codeforces.com/problemset/problem/643/E 题意: Q个操作. 加点,在原来的树上加一 ...

  9. Nginx+Tomcat多站点访问默认主页问题-狒狒完美解决-Q9715234

    <Engine name="Catalina" defaultHost="www.abc.com"> <Host name="www ...

  10. 使用Unity创建依赖注入

        这篇文章翻译自<Dependency Injection With Unity>第三章.文中提到的类似"前几节"的内容您不必在意,相信您可以看懂的. P.S:如 ...