源代碼下載下傳:MyActiveX20081229.rar
聲明:本文代碼基于CodeProject的文章《A Complete ActiveX Web Control Tutorial》修改而來,是以同樣遵循Code Project Open License (CPOL)。
在上一篇文章《COM元件開發實踐(七)---多線程ActiveX控件和自動調整ActiveX控件大小(上)》中介紹了ActiveX控件中使用多線程的基本需求,并提出了一個簡單的線程模型,但卻出現了意想不到的問題,本文将嘗試給出問題的一個可行的解法,并同時解決上文中提出的第二個問題。
其實解決的思路也很簡單,一開始我也早就想到了的,就是使用讓子線程PostMessage來發出自定義的消息來通知主線程,特定的事件已經發生了,需求主線程去響應。這不是什麼了不起的想法,但我對子線程PostMessage非常恐懼,因為以前的一個項目中就是這個問題導緻了記憶體洩露,是以這個方案一開始就被我否定了。
遍尋解決之道不可得時,隻得在csdn的論壇上發貼求教高手了,具體的讨論請參考這個文章:
http://topic.csdn.net/u/20081226/17/9bf0ae08-c54d-4934-b1b2-91baa27ff76e.html
看到jameshooo(胡柏華)的回帖後,還是決定回到起點,嘗試用PostMessage這個方案。
首先自定義兩個事件,分别表示操作成功和操作失敗
#define WM_OPTSUCCESS WM_APP+101 //操作成功
#define WM_OPTFAILED WM_APP+102 //操作失敗
然後回調函數中就變得非常簡單,隻需要post對應的事件即可。
複制代碼
/////////////////////
//回調函數
/////////////////////////
void CMyActiveXCtrl::OnSuccesful()
{//操作成功
this->PostMessage(WM_OPTSUCCESS,(WPARAM)NULL,(LPARAM)NULL);
}
void CMyActiveXCtrl::OnFailed()
{//操作失敗
this->PostMessage(WM_OPTFAILED,(WPARAM)NULL,(LPARAM)NULL);
再重載消息處理函數WindowProc,在其中調用外部的JavaScript函數或者Fire出外部頁面可以響應的事件。
複制代碼
LRESULT CMyActiveXCtrl::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_OPTSUCCESS:
{//操作成功,通知外部頁面
CString strOnLoaded("OnLoaded");
this->CallJScript(strOnLoaded);
return 0;
}
case WM_OPTFAILED:
{//操作失敗,通知外部頁面
//這裡不寫了,同上面
}
return COleControl::WindowProc(msg,wParam,lParam);
在OnCreate函數中加入啟動工作線程代碼:
int CMyActiveXCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;
m_MainDialog.Create(IDD_MAINDIALOG, this);
pThread.SetICallBack(this);//設定主線程回調函數
pThread.Start();//啟動工作線程
return 0;
重載掉OnClose函數,在其中加入關閉工作線程的代碼:
void CMyActiveXCtrl::OnClose(DWORD dwSaveOption)
pThread.Stop(true);//強行關閉工作線程
COMRELEASE(pWebBrowser);
COMRELEASE(pHTMLDocument);
COleControl::OnClose(dwSaveOption);
到此為止,一個多線程的ActiveX控件就誕生了。這裡是不會發生以前我遇到的記憶體洩露的,因為情況不同了,隻是在回調函數中簡單的post一個message,并沒有new一個記憶體區域并将這塊記憶體作為參數post給主線程,後面這種情況是可能會記憶體洩露的。
Ok,下面來考慮第二個問題,先簡單介紹下具體需求:就是一個AcitveX控件會用到不同的頁面中,每個頁面對這個控件的需求也不同,也就要求在兩個不同的頁面中,控件顯示的大小也不同。
jameshooo(胡柏華) 回帖說:“改變控件大小要通知容器,由容器再反過來通知控件改變大小,不然沒有任何效果。調用IOleInPlaceSite::OnPosRectChange即可”。是以就根據這個來嘗試提出一個解決方案來。
假設有兩種模式的控件,一種是“普通”模式, 如下圖所示:
一種是“特殊”模式,
為了差別開兩者,就考慮在web頁面中通過設定參數的方式來通知ActiveX控件,對于不同的模式填充不同的對話框就可以了。我們在web頁面中控件部分加入如下參數:
<PARAM NAME="IsSpecial" VALUE="TRUE">
相應的在CMyActiveXCtrl類中加入一個變量,這裡為簡單起見,選擇了類型為CString型,主要是為了傳參數友善。
CString m_bIsSpecial;//是否是"特殊"頁面
Web頁面傳入的參數值在下面這個函數中讀取:
void CMyActiveXCtrl::DoPropExchange(CPropExchange* pPX)
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
PX_String(pPX, _T("IsSpecial"), m_bIsSpecial); //讀取外部設定的參數
為了供控件選擇,這裡提供了兩種模式的對話框:
public:
CMyDlgTwo m_dlgSpecial;//特殊模式
CMyDlgThree m_dlgCommon;//普通模式
然後在建立和繪制對話框時,通過檢測參數是否為空就知道待建立的對話框類型到底是“普通“還是”特殊“了。
CRect newRc;
if(m_bIsSpecial.Compare(_T(""))==0)
{//沒設定參數,"普通“模式
this->m_dlgCommon.Create(IDD_DIALOG3,this);
//設定控件的大小
newRc.left = 0;
newRc.top = 0;
newRc.right = 200;
newRc.bottom = 200;
else
{//設定了參數,”特殊“模式
this->m_dlgSpecial.Create(IDD_DIALOG2,this);
this->m_pInPlaceSite->OnPosRectChange(&newRc);
void CMyActiveXCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
if (!pdc)
return;
this->m_dlgCommon.MoveWindow(rcBounds,TRUE);
this->m_dlgSpecial.MoveWindow(rcBounds,TRUE);
本文轉自Phinecos(洞庭散人)部落格園部落格,原文連結:http://www.cnblogs.com/phinecos/archive/2008/12/29/1364791.html,如需轉載請自行聯系原作者