经常遇到的一个问题是 MFC中开启多线程后 在非主线程中使用updata函数出现崩溃的情况。

来源:互联网 发布:恒宝充电器淘宝店 编辑:程序博客网 时间:2024/05/17 03:32
经常遇到的一个问题是 MFC中开启多线程后 在非主线程中使用updata函数出现崩溃的情况。
UINT MyThread(LPVOID p)
{
    CAbDlg* lpThis = (CAbDlg*)p;
    lpThis->m_str  = lpdata;
    lpThis->UpdateData(FALSE);
    return 0;
};
void CAbDlg::OnButton1() 
{
    pMap = AfxGetModuleThreadState()->m_pmapHWND;
    AfxBeginThread(MyThread, this);
}
点击按钮后出现如下问题

(一般方法不讲:就是传入this指针setwindowtext等)
下面我们通过具体调试来解决这个问题

F5启动程序点击按钮出现错误提示后点击重试

查看堆栈黄箭头是报错的地方,绿箭头是我们的代码位置
看看出错的位置代码情况
#ifdef _DEBUG
void CWnd::AssertValid() const
{
        if (m_hWnd == NULL)
                return;   
        ASSERT(HWND_TOP == NULL);     
        if (m_hWnd == HWND_BOTTOM)
                ASSERT(this == &CWnd::wndBottom);
        else if (m_hWnd == HWND_TOPMOST)
                ASSERT(this == &CWnd::wndTopMost);
        else if (m_hWnd == HWND_NOTOPMOST)
                ASSERT(this == &CWnd::wndNoTopMost);
        else
        {
                ASSERT(::IsWindow(m_hWnd));
                CHandleMap* pMap = afxMapHWND();
                ASSERT(pMap != NULL);
                CObject* p;
                ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||
                        (p = pMap->LookupTemporary(m_hWnd)) != NULL);
                ASSERT((CWnd*)p == this);   // must be us//这一句关键

                // Note: if either of the above asserts fire and you are
                // writing a multithreaded application, it is likely that
                // you have passed a C++ object from one thread to another
                // and have used that object in a way that was not intended.
                // (only simple inline wrapper functions should be used)
                //
                // In general, CWnd objects should be passed by HWND from
                // one thread to another.  The receiving thread can wrap
                // the HWND with a CWnd object by using CWnd::FromHandle.
                //
                // It is dangerous to pass C++ objects from one thread to
                // another, unless the objects are designed to be used in
                // such a manner.

        }
}
ASSERT((CWnd*)p == this);  这一句会检查CWnd对象是否是自己 如果不是则崩溃,
再看上面的代码 p如何拿到的我们也学会了,知道错误的原因,我们就可以解决了

也就是把这个p对象换成自己的
根本的是吧CHandleMap* pMap = afxMapHWND(); 或称自己的

这个数据怎么来呢,MFC是有自己的线程模块状态 现获取这个状态Afx
AfxGetModuleThreadState();
AFX_MODULE_THREAD_STATE
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
};

CHandleMap* pMap = NULL;
CHandleMap* pMap2 = NULL;
char* lpdata = "Hello";
UINT MyThread(LPVOID p)
{
    CAbDlg* lpThis = (CAbDlg*)p;
    lpThis->m_str  = lpdata;
    pMap2 = AfxGetModuleThreadState()->m_pmapHWND;
    AfxGetModuleThreadState()->m_pmapHWND = pMap;替换成主模块的
    lpThis->UpdateData(FALSE);
    
    return 0;
};
void CAbDlg::OnButton1() 
{
    pMap = AfxGetModuleThreadState()->m_pmapHWND;
    AfxBeginThread(MyThread, this);
}
运行起来看看是否实现了

结果还是错误的
挂在这里 访问失败
看堆栈往上找
我们updata之后应该吧线程模块状态再次恢复 
所以代码应该改成这样

CHandleMap* pMap = NULL;
CHandleMap* pMap2 = NULL;
char* lpdata = "Hello";
UINT MyThread(LPVOID p)
{
    CAbDlg* lpThis = (CAbDlg*)p;
    lpThis->m_str  = lpdata;
    pMap2 = AfxGetModuleThreadState()->m_pmapHWND;
    AfxGetModuleThreadState()->m_pmapHWND = pMap;
    lpThis->UpdateData(FALSE);
    AfxGetModuleThreadState()->m_pmapHWND = pMap2;
    return 0;
};
void CAbDlg::OnButton1() 
{
    pMap = AfxGetModuleThreadState()->m_pmapHWND;
    AfxBeginThread(MyThread, this);
}

在运行就不崩溃了
0 0
原创粉丝点击