实现组件(一)

来源:互联网 发布:铭万网络 编辑:程序博客网 时间:2024/06/05 16:32

三、实现组件(一)

1、建立一个工作区(WorkSpace)
2、在工作区中,建立一个 ATL 工程(Project)。示例程序中工程名称叫 Simple15,接受全部默认选项。
3、ClassView 中,执行鼠标右键菜单命令 New Atl Object...,添加 ALT 类。
4、左侧分类 Category 选择 Objects,右侧 Objects 选择 SimpleObject(其实就是默认项目)。
5、名称 Name 卡片中,输入组件名称。示例程序中是 DispConnect。



6、属性 Attributes 卡片中,接口类型选 Dual 双接口。注意一定要选择 Support Connection Points 来支持连接点。



7、ClassView 中,选择接口(IDispConnect),鼠标右键菜单添加函数 Add Method...



8、增加函数。和上回书的程序一样,增加一个接口函数计算加法,但通过连接点接口返回计算结果。



9、下面该增加“事件”函数了。选择事件接口(_IDispConnectEvents),添加函数。



10、该函数用来返回 Add() 函数的计算结果。



11、切换到 FileView 卡片,编译IDL文件。当然你也可以直接编译全部工程。其实编译的目的是为了从IDL文件产生TLB文件,因为 VC 的 IDE 环境只有知道了 TLB 后,才能生成下面的“事件代理类的程序代码”。



12、生成事件代理类程序代码。选择组件类对象(CDispConnect),执行鼠标右键菜单“实现连接点”



13、选择你要让 IDE 帮你生成哪个连接点的代理程序代码。我们这个组件只有一个连接点,那只好选择它了。 (在示例二中,我们需要实现两个连接点,那个时候,你就要选择两个了)



14、到此,VC 的 IDE 终于帮咱们完成了所有的框架,下面该咱们自己写真正的任务代码啦。

STDMETHODIMP CDispConnect::Add(long n1, long n2){long nVal = n1 + n2;Fire_Result( nVal );// 调用IDE帮我们生成的代理函数代码,发出事件return S_OK;}
15、修正 IDE 产生的代码中的错误。你不用死记硬背错误点,只要编译一下就会报出错误了。一般 VC6 帮我们生成的代码中,有2个地方可能会有BUG。一是打开头文件,找到连接点影射宏,修改如下:
BEGIN_CONNECTION_POINT_MAP(CDispConnect)CONNECTION_POINT_ENTRY(DIID__IDispConnectEvents)// 修改 IID_XXXX 为 DIID_XXXXEND_CONNECTION_POINT_MAP()
  这个错误简直可恨,既然我们使用的是双接口连接点,它生成的代码居然不会判断吗?另一个可能的错误可能发生在代理类中的 Fire_xxxx() 函数中。在示例程序中的 Fire_Result() 函数代码,大家自己去阅读,简单说就是循环地取得每个和自己连接对象(每个cookie表示的对象)的接口指针,(如果是自动化接口,则再取得 IDispatch 接口指针),然后调用事件函数。你不理解它现在没有太大的关系,不过在后面的示例二中,它给我们产生的代码是有错误的,我们需要进行修改。这是后话,待会儿再说。

四、实现调用者(一)

1、建立一个 MFC 工程(Project)。示例程序中的工程名称叫 Use。
2、按照咱们以前所学的知识,添加 #import、AfxOleInit()、......不多浪费口条了。如果你还不会,那么请重新从“第四回”再次阅读。 (注2)
3、这里只介绍一下重点部分。我们需要在调用者工程中,增加“接收器”对象。还记得上回书中的增加“回调接收器”对象的方法吗?上回中,我们的回调接口是从 IUnknown 继承下来的。本回中,由于我们的组件是双接口(Dual)的,连接点也是双接口的,因此这次我们的接收器要从 IDispatch 派生啦。



4、完成 CSink 类的接口函数(虚函数)
STDMETHODIMP 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::GetTypeInfoCount(unsigned int *){return E_NOTIMPL;}// 不用实现,反正也不用STDMETHODIMP CSink::GetTypeInfo(unsigned int,unsigned long,struct ITypeInfo ** ){return E_NOTIMPL;}// 不用实现,反正也不用STDMETHODIMP CSink::GetIDsOfNames(const struct _GUID &,unsigned short ** ,unsigned int,unsigned long,long *){return E_NOTIMPL;}// 不用实现,反正也不用STDMETHODIMP CSink::Invoke(long dispID,const struct _GUID &,unsigned long,unsigned short,struct tagDISPPARAMS * pParams,struct tagVARIANT *,struct tagEXCEPINFO *,unsigned int *){// 只需要实现这个就足够啦switch(dispID)// 根据不同的dispID,完成不同的回调函数{case 1:......// 这里就能接收到 COM 发出的事件啦break;case 2:......// 事件的代号 dispID 其实就是 IDL 文件中的连接点函数的id(n)的号码break;default:break;}return S_OK;}
原创粉丝点击