• 略谈如何从工作线程中弹出对话框


      朱金灿

         

    工作线程,在一些技术文章被称为辅助线程,是相对于主线程而言的。在工作线程中使用界面需要一些技巧。我就曾在工程线程中弹出对话框中遇到过莫名奇妙的错误。下面就我的经验谈谈如何从工作线程中弹出对话框(暂时只讲方法,原理还没彻底弄清楚)。

     

    实际上在工作线程中直接弹出模式对话框中在debug模式下有时出错(这里的有时的意思是必然会出错,但是不是每次都出错),弹出模式对话框的代码如下:

     

    1. DWORD WINAPI RecvThread(LPVOID lpParam)    // 工作线程函数
    2. {
    3.      CAIDlgProductName dlg;
    4.      if(dlg.DoModal() == IDOK)
    5. {
    6.      ……
    7. }

    8. }

          

    错误截图:

     

    如果跟踪DoModal函数,我们进入MFC源码找到出错的地方:

     

     

    1. #ifdef _DEBUG
    2. void CWnd::AssertValid() const
    3. {
    4.     if (m_hWnd == NULL)
    5.         return;     // null (unattached) windows are valid

    6.     // check for special wnd??? values
    7.     ASSERT(HWND_TOP == NULL);       // same as desktop
    8.     if (m_hWnd == HWND_BOTTOM)
    9.         ASSERT(this == &CWnd::wndBottom);
    10.     else if (m_hWnd == HWND_TOPMOST)
    11.         ASSERT(this == &CWnd::wndTopMost);
    12.     else if (m_hWnd == HWND_NOTOPMOST)
    13.         ASSERT(this == &CWnd::wndNoTopMost);
    14.     else
    15.     {
    16.         // should be a normal window
    17.         ASSERT(::IsWindow(m_hWnd));

    18.         // should also be in the permanent or temporary handle map
    19.         CHandleMap* pMap = afxMapHWND();
    20.         ASSERT(pMap != NULL);

    21.         CObject* p;

    22.         // 在下面一句出错
    23.         ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||
    24.             (p = pMap->LookupTemporary(m_hWnd)) != NULL);
    25.         ASSERT((CWnd*)p == this);   // must be us

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

         

    实际上当时给我启发的是上面那段Note。我用我浅薄的英文功底翻译一下大意就是:就是上面的asserts发生了同时你正在写的是一个多线程程序,那么asserts发生的原因很可能是你将一个C++对象从一个线程传递给另一个线程同时你无意中使用了那个C++对象(only simple inline wrapper functions should be used(抱歉,这一句不会翻译)),实际上线程之间传递CWnd对象应该传递句柄(HWND)。接收线程应该通过CWnd::FromHandle函数通过传递过来的句柄获取CWnd对象(这里准确的来说应该是CWnd对象的指针)。

     

    线程之间传递C++对象是危险的,除非那个对象被设计为以那种方式使用。

     

    由上面我想到一种在工作线程中弹出的对话框的办法:

    1.       转递视图类句柄给线程函数:

    1. HWND HView;
    2. …… // 获取视图类句柄

    3. CreateThread(NULL,0,RecvThread, HView
    4.                 ,0,&dwThreadId);

     

     

       

    2.       在线程函数中通过句柄获取视图类指针,获取数据给视图类发送自定义消息:

     

        

    1. DWORD WINAPI RecvThread(LPVOID lpParam)
    2. {
    3.    HWND HView = (HWND)lpParam;
    4.   CWnd* pMyView = CWnd::FromHandle(HView);
    5.   ……
    6.   pMyView ->SendMessage(WM_TASKDLG_MESSAGE,(WPARAM)(&str));

    7.    …….
    8. }

        

    3.   在视图类自定义一个消息函数OnTaskDlgMessage专门处理WM_TASKDLG_MESSAGE消息用于创建对话框:

     

    1. LRESULT CInteAView::OnTaskDlgMessage(WPARAM wParam, LPARAM lParam)
    2. {
    3.      CAIDlgProductName dlg;
    4.      if(dlg.DoModal() == IDOK)
    5.      {
    6.             ……
    7.      }
    8.      return 0;
    9. }


         

    当然上面将视图类换为框架类也是可以的。上面就我的经验谈了一种从工作线程中弹出对话框的办法,不当之处还请大家指点。


    参考文献:


    1.关于多线程中传递MFC窗口类指针时ASSERT_VALID出错的另类解决


    2.MFC中创建多线程 MFC对象指针不能在线程间传输


  • 相关阅读:
    nginx 详解
    阿里云 消息队列mq
    手机浏览器Yandex安装插件说明
    windows下JAVA环境变量配置
    共享文件夹免密登入
    自动添加静态路由
    加入WSUS补丁服务器并下载补丁
    加入时间同步服务器(NTP)
    更改rdp端口
    关闭及开启445等危险端口
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6471223.html
Copyright © 2020-2023  润新知