翻譯:賴儀靈

出處: http://blog.csdn.net/laiyiling,? http://www.shnenglu.com/techlab

聲明:版權歸原作者擁有,請勿隨意轉載此翻譯文檔,保留一切權利。

?

1.8??? 添加和激發事件

?

當某些客戶端對 COM 對象內部所發生的事情感興趣時,我們希望,也能夠很自然的通知客戶端,而不需要客戶端檢測對象。 COM 提供了一種標準機制——連接點結構,來發送這些通知到客戶端(通常稱為“激發事件”)

?

連接點的事件通常是某個接口的方法。為了支持大量不同的客戶端,事件接口通常定義為 dispinterface 。在 ATL 的簡單對象向導中,如果選擇支持連接點,在工程的 IDL 文件中救會生成一個事件。下面的代碼演示了向導生成的一個后增事件方法(粗體顯示):

[
?????????uuid(B830F523-D87B-434F-933A-623CEF6FC4AA),
?????????helpstring("_ICalcPiEvents Interface")
]
dispinterface _ICalcPiEvents {
?????????properties:
???????? methods:
????????[id(1)] void OnDigit([in] short nIndex,
?????????????? [in] short nDigit);
};

支持連接點選項除了修改 IDL 文件外,在類得定義中也會又幾處變化。基類中會添加接口 IConnectionPointContainer 的實現 IConnectionPointContainerImpl ,它提供函數管理這個類上的多個事件接口。在此例子中,基類 IConnectionPointImpl 為指定事件接口 _ICalcPiEvent 實現了一個連接點。 COM_MAP 也被修改添加了一個 IConnectionPointContainer 的入口,一個新的 CONNECTION_MAP 也被添加到類中。

?

向導也同時給連接點生成一個代理類。代理類也被添加到基類列表中,它提供了一種便捷的方法進行真正的事件激發(也就是調用連接點上的方法)。此功能非常有用,因為連接點通常是基于 dispinterface

比如:

						
STDMETHODIMP CCalcPi::CalcPi(BSTR *pbstrPi) {
  // (code to calculate pi removed for clarity)
  ...
  // Fire each digit
  for( short j = 0; j != m_nDigits; ++j ) {
    Fire_OnDigit(j, (*pbstrPi)[j+2] - L'0');
  }

  ...
}

CCalcPi 類對象現在可以發送 HTML 頁面能夠處理的事件:
<object classid="clsid:E5F91723-E7AD-4596-AC90-17586D400BF7"
??????? id=objPiCalculator>
??????? <param name=digits value=50>
</object>

<input type=button name=cmdCalcPi value="Pi to 50 Digits:">
<span id=spanPi>unknown</span>

<p>Distribution of first 50 digits in pi:
<table border cellpadding=4>
... <!- table code removed for clarity >
</table>

<script language=vbscript>
? ' Handle button click event
? sub cmdCalcPi_onClick
??? spanPi.innerText = objPiCalculator.CalcPi
? end sub

? ' Handle calculator digit event
? sub objPiCalculator_onDigit(index, digit)

??? select case digit
??? case 0: span0.innerText = span0.innerText + 1
??? case 1: span1.innerText = span1.innerText + 1
??? ... <! etc >
??? end select
??? spanTotal.innerText = spanTotal.innerText + 1
? end sub
</script>
???

此示例 HTML 頁面處理這些事件,返回了 PI 的前 50 位數字和數字分類。如圖 1-11 所示。

?

r_1-11.JPG
1-11? PI 的前 50 位小數

?

關于 ATL 支持連接點的更多信息,參考第九章“連接點”。

?

1.9??? 使用窗口

?

正因為我們開發的是微軟窗口程序,因此很多時候可以很方便的彈出窗口或者對話框。比如,我們前面調用的 MessageBox 函數就會產生一個有點討厭的廣告通知。如圖 1-12 所示。

?

r_1-12.JPG
1-12? 討厭的消息框

?

通常,建立一個自定義的對話框也是有幾分痛苦的。對大多數的 Win32 程序員來說,有時不得不陷于一大堆不喜歡的程序代碼中,有時陷于建立大量的消息傳遞代碼,把窗口消息傳遞到對話框的成員函數(畢竟對話框是一個對象)。和 MFC 一樣, ATL 也有大量的代碼來建立窗口和對話框。添加一個新的對話框,選擇菜單 Project=>Add Class ,然后從彈出的對話框模板列表中選擇 ATL 對話框,如圖 1-13 所示。

?

r_1-13.JPG
1-13? 插入一個對話框類

?

ATL 對話框向導(如圖 1-14 )比其他的 ATL 類向導簡單。你僅僅需要輸入 C++ 類名稱,因為對話框是一個 Win32 對象,而不是 COM 對象。

?

r_1-14.JPG
1-14? ATL 對話框向導

?

向導會創建新的對話框資源,并用它創建一個從 CAxDialogImlo 繼承的類。派生類利用宏 MSG_MAP 來發送消息給處理函數。如下所示:

?

class CAdvert : public CAxDialogImpl<CAdvert> {

public:

? CAdvert() {}

? ~CAdvert() {}

? enum { IDD = IDD_ADVERT };

?

BEGIN_MSG_MAP(CAdvert)

??? MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)

??? COMMAND_HANDLER(IDOK, BN_CLICKED, OnClickedOK)

??? COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnClickedCancel)

??? CHAIN_MSG_MAP(CAxDialogImpl<CAdvert>)

END_MSG_MAP()

?

LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam,

????????????????????? BOOL& bHandled) {

??? if( m_bstrClient.Length() ) {

????? CComBSTR bstrCaption = OLESTR("CalcPi sponsored by ");

????? bstrCaption += m_bstrClient;

?

????? USES_CONVERSION;

????? SetWindowText(OLE2CT(bstrCaption));

??? }

??? return 1; // 允許系統設置焦點

? }

?

? LRESULT OnClickedOK(WORD wNotifyCode, WORD wID, HWND hWndCtl,

???? BOOL& bHandled) {

??? EndDialog(wID);

??? return 0;

}

?

? LRESULT OnClickedCancel(WORD wNotifyCode, WORD wID,

??? HWND hWndCtl, BOOL& bHandled) {

??? EndDialog(wID);

??? return 0;

? }

?

? ?CComBSTR m_bstrClient;

};

?

如果你還想處理其他的消息,只需要手動在消息映射宏里添加適當的消息入口和處理函數即可。如果你愿意,你還可以通過 Class 視圖,在 CAxDialogImli 基類上點擊右鍵添加消息處理函數:選擇屬性,點擊消息工具欄。圖 1-15 顯示了窗口結果。

?

r_1-15.JPG
1-15 添加窗口消息處理函數

?

?

有關 ATL 對窗口支持的更多信息(包括建立獨立的窗口應用程序),請參考第十章“窗口”。

?

1.10 ?COM 控件

?

COM 控件是一種自己能提供用戶界面( UI )的對象,而這些 UI 與控件容器緊密集成。 ATL 通過基類 CComControl 、以及其他的一些 IXxxImlp 接口實現,對 COM 控件提供了廣泛的支持。這些基類能處理創建簡單控件大部分細節(盡管在高級特征里還包括很多內容,這些會在第十一章的“ ActiveX 控件”里介紹)。在生成 CalcPi 類時的 Add Class 對話框里,如果你選擇了 ATL Conotrol ,只需要實現 OnDraw 函數可以實現 UI 了:?

				
HRESULT CCalcPi::OnDraw(ATL_DRAWINFO& di) {
  CComBSTR bstrPi;
  if( SUCCEEDED(this->CalcPi(&bstrPi)) ) {
    DrawText(di.hdcDraw, COLE2CT(bstrPi), -1,
      (RECT*)di.prcBounds,
      DT_SINGLELINE | DT_CENTER | DT_VCENTER);
  }

  return S_OK;
}

向導同樣會生成一個示例 HTML 頁面,我們把示例頁面修改一下,使它占據整個瀏覽器,并把初始的小數位設置為 50

?

<HTML>

<HEAD>

<TITLE>ATL 8.0 test page for object CalcPiControl</TITLE>

</HEAD>

<BODY>

?

<OBJECT ID="CalcPi"

??? CLASSID="CLSID:9E7ABA7A-C106-4813-A50C-B15C967264B6"

??? height="100%" width="100%">

??? <param name="Digits" value="50">

</OBJECT>

?

</BODY>

</HTML>

?

IE 中顯示這個 HTML 頁面,它將產生控件的一個視圖(圖 1-16 )。關于用 ATL 建立控件的更多信息,請參考第十一章“ ActiveX 控件”。

?

r_1-16.JPG
1-16 ?Internet Explorer 中的 CalcPi 控件