MFC提供了許多十分有用的類和對象,在很多時候在Office外掛程式、BHO、常規DLL這樣的工程中加入MFC支援是一個不錯的選擇。但是,MFC中的很多功能,例如資源尋找,訊息預先處理等等都依賴於在進程或者線程建立時被初始化的MFC內部資料;而對於需要添加MFC支援的工程,這些資料並不會被自動地初始化。這時候使用一些MFC的功能,例如使用CString從字串表載入一個字串,或者使用CDialog::DoModal()建立一個模態對話方塊,都會有斷言錯誤,用ATL嚮導建立的支援MFC的程式也沒有多少改善,在CWinApp的DLL版本中沒有初始化線程資料,所以調用AfxGetThread會返回null 指標。解決這個問題的一個辦法是使用AfxBeginThread來啟動一個MFC線程,這樣MFC會初始化線程相關的資料。在下面的樣本中,我線上程初始化時建立了一個模態對話方塊,以避免直接建立模態對話方塊會觸發的宣告失敗資訊。為了類比模態對話方塊的效果,在CDialogThread::WaitForDoModal()這個函數中建立了一個訊息迴圈來等待線程結束,同時用MsgWaitForMultipleObjects來避免死結。因為MFC中和進程相關的資料並不總是被正確初始化,在調用模態對話方塊之前也需要手動設定一下。 //如果這段代碼可以工作,那麼它的作者是Jiangsheng
//否則我不知道它的作者
void __stdcall CFrontPageAddin::OnClickButtonExportCHM(IDispatch* /*Office::_CommandBarButton*
*/ Ctrl,VARIANT_BOOL * CancelDefault)
{
AtlTrace(_T(" CFPAnt::OnClickButtonExportCHM\n"));
//create a thread to avoid assert failure
CDialogThread* pDialogThread=
(CDialogThread*)AfxBeginThread(RUNTIME_CLASS(CDialogThread),
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED,NULL);
if(pDialogThread)
{
pDialogThread->m_prc=RUNTIME_CLASS(CExportCHMSheet);
pDialogThread->ResumeThread();
pDialogThread->WaitForDoModal();
delete pDialogThread;
}
}
class CDialogThread : public CWinThread
{
int m_nModalResult;
CRuntimeClass* m_prc;
void WaitForDoModal();
};
CDialogThread::CDialogThread()
{
m_bAutoDelete=FALSE;
m_prc=NULL;
m_nModalResult=0;
}
BOOL CDialogThread::InitInstance()
{
// TODO: perform and per-thread initialization here
AFX_MANAGE_STATE(AfxGetAppModuleState());
AFX_MODULE_STATE* pModuleState=AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle=_Module.GetModuleInstance();
AfxSetResourceHandle(_Module.GetModuleInstance());
if(m_prc)
{
if(m_prc->IsDerivedFrom(RUNTIME_CLASS(CDialog)))
{
CDialog* pDialog=(CDialog*)m_prc->CreateObject();
if(pDialog)
{
m_pMainWnd=pDialog;
m_nModalResult=pDialog->DoModal();
}
}
else if(m_prc->IsDerivedFrom(RUNTIME_CLASS(CPropertySheet)))
{
CPropertySheet* pDialog=(CPropertySheet*)m_prc->CreateObject();
if(pDialog)
{
m_pMainWnd=pDialog;
m_nModalResult=pDialog->DoModal();
}
}
}
return FALSE;
}
void CDialogThread::WaitForDoModal()
{
//from http://blogs.msdn.com/oldnewthing/archive/2005/02/17/375307.aspx
MSG msg;
UINT cRecords = 0;
while (true) {
switch (MsgWaitForMultipleObjects(1, &m_hThread,
FALSE, INFINITE, QS_ALLINPUT)) {
case WAIT_OBJECT_0:
return ; // event has been signalled
break;
case WAIT_OBJECT_0+1:
// we have a message - peek and dispatch it
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
break;
default:
return ; // unexpected failure
}
}
}
使用VC6.0編譯通過。
上面的方法只對於DLL之類的組件比較有用。對於使用MFC的應用程式來說,上面的方法比較繁瑣。比較簡單的方法還是使用MFC嚮導來建立應用程式,然後再添加ATL或者.Net之類的額外支援。參考微軟知識庫文章Q181505和Q824480。
參考
- BUG: You receive an "ASSERT in wincore.cpp" assert when an MFC application calls a function in MFC regular DLL in Visual C++(http://support.microsoft.com/kb/194300)
- PRB: ATL COM AppWizard Doesn't Offer MFC Support for .EXE(http://support.microsoft.com/kb/181505)
- PRB:為 C++ DLL 項目建立託管擴充時出現連結器警告(http://support.microsoft.com/kb/814472)
- BUG: "HRESULT - 0x80010106" Error When You Run a Managed C++ Application(http://support.microsoft.com/kb/824480)
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=459594