15连接点vc60Word格式文档下载.docx
- 文档编号:451534
- 上传时间:2023-04-28
- 格式:DOCX
- 页数:13
- 大小:209.27KB
15连接点vc60Word格式文档下载.docx
《15连接点vc60Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《15连接点vc60Word格式文档下载.docx(13页珍藏版)》请在冰点文库上搜索。
(你想写个IE的插件吗?
我们后面就要讲到啦)
3、每一个连接点,可以被多个客户端的接收器(Sink)连接;
这个我们已经熟悉啦,还记得我们在上回书中为了管理多个回调接口,使用了cookie的方式进行区别吗?
!
三、实现组件
(一)
1、建立一个工作区(WorkSpace)
2、在工作区中,建立一个ATL工程(Project)。
示例程序中工程名称叫Simple15,接受全部默认选项。
3、ClassView中,执行鼠标右键菜单命令NewAtlObject...,添加ALT类。
4、左侧分类Category选择Objects,右侧Objects选择SimpleObject(其实就是默认项目)。
5、名称Name卡片中,输入组件名称。
示例程序中是DispConnect。
6、属性Attributes卡片中,接口类型选Dual双接口。
注意一定要选择SupportConnectionPoints来支持连接点。
7、ClassView中,选择接口(IDispConnect),鼠标右键菜单添加函数AddMethod...
8、增加函数。
和上回书的程序一样,增加一个接口函数计算加法,但通过连接点接口返回计算结果。
9、下面该增加“事件”函数了。
选择事件接口(_IDispConnectEvents),添加函数。
10、该函数用来返回Add()函数的计算结果。
11、切换到FileView卡片,编译IDL文件。
当然你也可以直接编译全部工程。
其实编译的目的是为了从IDL文件产生TLB文件,因为VC的IDE环境只有知道了TLB后,才能生成下面的“事件代理类的程序代码”。
12、生成事件代理类程序代码。
选择组件类对象(CDispConnect),执行鼠标右键菜单“实现连接点”
13、选择你要让IDE帮你生成哪个连接点的代理程序代码。
我们这个组件只有一个连接点,那只好选择它了。
(在示例二中,我们需要实现两个连接点,那个时候,你就要选择两个了)
14、到此,VC的IDE终于帮咱们完成了所有的框架,下面该咱们自己写真正的任务代码啦。
STDMETHODIMPCDispConnect:
:
Add(longn1,longn2)
{
longnVal=n1+n2;
Fire_Result(nVal);
//调用IDE帮我们生成的代理函数代码,发出事件
returnS_OK;
}
15、修正IDE产生的代码中的错误。
你不用死记硬背错误点,只要编译一下就会报出错误了。
一般VC6帮我们生成的代码中,有2个地方可能会有BUG。
一是打开头文件,找到连接点影射宏,修改如下:
BEGIN_CONNECTION_POINT_MAP(CDispConnect)
CONNECTION_POINT_ENTRY(DIID__IDispConnectEvents)//修改IID_XXXX为DIID_XXXX
END_CONNECTION_POINT_MAP()
这个错误简直可恨,既然我们使用的是双接口连接点,它生成的代码居然不会判断吗?
另一个可能的错误可能发生在代理类中的Fire_xxxx()函数中。
在示例程序中的Fire_Result()函数代码,大家自己去阅读,简单说就是循环地取得每个和自己连接对象(每个cookie表示的对象)的接口指针,(如果是自动化接口,则再取得IDispatch接口指针),然后调用事件函数。
你不理解它现在没有太大的关系,不过在后面的示例二中,它给我们产生的代码是有错误的,我们需要进行修改。
这是后话,待会儿再说。
四、实现调用者
(一)
1、建立一个MFC工程(Project)。
示例程序中的工程名称叫Use。
2、按照咱们以前所学的知识,添加#import、AfxOleInit()、......不多浪费口条了。
如果你还不会,那么请重新从“第四回”再次阅读。
(注2)
3、这里只介绍一下重点部分。
我们需要在调用者工程中,增加“接收器”对象。
还记得上回书中的增加“回调接收器”对象的方法吗?
上回中,我们的回调接口是从IUnknown继承下来的。
本回中,由于我们的组件是双接口(Dual)的,连接点也是双接口的,因此这次我们的接收器要从IDispatch派生啦。
4、完成CSink类的接口函数(虚函数)
STDMETHODIMPCSink:
QueryInterface(conststruct_GUID&
iid,void**ppv)
*ppv=this;
ULONG__stdcallCSink:
AddRef(void)
{return1;
}//做个假的就可以,因为反正这个对象在程序结束前是不会退出的
Release(void)
{return0;
GetTypeInfoCount(unsignedint*)
{returnE_NOTIMPL;
}//不用实现,反正也不用
GetTypeInfo(unsignedint,unsignedlong,structITypeInfo**)
GetIDsOfNames(conststruct_GUID&
unsignedshort**,unsignedint,unsignedlong,long*)
Invoke(
longdispID,
conststruct_GUID&
unsignedlong,
unsignedshort,
structtagDISPPARAMS*pParams,
structtagVARIANT*,
structtagEXCEPINFO*,
unsignedint*)
{//只需要实现这个就足够啦
switch(dispID)//根据不同的dispID,完成不同的回调函数
{
case1:
......//这里就能接收到COM发出的事件啦
break;
case2:
......//事件的代号dispID其实就是IDL文件中的连接点函数的id(n)的号码
default:
break;
}
五、示例
(二)
示例程序中的第2个组件(MultConnect),我们再增加一个连接点(_IDispConnectEvents2)。
这个接口对象负责完成一个时钟,每间隔一定的毫秒就向调用者发出“时钟事件”。
增加第二个连接点的方法是要手工修改IDL文件
......
libraryMULTCONNECTLib
importlib("
stdole32.tlb"
);
stdole2.tlb"
......//第一个,ATL框架默认给我们生成的连接点接口描述
[//需要手工增加第二个或更多个连接点
uuid(F81DB93F-4F63-4A55-8114-A32BC78466D3),//CLSID可以用GUIDGEN.EXE来产生
helpstring("
_IDispConnectEvents2Interface"
)
]
dispinterface_IDispConnectEvents2
properties:
methods:
};
[
uuid(9461BE82-0D64-4E3B-B0DB-2306D1BFE3F0),//这是示例程序的类型库ID,肯定和你生成的不一样的啦
DispConnectClass"
coclassDispConnect
[default]interfaceIDispConnect;
[default,source]dispinterface_IDispConnectEvents;
[source]dispinterface_IDispConnectEvents2;
//别忘了,这里还有一行呢
};
好了,和前面的方式一样,增加接口函数、编译IDL文件、让IDE帮我们实现代理类代码、输入程序代码、修改框架代码中的BUG。
在示例中,我们的事件函数叫HRESULTTimer([in]VARIANTvarData),varData中传递一个时间类型(VT_DATA)的信息(注3)。
下面我们来看一下代理类代码中的错误:
HRESULTFire_Timer(VARIANTvarDate)
CComVariantvarResult;
T*pT=static_cast(this);
intnConnectionIndex;
CComVariant*pvars=newCComVariant[1];
intnConnections=m_vec.GetSize();
for(nConnectionIndex=0;
nConnectionIndex<
nConnections;
nConnectionIndex++)
pT->
Lock();
CComPtrsp=m_vec.GetAt(nConnectionIndex);
Unlock();
IDispatch*pDispatch=reinterpret_cast(sp.p);
if(pDispatch!
=NULL)
{
VariantClear(&
varResult);
//原始代码,这里居然是pvars[0]=&
varData?
愚蠢之极!
只好你自己修改啦
pvars[0]=varDate;
DISPPARAMSdisp={pvars,NULL,1,0};
pDispatch->
Invoke(0x1,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,&
disp,&
varResult,NULL,NULL);
}
delete[]pvars;
returnvarResult.scode;
在编写调用者客户端代码方面,如果你需要接收时钟事件,那么可以仿照示例一再从IDispatch派生一个时钟接收器。
大家下载事例程序代码,里面有丰富的注释信息。
六、小结
连接点,尤其是双接口的连接点,在远程(DCOM)环境上运行效率是比较低的。
如果你只想完成简单的“通知”功能,那么前一回中的“回调接口”是一个明智的方案,并且可以运行在DCOM环境上。
连接点方案当然也很重要,因为微软的许多应用程序(IE、Office......)都支持连接点,并且ActiveX只能通过连接点接口提供“事件”功能。
所以,咱们还是都掌握为善吧。
善哉、善哉......
注1:
金庸老先生的武侠小说里,总是用“XX紧”来表示“很XX”。
我也学一学,嘿嘿。
注2:
如果看了好几遍,您老人家还不会的话,那只好......先别学了。
5555
注3:
DATA类型就是是8字节的double,它的整数部分表示从1899年12月30日开始的总天数,小数部分表示当天的时间已经渡过了一天的多少分之一。
这个时间类型,用VARIANT表示,就是VT_DATE类型,MFC中用COleDateTime表示。
示例程序中有对该类型的操作示范。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 15 连接 vc60