应该使用哪个框架?用ATL和MFC来创建ActiveX控件3

下面的代码显示了MFCMsgTraffic控件是怎样将它的颜色和时间间隔属性保存起来的:

void CMFCMsgTrafficCtrl::DoPropExchange(CPropExchange* pPX)

{

ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));

COleControl::DoPropExchange(pPX);

PX_Color(pPX, "GraphLineColor", m_graphLineColor);

PX_Long(pPX, "GraphInterval", m_interval);

}

MFC包括了若干PX_函数在控件和存储媒体间转移数据,它们是:

PX_Short

PX_UShort

PX_Long

PX_ULong

PX_Color

PX_Bool

PX_String

PX_Currency

PX_Float

PX_Double

PX_Blob

PX_Font

PX_Picture

PX_IUnknown

PX_VBXFontConvert

PX_DataPath

在ATL中管理控件属性持续涉及到两个步骤。第一步是添加你希望客户能够使用的持续接口的ATL实现。ATL包括了类IPersistStorageImpl, IPersistStreamInitImpl, 和 IPersistPropertyBagImpl, 它们实现了三个主COM持续机制。

第二步是在控件的属性映射中插入属性。当一个客户请求保存或者加载基于ATL的控件时,ATL检查控件的属性映射表,将控件的属性输出到存储媒介,或者从存储媒介输入。属性映射表是属性名字、DISPIDs的一个表,有时还包括一个属性页面GUID。ATL遍历词表查找该持续哪个属性,并将其持续到合适的媒体。图5显示了继承了持续接口实现和一个属性映射的ATLMsgTraffic控件。

属性页

ActiveX控件经常在开发者将控件放到各类容器时提供属性页帮助开发者。将消息流控件放入一个对话框的开发者可能想要配置控件的各个方面,象控件的取样间隔,或者绘图线条的颜色。例如,当控件放在一个对话框中,你想通过右击鼠标得到控件的属性时,Visual Studio显示了一个突出的对话框。这里将说明其工作过程。

Visual Studio请求控件在一个对话框框架里显示属性页(Visual Studio IspecifyPropertyPages接口请求控件提供一个属性页的清单),属性页显示在Visual Studio中,但是通过控件提供的一个COM接口,保持与控件的连接。每当你完成了属性编辑并从Visual Studio中关闭了对话框,它就会要求属性页更新控件中的属性。

当你生成一个MFC的控件时,wizard给你一个对话框模板和一个从ColePropertyPage中派生的代表此控件的缺省属性页的类。Visual Studio使得实现一个控件的属性和此属性页中的属性的连接变得容易了。当你使用ControlWizard的Automation tab添加属性到你的基于MFC的控件中的时候,你给了属性一个外部名字。这个名字是外部客户方(包括属性页)用来识别该属性的。

你按照开发其它任何对话框的方法来开发属性页——将控件添加到对话框模板,将对话框成员变量和控件联系起来。ControlWizard添加DDX/DDV代码在对话框控件和成员变量之间交换数据。然而,当你将成员变量和对话框控件相关联时,ControlWizard给你提供了这样一个机会,你可以将外部属性名字用于对话框的成员变量。此外部名字是你给控件添加属性时键入的字符串。

当属性页需要将变化应用于控件时(例如当按下Apply按钮时),属性页使用控件的Idispatch接口以及外部名字来修改控件的属性。在MFC中,你可以通过ClassWizard来添加一个新属性,添加一个新的对话框模板到工程中,让ClassWizard创建一个类——要确保是从ColePropertyPage中派生出来的类。然后,为了使新的属性页可以被外界访问到,将它的GUID添加到控件的属性页映射中(在控件的.CPP文件中查找BEGIN_ PROPPAGEIDS 和 END_PROPPAGEIDS两个宏)。不象MFC的ActiveX ControlWizard,ATL COM App Wizard并不向DLL中添加缺省的属性页。这意味着你要自己完成此工作。幸运的是,又一个wizard可以向属性页中添加基于ATL的DLL。只要选择Insert ATL Object,然后找到属性页对象。Wizard将一个对话框模板和一个C++类与所有必要的COM goo一起添加到一个属性页中。让它们完成什么工作是你的事情。

不幸的是,ATL属性页的wizard驱动特性不如基于MFC的属性页,你得手工完成应用和显示操作。这就意味着提供Apply 和 Show的函数实现到你的属性页类中。Apply函数只是提取对话框中控件的状态,遍历属性页拥有的指向控件的接口指针列表,使用接口指针来修改控件属性。Show函数通常提取控件的状态,然后以次来组织对话框的控件。下面的代码显示了基于ATL的属性页是怎样处理Apply函数的:

STDMETHOD(Apply)(void)

{

long nInterval = GetDlgItemInt(IDC_EDITINTERVAL);

ATLTRACE(_T("CMainPropPage::Apply/n"));

for (UINT i = 0; i < m_nObjects; i++)

{

IATLMsgTrafficCtl* pATLMsgTrafficCtl;

m_ppUnk[i]->QueryInterface(IID_IATLMsgTrafficCtl,

(void**)&pATLMsgTrafficCtl);

if(pATLMsgTrafficCtl) {

pATLMsgTrafficCtl->put_Interval(nInterval);

pATLMsgTrafficCtl->Release();

}

}

m_bDirty = FALSE;

return S_OK;

}

为基于ATL的控件提供一个属性页的第二步是确保属性页的CLSID出现在控件的属性映射中,图5中列出的属性持续代码提供了它的一个例子。消息映射表明了控件的图线颜色,被标准的颜色属性页管理。控件的取样间隔由控件的主属性页来管理。

Window 消息

MFC和ATL在它们处理window消息方面有很多共同之处,都使用消息映射,都有wizards来生成代码处理window消息。在MFC中,消息映射可以添加到任何一个CcmdTarget派生的类中,然后你就可以用ClassWizard来建立你的控件的事件处理器了。图6显示了基于MFC的控件怎样处理WM_ TIMER消息。另外,MFC提供了处理命令和控件通告的宏。象MFC一样,ATL通过消息映射来处理window消息,只要你的类是从CwindowImpl派生的,而且包含ATL的消息映射宏,你就可以使用类视来建立事件处理器。图7显示了ATL消息流控制是怎样处理WM_TIMER消息的。

ATL使用MESSAGE_HANDLER宏将标准的window消息映射到一个C++类。此宏简单的产生一个将window消息和类的成员函数关联的表。除了常规消息,消息映射还可以处理其它类型的事件。图8显示了能参与消息映射的各种宏。

连接和事件

最后要进行的比较是MFC和ATL各是怎样处理连接点和事件集的。为了管理连接点和事件集,需要一个COM类来实现IconnectionPointContainer,然后创建一种提供指向IconnectionPoint的指针给客户的方法。MFC的主控件类,ColeControl,已经有了内置的IconnectionPointContainer,MFC通过连接映射提供了连接点。MFC已经为IPropertyNotifySink定义了连接点和控件的缺省事件集。

为了完善一个基于MFC的控件的缺省事件集,你只要简单的使用ClassWizard's ActiveX Event tab。在你使用ClassWizard添加事件的时候,Visual Studio更新你的控件的.ODL文件,为潜在的包容器描述外出事件。另外,Visual Studio添加一个函数到你的类中,你可以调用它反过来向包容器激发事件。图9演示了基于MFC的控件的事件触发机制。基于MFC的控件的事件触发函数只是由包容器在它和控件建立连接点时提供的一个Idispatch指针的一些简单的包裹器。

在基于ATL的控件中建立事件则有所不同。在基于ATL的控件中,你从定义控件的.IDL文件中的事件开始。接着你建立了类型库的编译工程文件。图10显示了在IDL中描述的基于ATL的控件的事件集。

一但类型库编译通过,你就可以通过在类视中选择控件的类,在类上右击,然后选择Implement Connection Point,让类视来为你创建一个回调代理了。Visual Studio弹出一个对话框,列出控件类型库中所有可访问的事件接口。你选择那些你希望回调代理做的,Visual Studio就为你写一个代理。图11显示了基于ATL的消息流控制的回调代理。Visual Studio产生的回调代理代表了一个C++友好的函数集,被客户实现的接口所调用。

MFC的IconnectionPointContainer实现是硬分布到ColeControl中,并且每个连接点是由一个连接映射处理的,而ATL的实现是用多重继承处理的。你的控件类继承IconnectionPointContainerImpl和类视生成的代理。如果你开始一个工程的时候,选择了"Supports connection points",ObjectWizard就为你添加IconnectionPointContainerImpl。如果你忘了标记检查框,你可以写进去。此代码显示了连接点机制是怎样加入一个控件中的。类ATL_NO_VTABLE CATLMsgTrafficCtl :

{

&#8226;&#8226;&#8226;

public IConnectionPointContainerImpl<CATLMsgTrafficCtl>,

public CProxy_DATLMsgTrafficEvents<CATLMsgTrafficCtl>

&#8226;&#8226;&#8226;

{

LRESULT OnTimer(UINT msg, WPARAM wParam,

LPARAM lParam,

BOOL& bHandled) {

//&#8226;&#8226;&#8226;

if(nMessagesToShow > m_threshold)

{

Fire_ExceededThreshold(nMessagesToShow, m_threshold);

}

//&#8226;&#8226;&#8226;

}

};

作为一个应用框架ATL和MFC的比较

最近,许多开发者开始对使用ATL作为框架来开发应用和控件感兴趣了。当然,MFC已经使用了很长时间了,是一个能够开发可双击的基于Windows的应用的非常成熟的框架。例如,MFC包括了这样的特性作为一个总体文档/视体系结构:Object Linking 和 Embedding支持, 以及工具条和状态栏。

然而,所有这些功能都是有代价的。一些更加普遍的抱怨是MFC的比较深的足迹(无论是在DLL中,或是在静态连接的版本中),以及自身的某种相互依存性。例如,买入MFC的一种特性意味着买入MFC的对象连接和嵌入,这意味着买入MFC的文档/视结构。另一方面,ATL是一个原始的框架,没有任何应用框架goodies。

正象你已经看到的,两个框架都提供创建控件的可行途径。然而,两者都各有千秋,也各有弊病。用MFC编写控件通常更加容易——尤其是如果你不是在开发COM集中的应用并且你需要windowing和drawing支持。ATL的体系架构更加靠近COM的核心,你还会经常发现自己在编写很多的SDK类型的代码——就是说,你在回过头来用window和设备上下文句柄。ATL为更广范围的控件类型提供了很大的支持,象复合控件,基于HTML的控件,没有design-time接口的轻量级的控件等等。MFC仅提供完全成熟的控件。

对ATL进行分支的实现是非常直接的。例如,增加一个接口通常是一件添加接口到继承表,在COM映射中添加一个入口然后实现接口函数的工作。分支MFC的实现通常是一种折磨。例如,添加一个接口到基于MFC的控件意味着处理所有那些接口映射宏。

最后,ATL提供了大量的调试支持,包括接口引用计数以及QueryInterface调试支持,这在MFC中是没有的。

这两种体系架构的区别是非常明显的。通常,MFC使得你很快完成你的工程并更快的运行起来,但是牺牲了灵活性。ATL没有那样快,那样容易使用,但是它是COM友好的。而且,好像随着ATL的成熟,它将会越来越容易使用。  
 更多分享请关注:软信网-编程-http://www.iis365.net.cn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值