略談如何從工作線程中彈出對話框
工作線程,在一些技術文章被稱為輔助線程,是相對于主線程而言的。在工作線程中使用界面需要一些技巧。我就曾在工程線程中彈出對話框中遇到過莫名奇妙的錯誤。下面就我的經驗談談如何從工作線程中彈出對話框(暫時只講方法,原理還沒徹底弄清楚)。
實際上在工作線程中直接彈出模式對話框中在debug模式下有時出錯(這里的有時的意思是必然會出錯,但是不是每次都出錯),彈出模式對話框的代碼如下:
- DWORD WINAPI RecvThread(LPVOID lpParam) // 工作線程函數
- {
- CAIDlgProductName dlg;
- if(dlg.DoModal() == IDOK)
- {
- ……
- }
- }
錯誤截圖:
如果跟蹤DoModal函數,我們進入MFC源碼找到出錯的地方:
- #ifdef _DEBUG
- void CWnd::AssertValid() const
- {
- if (m_hWnd == NULL)
- return; // null (unattached) windows are valid
- // check for special wnd??? values
- ASSERT(HWND_TOP == NULL); // same as desktop
- 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
- {
- // should be a normal window
- ASSERT(::IsWindow(m_hWnd));
- // should also be in the permanent or temporary handle map
- 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.
- }
- }
實際上當時給我啟發的是上面那段Note。我用我淺薄的英文功底翻譯一下大意就是:就是上面的asserts發生了同時你正在寫的是一個多線程程序,那么asserts發生的原因很可能是你將一個C++對象從一個線程傳遞給另一個線程同時你無意中使用了那個C++對象(only simple inline wrapper functions should be used(抱歉,這一句不會翻譯)),實際上線程之間傳遞CWnd對象應該傳遞句柄(HWND)。接收線程應該通過CWnd::FromHandle函數通過傳遞過來的句柄獲取CWnd對象(這里準確的來說應該是CWnd對象的指針)。
線程之間傳遞C++對象是危險的,除非那個對象被設計為以那種方式使用。
由上面我想到一種在工作線程中彈出的對話框的辦法:
1. 轉遞視圖類句柄給線程函數:
- HWND HView;
-
…… // 獲取視圖類句柄
- CreateThread(NULL,0,RecvThread, HView
- ,0,&dwThreadId);
2. 在線程函數中通過句柄獲取視圖類指針,獲取數據給視圖類發送自定義消息:
- DWORD WINAPI RecvThread(LPVOID lpParam)
- {
- HWND HView = (HWND)lpParam;
- CWnd* pMyView = CWnd::FromHandle(HView);
- ……
- pMyView ->SendMessage(WM_TASKDLG_MESSAGE,(WPARAM)(&str));
- …….
- }
3. 在視圖類自定義一個消息函數OnTaskDlgMessage專門處理WM_TASKDLG_MESSAGE消息用于創建對話框:
- LRESULT CInteAView::OnTaskDlgMessage(WPARAM wParam, LPARAM lParam)
- {
- CAIDlgProductName dlg;
- if(dlg.DoModal() == IDOK)
- {
- ……
- }
- return 0;
- }
當然上面將視圖類換為框架類也是可以的。上面就我的經驗談了一種從工作線程中彈出對話框的辦法,不當之處還請大家指點。
RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成