翻译:赖仪灵

出处: http://blog.csdn.net/laiyiling,  http://www.cppblog.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 控件