描述:
高分请教:用MFC开发的非可视ActiveX控件,要在内部实现消息传递,如何实现???
解决方案1:
mark
用SOCKET,我用过没问题
解决方案3: 连接点不适应FireEvent之类需要用消息来通讯的场合
转成有窗口的吧
连接点的实现方法
lostall
连接点技术是在服务器方声明一个接口,由客户端实现这个接口,服务器方内部调用这个接口的方法,从而可以向客户发送事件消息,客户接收事件并可进行处理。
事件接口可以是从IUnknown继承,也可以是从IDispatch继承,考虑到用其他语言也要能实现这个接口,所以应该从IDispatch继承,这其实是双接口形式。实际上用ATL缺省处理的方式,也不是从IDispatch继承的,只是必须通过Invoke实现而已。
一、服务器端添加事件接口
不用做什么工作,在New ATL Object时,选上Support ConnectionPoint就可以了。注意添加的事件接口在IDL文件中,标志的是dispinterface,这表明它既不是从 IUnknown继承也不是从IDispatch继承。
二、客户端实现事件接口
(1)用MFC实现.可参阅MFCTestAtl2的代码(下载)。
用MFC实现,首先要找一个类来实现事件接口,只要是从CCmdTarget派生的类都可以用来实现这个事件接口。参考ConnectionPoint中的例子,这里选择的是CMainFrame。首先在MainFrame.h中:
class CMainFrame : public CFrameWnd
{
DECLARE_DISPATCH_MAP() //声明dispatch map表
DECLARE_INTERFACE_MAP() //建立接口映射表,目的是方便QueryInterface的实现
public:
IAtlTest2Ptr m_pIAtlTest2; //声明一个COM组件对象的实例
DWORD m_dwCookie ; //用于标记一个连接点
void OnShow(); //事件处理函数,没有参数的情况
void OnShow2(short); //事件处理函数,有参数的情况
};
然后修改MainFrame.cpp
#include "afxctl.h" //定义了AfxConnectionAdvise、AfxConnectionUnadvise
....
//填充dispatch map表,以供Invoke()调用
BEGIN_DISPATCH_MAP( CMainFrame, CFrameWnd)
DISP_FUNCTION_ID(CMainFrame, "Show", 1, OnShow, VT_EMPTY, NULL)
DISP_FUNCTION_ID(CMainFrame, "Show2", 2, OnShow2, VT_EMPTY, VTS_I2)
END_DISPATCH_MAP( )
//填充接口映射表,方便QueryInterface处理,因为它是根据IID来判断接口的
//因为IDispatch已被CCmdTarget嵌套了,所以不需要在.h文件中用BEGIN_INTERFACE_PART(,)和
//END_INTERFACE_PART()来声明这个嵌套类,这里是struct XDispatch{...}m_xDispatch
BEGIN_INTERFACE_MAP( CMainFrame, CFrameWnd)
INTERFACE_PART(CMainFrame, DIID__IAtlTest2Events, Dispatch)
END_INTERFACE_MAP()
void CMainFrame::OnShow()
{
AfxMessageBox("connection point success 1");
}
void CMainFrame::OnShow2(short)
{
AfxMessageBox("connection point success 2");
}
CMainFrame::CMainFrame()
{
// TODO: add member initialization code here
m_pIAtlTest2 = NULL;
//Initialize COM libraries...create MTA...
CoInitialize(NULL);
//这个函数将m_xDispatch与一个COleDispatchImpl对象联系了起来,COleDispatchImpl实现了
//IDispatch接口,且数据结构与m_xDispatch相同,所以可以赋值给它。这样就等于m_xDispatch
//实现了IDispatch,把它传给COM组件后,COM组件就可以调用它的Invoke()函数(如在FireShow()中),
//COleDispatchImpl::Invoke()中用到了dispatch map,这个表由DECLARE_DISPATCHMAP声明,
//在BEGIN_DISPATCHMAP(,)、END_DISPATCHMAP()中被填充,比如Show()、Show2()。这样Invoke()
//就可以根据dispid在dispatch map中找到这两个函数,从而调用它们,从而实现了连接点。
//另一方面,也可以看到用dispinterface声明的事件接口本身并不是从哪个接口继承的,既不是IUnknown
//也不是IDispatch,只不过通过一系列手段,使Invoke()能识别其中的属性、方法而已。
//dispinterface表示这个接口是纯IDispatch接口。
EnableAutomation();
//creat the instance of the calculator server...
m_pIAtlTest2.CreateInstance(__uuidof(AtlTest2));
//advise the server that this (mainframe) is the sink for events...
//这个函数内部将进行QueryInterface(IConnectionPointContainer,..)、FindConnectionPoint()、Advise()等一系列调用
BOOL Ret = AfxConnectionAdvise(
m_pIAtlTest2, //可连接对象的接口指针
DIID__IAtlTest2Events, //连接接口ID
GetIDispatch(FALSE), //把内嵌的IDispatch实现类的一个对象实例m_xDispatch传了出去
FALSE, //donod addref
&m_dwCookie ); //cookie to break connection later...
}
CMainFrame::~CMainFrame()
{
if(m_pIAtlTest2)
{
//break the connection...donot receive any more events...
//将调用Unadvise()
AfxConnectionUnadvise( m_pIAtlTest2,
DIID__IAtlTest2Events ,
GetIDispatch(FALSE),
FALSE,
m_dwCookie );
m_pIAtlTest2.Release();//release the object...stops any threads internal to object...
m_pIAtlTest2 = NULL;
}
//unload com libraries...
CoUninitialize();
}
再在stadafx.h中加入
#import "..\test2.tlb" no_namespace named_guids
然后就可以在其他地方调用m_pIAtlTest2的函数了
(2) 用ATL实现事件接口
用ATL实现第一步想到的一样也是要找个类来实现事件接口,用IDispEventImpl这个类会很方便。可参阅AtlTestAtl2的代码(下载)。
a. 用MFC生成一个对话框程序。
b. 用New ATL Object添加对ATL的支持,但并不真的添加新的ATL对象。
c. 在stadafx.h中加入
#import "..\test2.tlb" raw_interfaces_only, no_namespace, named_guids
d. 添加一个从IDispEventImpl派生的新类。
#ifndef __EVENTSINK_H
#define __EVENTSINK_H
namespace
{
static const int DISPID_SHOW = 1;
static const int DISPID_SHOW2 = 2;
_ATL_FUNC_INFO OnShowInfo =
{
CC_STDCALL, //calling conv...
VT_EMPTY, //return value...
0 , //number of arguments...
NULL //argumnent types...
};
_ATL_FUNC_INFO OnShow2Info =
{
CC_STDCALL, //calling conv...
VT_EMPTY, //return value...
1 , //number of arguments...
{VT_I2} //argumnent types...
};
}
class CEventSink : public IDispEventImpl<1, CEventSink, &DIID__IAtlTest2Events, &LIBID_TEST2Lib>
{
public:
CEventSink(){};
virtual ~CEventSink(){};
void __stdcall OnShow(); //没有参数的情况
void __stdcall OnShow2(short);//有参数的情况
//必须要用SINK_ENTRY_INFO,必须指定参数信息
BEGIN_SINK_MAP(CEventSink)
SINK_ENTRY_INFO(1,DIID__IAtlTest2Events,DISPID_SHOW,OnShow,&OnShowInfo)
SINK_ENTRY_INFO(1,DIID__IAtlTest2Events,DISPID_SHOW2,OnShow2,&OnShow2Info)
END_SINK_MAP()
};
#endif