四、客户端实现步骤


  大家下载示例程序后,去浏览客户端的实现程序吧。这里我只说明一下关于接收器是如何构造的:



图八、从 ICallBack 派生接收器类 CSink



  从 ICallBack 派生一个类 CSink。确认后 IDE 会有一个警告,说它找不到 ICallBack 的头文件,不用理它,因为只有当编译的时候,#import 才会为我们生成 xxxx.tlh、xxxx.tli 文件,这些文件就有 ICallBack 的声明啦。


  这里 ICallBack 是 COM 接口,因此 CSink 是不能事例化的,如果你去编译,会得到一坨一坨(注3)的错误,报告说你没有实现 virtual 函数。然后,我们可以按照错误报告,去实现所有的虚函数:

// STDMETHODIMP 是宏,等价于 long __stdcallSTDMETHODIMP CSink::QueryInterface(const struct _GUID &iid,void ** ppv)
{
	*ppv=this;	// 不管想得到什么接口,其实都是对象本身
	return S_OK;
}

ULONG __stdcall CSink::AddRef(void)
{	return 1;	}// 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

ULONG __stdcall CSink::Release(void)
{	return 0;	}// 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

STDMETHODIMP CSink::raw_Fire_Result(long nResult)
{
	... ...	// 把计算结果显示在窗口中
	return S_OK;
}

五、小结


  COM 组件实现事件、通知这样的功能有两个基本方法。今天介绍的回调接口方式非常好,速度快、结构清晰、实现也不复杂;下回书介绍连接点方式(Support Connection Points),连接点方法其实并不太好,速度慢(如果是远程DCOM方式,要谨慎选择它)、结构复杂、唯一的好处就是 ATL 对它进行了包装,所以实现起来反而比较简单。不介绍又不行,因为微软绝大数支持事件的组件都是用连接点实现的,咳......讨厌的微软(注4)。