首页
社区
课程
招聘
[旧帖] [原创]新查出的一个BUG,和MFC注册窗口类管理相关 0.00雪花
发表于: 2010-4-1 16:14 2685

[旧帖] [原创]新查出的一个BUG,和MFC注册窗口类管理相关 0.00雪花

2010-4-1 16:14
2685

最近我维护的一个项目出了问题,会莫名其妙地崩溃,分析崩溃现场,发现是内存被莫名其妙冲坏,但是对调查没有任何帮助,今天终于查到了原因,和大家分享,希望大家引以为戒:

MSDN上说,在DLL中注册的窗口类在DLL卸载时不会自动反注册,我们程序必须保证在DLL卸载时,将所有在DLL中注册的窗口类反注册掉(原文:Windows NT/2000 or later: No window classes registered by a .dll are unregistered when the .dll is unloaded. A .dll must explicitly unregister its classes when it is unloaded. )。

MFC也是这样做的,只要我们的DLL中都使用AfxRegisterWndClass来注册窗口类,就不会出问题,MFC框架能够保证在DLL卸载时将所有在DLL中注册的窗口类反注册掉,所以微软建议在DLL中注册窗口类使用AfxRegisterWndClass调用注册窗口类。

MFC根据AfxRegisterWndClass提供的参数组装成一个窗口类名,并且使用该窗口类名注册一个窗口类,如过该窗口是在DLL中注册的,注册成功后会将该窗口类名记录到MFC的一个内部变量中,该变量是一个4096大小的字符数组,各个窗口类名之间使用”\n\0” 分隔。

为了防止相同类型的窗口类被反复注册,AfxRegisterWndClass会先检查根据参数组装的窗口类名的窗口类是否已经注册,如果已经注册,AfxRegisterWndClass就会直接返回那个窗口类名。

MFC记录下窗口类名的目的是为了在DLL卸载时,将所有在DLL中注册的窗口类都反注册掉,否则会有问题。注意,如果DLL中注册的窗口类超过了MFC设计容限,反注册列表会溢出,进而导致程序崩溃,没有任何警告,往往在运行中某一时刻爆发,这种缺陷是比较隐蔽的,再现性一般不确定,往往修正成本很高。

我们的项目的某些操作会无节制地注册大量的窗口类,会轻易超过了微软设计人员的设计界限,导致数组越界,进而导致程序崩溃。

下面附上MFC相关源代码,代码比较简单明了,很容易理解:

LPCTSTR AFXAPI AfxRegisterWndClass(UINT nClassStyle,
        HCURSOR hCursor, HBRUSH hbrBackground, HICON hIcon)
{
        // Returns a temporary string name for the class
        //  Save in a CString if you want to use it for a long time
        LPTSTR lpszName = AfxGetThreadState()->m_szTempClassName;

        // generate a synthetic name for this class
        HINSTANCE hInst = AfxGetInstanceHandle();
        if (hCursor == NULL && hbrBackground == NULL && hIcon == NULL)
                wsprintf(lpszName, _T("Afx:%x:%x"), (UINT)hInst, nClassStyle);
        else
                wsprintf(lpszName, _T("Afx:%x:%x:%x:%x:%x"), (UINT)hInst, nClassStyle,
                        (UINT)hCursor, (UINT)hbrBackground, (UINT)hIcon);

        // see if the class already exists
        WNDCLASS wndcls;
        if (::GetClassInfo(hInst, lpszName, &wndcls))
        {
                // already registered, assert everything is good
                ASSERT(wndcls.style == nClassStyle);

                // NOTE: We have to trust that the hIcon, hbrBackground, and the
                //  hCursor are semantically the same, because sometimes Windows does
                //  some internal translation or copying of those handles before
                //  storing them in the internal WNDCLASS retrieved by GetClassInfo.
                return lpszName;
        }

        // otherwise we need to register a new class
        wndcls.style = nClassStyle;
        wndcls.lpfnWndProc = DefWindowProc;
        wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
        wndcls.hInstance = hInst;
        wndcls.hIcon = hIcon;
        wndcls.hCursor = hCursor;
        wndcls.hbrBackground = hbrBackground;
        wndcls.lpszMenuName = NULL;
        wndcls.lpszClassName = lpszName;
        if (!AfxRegisterClass(&wndcls))
                AfxThrowResourceException();

        // return thread-local pointer
        return lpszName;
}

// like RegisterClass, except will automatically call UnregisterClass
BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass)
{
        WNDCLASS wndcls;
        if (GetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName,
                &wndcls))
        {
                // class already registered
                return TRUE;
        }

        if (!::RegisterClass(lpWndClass))
        {
                TRACE1("Can't register window class named %s\n",
                        lpWndClass->lpszClassName);
                return FALSE;
        }

        if (afxContextIsDLL)
        {
                AfxLockGlobals(CRIT_REGCLASSLIST);
                TRY
                {
                        // class registered successfully, add to registered list
                        AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
                        LPTSTR lpszUnregisterList = pModuleState->m_szUnregisterList;
                        // the buffer is of fixed size -- ensure that it does not overflow
                        ASSERT(lstrlen(lpszUnregisterList) + 1 +
                                lstrlen(lpWndClass->lpszClassName) + 1 <
                                _countof(pModuleState->m_szUnregisterList));
                        // append classname + newline to m_szUnregisterList
                        lstrcat(lpszUnregisterList, lpWndClass->lpszClassName);
                        TCHAR szTemp[2];
                        szTemp[0] = '\n';
                        szTemp[1] = '\0';
                        lstrcat(lpszUnregisterList, szTemp);
                }
                CATCH_ALL(e)
                {
                        AfxUnlockGlobals(CRIT_REGCLASSLIST);
                        THROW_LAST();
                        // Note: DELETE_EXCEPTION not required.
                }
                END_CATCH_ALL
                AfxUnlockGlobals(CRIT_REGCLASSLIST);
        }

        return TRUE;
}

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
        if (dwReason == DLL_PROCESS_ATTACH)
        {
                BOOL bResult = FALSE;

#ifdef _AFXDLL
                // wire up resources from core DLL
                AfxCoreInitModule();
#endif

                _AFX_THREAD_STATE* pState = AfxGetThreadState();
                AFX_MODULE_STATE* pPrevModState = pState->m_pPrevModuleState;

                // Initialize DLL's instance(/module) not the app's
                if (!AfxWinInit(hInstance, NULL, _T(""), 0))
                {
                        AfxWinTerm();
                        goto Cleanup;       // Init Failed
                }

                // initialize the single instance DLL
                CWinApp* pApp; pApp = AfxGetApp();
                if (pApp != NULL && !pApp->InitInstance())
                {
                        pApp->ExitInstance();
                        AfxWinTerm();
                        goto Cleanup;       // Init Failed
                }

                pState->m_pPrevModuleState = pPrevModState;
#ifdef _AFXDLL
                // wire up this DLL into the resource chain
                VERIFY(AfxInitExtensionModule(controlDLL, hInstance));
                CDynLinkLibrary* pDLL; pDLL = new CDynLinkLibrary(controlDLL);
                ASSERT(pDLL != NULL);
#else
                AfxInitLocalData(hInstance);
#endif

                bResult = TRUE;

Cleanup:
                pState->m_pPrevModuleState = pPrevModState;
#ifdef _AFXDLL
                // restore previously-saved module state
                VERIFY(AfxSetModuleState(AfxGetThreadState()->m_pPrevModuleState) ==
                        &afxModuleState);
                DEBUG_ONLY(AfxGetThreadState()->m_pPrevModuleState = NULL);
#endif
                return bResult;
        }
        else if (dwReason == DLL_PROCESS_DETACH)
        {
#ifdef _AFXDLL
                // set module state for cleanup
                ASSERT(AfxGetThreadState()->m_pPrevModuleState == NULL);
                AfxGetThreadState()->m_pPrevModuleState =
                        AfxSetModuleState(&afxModuleState);
#endif

                CWinApp* pApp = AfxGetApp();
                if (pApp != NULL)
                        pApp->ExitInstance();

#ifdef _DEBUG
                // check for missing AfxLockTempMap calls
                if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
                {
                        TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
                                AfxGetModuleThreadState()->m_nTempMapLock);
                }
#endif
                AfxLockTempMaps();
                AfxUnlockTempMaps(-1);

                // terminate the library before destructors are called
                AfxWinTerm();

#ifdef _AFXDLL
                AfxTermExtensionModule(controlDLL, TRUE);
#else
                AfxTermLocalData(hInstance, TRUE);
#endif
        }
        else if (dwReason == DLL_THREAD_DETACH)
        {
                AFX_MANAGE_STATE(&afxModuleState);

#ifdef _DEBUG
                // check for missing AfxLockTempMap calls
                if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
                {
                        TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
                                AfxGetModuleThreadState()->m_nTempMapLock);
                }
#endif
                AfxLockTempMaps();
                AfxUnlockTempMaps(-1);

                AfxTermThread(hInstance);
        }

        return TRUE;
}

void AFXAPI AfxWinTerm(void)
{
        // unregister Window classes
        AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
        AfxLockGlobals(CRIT_REGCLASSLIST);
        LPTSTR lpsz = pModuleState->m_szUnregisterList;
        while (*lpsz != 0)
        {
                LPTSTR lpszEnd = _tcschr(lpsz, '\n');
                ASSERT(lpszEnd != NULL);
                *lpszEnd = 0;
                UnregisterClass(lpsz, AfxGetInstanceHandle());
                lpsz = lpszEnd + 1;
        }
        pModuleState->m_szUnregisterList[0] = 0;
        AfxUnlockGlobals(CRIT_REGCLASSLIST);

        // cleanup OLE if required
        CWinThread* pThread = AfxGetApp();
        if (pThread != NULL && pThread->m_lpfnOleTermOrFreeLib != NULL)
                (*pThread->m_lpfnOleTermOrFreeLib)(TRUE, FALSE);

        // cleanup thread local tooltip window
        _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
        if (pThreadState->m_pToolTip != NULL)
        {
                if (pThreadState->m_pToolTip->DestroyToolTipCtrl())
                        pThreadState->m_pToolTip = NULL;
        }

        if (!afxContextIsDLL)
        {
                // unhook windows hooks
                if (pThreadState->m_hHookOldMsgFilter != NULL)
                {
                        ::UnhookWindowsHookEx(pThreadState->m_hHookOldMsgFilter);
                        pThreadState->m_hHookOldMsgFilter = NULL;
                }
                if (pThreadState->m_hHookOldCbtFilter != NULL)
                {
                        ::UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);
                        pThreadState->m_hHookOldCbtFilter = NULL;
                }
        }
}


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (2)
雪    币: 2105
活跃值: (424)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
不错的东西~~
2010-4-1 23:32
0
雪    币: 184
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
谢谢您的赏识,呵呵!
2010-4-2 10:21
0
游客
登录 | 注册 方可回帖
返回
//