使用ATL開發ActiveX控件(轉載)

www.cnblogs.com/watchdatalearn2012620/archive/2012/08/07/2626720.html

本文描述了使用ATL開發一個ActiveX控件的完整過程。

一、創建項目

單擊起始頁中的“New Project…”,選擇“ATL”分類下的“ATL Project”項目,項目名稱爲“Calculator”。在隨後出現的項目嚮導中,使用默認配置即可。

image

二、添加控件

在解決方案管理器中的項目上右擊,依次選擇“Add”、“Class”,在添加類對話框中選擇ATL分類下的ATL Control類型。單擊“Add”按鈕,將會出現添加ATL Control嚮導。

image 

image

在嚮導的第二步中,將接口類型選擇爲“Dual”,爲控件支持事件做爲準備,在Support選項中,選中“Connection points”複選框。

隨後出現選擇控件要實現的接口的界面,除VS默認添加的實現外,再添加IObjectSafety接口,實現該接口可以避免控件在IE中使用時IE彈出運行的腳本不安全的提示。

image

 

 

三、爲控件添加並實現方法

在Class View窗口中右擊ICalc接口,依次選擇“Add”、“Add Method…”,此處假定我們實現一個加法運算,將方法命名爲“Add”,然後添加參數:

image

需要注意的是對返回值的處理。應將參數類型選定爲DOUBLE*,並選中“retval”複選框。

嚮導結束後,VS自動在Calc.cpp中添加了該方法的空實現,略加修改後的方法代碼爲:

STDMETHODIMP CCalc::Add(DOUBLE a, DOUBLE b, DOUBLE* result)
{
    *result = a + b;

    return S_OK;
}

測試該方法:

由於只是調用該控件進行加法運算,並不需要該控件的界面展示,因此在測試控件之前,可以將VS自動生成的OnDraw方法中的其他代碼刪除,直接返回 S_OK 即可。

對VS自動生成的用於測試的htm略做修改來測試添加的方法。修改後的完整htm代碼如下:

複製代碼
<HTML>
<HEAD>
<TITLE>ATL 8.0 test page for object Calc</TITLE>
</HEAD>
<BODY>

<OBJECT ID="Calc" CLASSID="CLSID:59443E6F-7B99-4F75-A7AF-6FEE5B8208CD"></OBJECT>

<input type="button" value="Add" οnclick="add();" />

<script type="text/javascript">
    function add() {
        var calc = document.getElementById('Calc');
        var result = calc.Add(2, 3);
        alert(result);
    }
</script>

</BODY>
</HTML>
複製代碼

點擊“Add”按鈕後的運行效果:

image

四、爲控件添加事件

假定控件進行的是一個非常複雜的運算,爲了在調用運算時不阻塞調用者線程,可以使用異步方式完成運算。控件在完成運算時需要通知調用者,這時便需要事件。

首先按照步驟三中的方法,添加一個異步調用加法運算的方法AddAsync,然後爲控件添加運算完成的事件AddCompleted。

在Class View窗口中右擊_ICalcEvents接口,依次選擇“Add”、“Add Method…”,根據添加方法嚮導添加AddCompleted方法,如下圖所示:

image

然後在Class View窗口中右擊CCalc類,依次選擇“Add”、“Add Connection Point…”,在彈出的實現連接點窗口中實現_ICalcEvents接口。

image

 

完成嚮導後,VS會自動爲我們生成基本框架,包括引發事件的方法Fire_AddCompleted。我們只需在AddAsync方法中添加運算並在運算結束時調用Fire_AddCompleted的代碼:

複製代碼
STDMETHODIMP CCalc::AddAsync(DOUBLE a, DOUBLE b)
{
    double result;
    result = a + b;
    Fire_AddCompleted(&result);

    return S_OK;
}
複製代碼

在網頁中添加異步計算的代碼進行測試(添加的javascript代碼如下),應該能夠得到我們想要的效果。

複製代碼
<script type="text/javascript">
    function addAsync() {
        var calc = document.getElementById('Calc');
        calc.attachEvent("AddCompleted", OnAddCompleted);
        calc.AddAsync(3, 4);
    }

    function OnAddCompleted(result) {
        alert(result);
    }
</script>
複製代碼

五、ActiveX控件的事件與多線程

細心的讀者一定會發現步驟四中所謂的“異步”是假的:雖然在運算結束後使用事件對調用者進行通知,但由於運算是在主線程上進行的,所以調用過程仍是同步的。步驟四其實只是展示了一下事件的簡單用法,如果真正實現異步,則需要在控件中使用多線程。

在ActiveX控件中使用多線程時需要注意的是:引發事件(即調用Fire_XXXX)必須在窗口線程中進行,否則會導致事件不能被ActiveX控件的容器處理。如果事件發生時執行代碼的線程不是窗口線程。那麼應該使用PostMessage或SendMessage來通知窗口線程,並在消息處理函數中執行Fire_XXXX。爲了使用控件的消息機制,還應該在CCalc的構造函數中將m_bWindowOnly字段設置爲TRUE。

以下是改爲多線程後的部分示例代碼:

至此,一個簡單的ActiveX控件就開發完成了。關於ActiveX控件的打包部署等問題,可以參考以下內容:

1、Web發佈cab文件打包的ActiveX控件總結

2、ActiveX控件打包成Cab置於網頁中自動下載安裝

3、VS2005下MFC開發的ActiveX控件的部分總結

本文示例所使用的開發環境爲Visual Studio 2010。

 

附:使用VC6開發時的注意事項

在今天看來,VC6顯得有些古老,但由於目前能見到的大多數版本的Windows操作系統已經內置了運行VC6開發的應用程序所需要的庫,因此從方便發佈的角度看,使用VC6來開發ActiveX控件不失爲一個好的選擇。

使用VC6開發ActiveX控件與上文所述步驟大同小異,但是需要注意微軟給開發者留下的兩道試題--使用VC6嚮導生成的代碼中包含兩處錯誤:

第一處錯誤位於連接點映射,DIID__IXXXXEvents中的第一個字符‘D’需手動添加。

示例代碼:

BEGIN_CONNECTION_POINT_MAP(CCalc)
	CONNECTION_POINT_ENTRY(DIID__ICalcEvents)	// 修改 IID_XXXX 爲 DIID_XXXX
END_CONNECTION_POINT_MAP()

 

 

該錯誤會造成生成失敗,比較容易發現。

第二處錯誤位於Fire_XXXX方法內,不會造成生成失敗但會造成運行結果莫名其妙,因此該錯誤更隱蔽一些。

示例代碼:

if (pConnection)
{
  CComVariant avarParams[1];
  avarParams[0].byref = result;    //此處自動生成的代碼有錯誤,應去掉原代碼中的取址運算
  avarParams[0].vt = VT_R8|VT_BYREF;
  CComVariant varResult;

  //...
}
發佈了16 篇原創文章 · 獲贊 5 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章