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

原创 2017年01月11日 14:54:26
经常遇到的一个问题是 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);
}

在运行就不崩溃了
版权声明:本文为博主原创文章,未经博主允许不得转载。

MFC中如何创建一个线程

MFC中如何创建一个线程.hCWinThread* FHz_Thread;//声明线程 static UINT StartTestGKThread(void *param);//声明线程函数.c...
  • liudonghong128
  • liudonghong128
  • 2016年12月29日 14:01
  • 989

MFC不能多线程操作控件的原因

表现错误示例 网友hewwatt大致原因解释如下 原因分析 窗口类 MFC状态 模块本地数据 进程本地数据 线程本地数据 模块线程状态 包装类对象和句柄映射 解决办法 注意事项  对于大多数mfc对象...
  • XscKernel
  • XscKernel
  • 2016年05月17日 15:55
  • 2602

boost::thread ASSERT(AfxGetModuleState() != AfxGetAppModuleState()); // 在这一句发生断言错误

我也碰到过与楼主一样的问题.  boost::thread + MFC DLL 問題 基本上這個問題在boost 1.38就有了,只是沒想到更新成boost 1.45後問題還是沒有解決.....
  • xiao3131
  • xiao3131
  • 2014年09月11日 16:02
  • 1007

AfxGetModuleState() 与 AfxGetModuleThreadState()

http://blog.pfan.cn/sword2008/29453.htmlhttp://blog.pfan.cn/sword2008/29453.html AfxGetModuleState()...
  • csw_100
  • csw_100
  • 2010年01月27日 11:24
  • 5224

VC启动一个新线程的三种方法

主要用AfxBeginThread()函数来 UINT  myproc(LPVOID  lParam) { CITTDlg *pWnd = (CITTDlg *)lParam; pWnd->KMe...
  • u014568921
  • u014568921
  • 2015年03月14日 19:53
  • 8626

如何开新线程

1.声明线程内将运行的程序: static UINT jisuan(void *param); 注意:1.必须为静态函数。             2.必须是UINT型。             3...
  • wf6892
  • wf6892
  • 2016年10月27日 13:05
  • 257

乱码总结

乱码很麻烦,网上用utf-8的居多,但是我没尝试出来,个人感觉gbk更舒服,  utf8的通用性比gbk好,所以先用着这个,以后再改,必要的话 乱码解决如下途径:  以gbk为例   ...
  • xiaoliu123586
  • xiaoliu123586
  • 2012年04月07日 20:55
  • 746

在mfc的子线程中创建非模态的对话框出现的问题

前几天在编写一个程序的时候,需要在一个
  • bobopeng
  • bobopeng
  • 2014年08月05日 22:59
  • 1768

android 快速创建一个新的线程

http://blog.sina.com.cn/s/blog_71dbc27f01017mnj.html 第一种:直接创建子线程并启动       new Thread() { @Ove...
  • gaoguoxin2
  • gaoguoxin2
  • 2016年02月17日 18:09
  • 2171

AfxBeginThread开启一个线程,怎样去关闭这个线程?

一般不要主动去关闭,让线程自己返回即可!但是你一定要关闭,有几种方法: (1)在线程函数内部调用AfxEndThread关闭;  (2)或定义一个全局bool变量bIsRunning设为tru...
  • u010246665
  • u010246665
  • 2016年04月10日 21:39
  • 1497
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:经常遇到的一个问题是 MFC中开启多线程后 在非主线程中使用updata函数出现崩溃的情况。
举报原因:
原因补充:

(最多只允许输入30个字)