ActiveX控件实现

完成本章学习后,您将可以:javascript



  • Ø         了解什么是ActiveX控件 
  • Ø         掌握如何编写基于MFC的ActiveX控件 
  • Ø         掌握如何测试ActiveX控件 
  • Ø         掌握如何测试ActiveX控件 
  • Ø         了解ActiveX控件如何注册

 

重点:ActiveX控件的实现、测试及注册。html

 

本章将介绍ActiveX控件的应用与工做原理。咱们能够把ActiveX控件看作是一个极小的服务器应用程序,它不能独立运行,必须嵌入到某个容器程序中,与该容器一块儿运行。那么,该容器就至关于客户程序,它使用ActiveX提供的服务。java

 

注:本文改编自孙鑫教程,在此基础上加入了Active控制与网页的交互。编程

 

1.1 什么是ActiveX控件

 

Activex控件是微软提供的功能强大的程序设计和开发技术。ActiveX是基于OLE和COM的一门开发技术,它既是一个自动化对象,也是一个COM对象。根据微软权威的软件开发指南MSDN(Microsoft Developer 
Network)的定义,ActiveX控件之前也叫作OLE控件或OCX控件,它是一些软件组件或对象,能够将其插入到WEB网页或其它应用程序中。在形态上ActiveX控件是后缀名为ocx的控件,可是,读者应该注意的是,Activex控件对应的文件也能够是其余后缀名,如.dll。浏览器

 

一个典型的ActiveX控件,它具备方法、属性、事件这三种特性。服务器

 

 

 

1.2 ActiveX控件的好处

 

在实际编程中,咱们能够将经常使用的功能封装在一个ActiveX控件中,而后将该控件提供给VB或Delphi的开发人员使用。例如,咱们开发了一个中国地图控件,正好有一个公司有许多分支机构,如麦当劳,它会不断地在全国各地增长它的加盟店,而麦当劳公司总部须要实时地观测它每个月新增的这些加盟店的地理位置。因而麦当劳公司就能够直接购买咱们开发的这个地图控件,在地图上显示它们各分支机构的位置,而不须要再自行开发这种控件了。如今不少公司都在作Activex控件的开发,将一些经常使用功能封装到一个ActiveX控件中,而后提供给其余公司或最终用户直接使用。框架

 

1.3 用MFC编写基本的ActiveX控件

 

下面,咱们利用VC++编写一个ActiveX控件,这能够利用MFC ActiveX 
ControlWizard为我编辑器

 

生成一个ActiveX控件程序的框架。MFC为ActiveX控件的开发提供了很好的支持,对ActiveX来讲,它的底层其实是采用COM技术实现的,可是利用MFC ActiveX ControlWizard,即便对COM不了解,咱们也能够开发出一个功能完善的ActiveX控件。函数

 

本例将开发一个时钟控件。在VC++开发环境中,选择【File\New】菜单项,在打开的对话框上选择Projects选项卡,并在列表框中选择MFC 
ActiveX ControlWizard,工程名设置为:Clock。单击【OK】按钮,进入MFC ActiveX 
ControlWizard向导的第一步,以下图所示:工具

 

 

 

 

这里,第一个选项的做用是询问用户该工程中将要提供的控件数目。注:一个文件中能够包含多个ActiveX控件。本例中,咱们对以上选项都选择默认。

 

单击【Next】按钮,进入MFC ActiveX 
ControlWizard向导的第二步,以下图所示。

 

 

 

 

单击【Finish】,就建立了一个MFC ActiveX控件工程。咱们能够看到,MFC 
ActiveX ControlWizard向导建立的工程自动生成了三个类,以下图所示:

 

 

 

 

其中CClockApp类派生于COleControlModule类,然后者的派生层次见下图

 

 

 

 

能够看到,COleControlModule类是从CWinApp类派生的,因此能够把该类看做是一个应用程序类,它的实例表示了控件程序自己。也就是说,CClockApp类至关于单文档应用程序的应用程序类。

 

CClockCtrl类派生于COleControl类,后者的派生层次结构以下图所示:

 

 

能够看到,COleControl类是从CWnd类派生的,所以,它也是一个窗口类,至关于单文档应用程序中的主窗口类,或者视类,那么对控件窗口进行的操做都将在CClockCtrl类中完成。在该类中,能够看到它提供了一个OnDraw函数,当控件窗口发生重绘时就会调用这个函数。若是控件须要输出图形,就能够在这个函数中编写相应的实代码。

 

咱们先来看看CClockCtrl类头文件中的部份内容:





// Message 
maps

 


       //{{AFX_MSG(CClockCtrl)


              // NOTE – ClassWizard will add and remove 
member functions here.


              //    DO NOT EDIT what you see in these 
blocks of generated code !


       //}}AFX_MSG


       DECLARE_MESSAGE_MAP()


 


// 
Dispatch maps


       //{{AFX_DISPATCH(CClockCtrl)


              // NOTE – ClassWizard will add and remove 
member functions here.


              //    DO NOT EDIT what you see in these 
blocks of generated code !


       //}}AFX_DISPATCH


       DECLARE_DISPATCH_MAP()


 


       afx_msg void AboutBox();


 


// 
Event maps


       //{{AFX_EVENT(CClockCtrl)


              // NOTE – ClassWizard will add and remove 
member functions here.


              //    DO NOT EDIT what you see in these 
blocks of generated code !


       //}}AFX_EVENT


       
DECLARE_EVENT_MAP()

 

 

 

咱们能够看到,在该文件中不只提供了一个消息映射,它还提供了一个调度映身和事件映射。其中调度映射是MFC提供的一种映射机制,主要是为了让外部应用程序能够方便地访问控件的属性和方法,而事件映射也是MFC提供的一种映射机制,让控件能够向包含它的容器发送事件通知。稍后咱们为Clock控件添加方法和属性时就会用到这两个映射。

 

CClockPropPage类派生于COlePropertyPage类,后者的派生层次结构以下图所示:

 

 

 

 

能够看到,COlePropertyPage类派生于CDialog类,它以一种相似于对话框的图形界面显示一个自定义控件的属性。也就是说,CClockPropPage类是用来显示Clock控件的属性页的。

 

另外,读者能够看到在该工程中还有两项内容:_DClock和_DClockEvents,之母们的前面都有一个像平放着小勺同样的图标(),该图标表示对应的项是接口,接口是控件与外部程序进行通讯的协议。能够把接口看做是函数的集合,外部程序经过这个接口所暴露出来的方法去访问控件的属性和方法。实际上,能够把接口看做是一个抽象基类,在此接口中定义的全部函数都是纯虚函数,这些函数的实现是在CClockCtrl类中完成的。MFC经过底层的封装,让CClockCtrl类继承自接口:_DClock,因此经过该接口调用的函数其实是调用CClockCtrl类中真正实现的函数。ActiveX控件中的接口与计算机机硬件的接口是相似的,例如,在计算机硬件中,主板与显卡间的通讯是经过主板上的插槽完成的,这个插槽就是主板与显卡进行通讯的接口,一旦咱们制定了这个接口,就能够任意地选择一块主板与一块显卡进行通讯。由于该接口是标准的,因此选择任一厂商生产的主板,任一厂商生产的显示都是能够的,只要它们的接口听从共同的标准。主板经过该接口所暴露出来的方法去调用显卡的显示功能,而显卡须要实现该接口所暴露出来的方法。显卡就至关于这里的ActiveX控件,而主板就至关于与控件通讯的外部容器。若是两个通讯实体要经过接口进行通讯,那么确定是其中的一个实体实现该接口所暴露出来的方法,而另外一个实体经过接口调用这些方法。这里,就是ActiveX控件实现接口所暴露出来的方法,而容器调用这些方法。关于接口的底层实现,须要了解一些COM的基本知识,读者如感兴趣的话,可自行查看相关资料。本例中,由于MFC提供的封装,因此底层的细节是看不到的。

 

这里,咱们利用Build(F7)命令生成Clock控件程序,而后在该工程所在目录的Debug目录下,能够看到生成了一个Clock.ocx文件,这就是程序生成的ActiveX控件文件。在使用时,只须要将这个文件传递给使用方,通过注册后就可使用该控件了。

 

 

 

1.4 ActiveX控件的测试

 

咱们在VC++开发环境中运行Clock程序,将出现以下对话框,让用户选择一个可执行程序。

 

 

 

 

前面已经提到,ActiveX控件不能独立运行,它必须嵌入到一个容中运行。所以,咱们能够点击该对话框上标示了一个向右箭头的按钮,将弹出以下的快捷菜单。

 

 

 

 

能够选择【ActiveX Control Test 
Container】菜单项,也就是说,咱们选择ActiveX 
Control Test Container这个应用程序做为Clock控件的容器,该应用程序位于Microsoft Visual Studio安装目录下的Commaon\Tools子目录下,程序名称为:TSTCON32.EXE。若是没有出现这个应用程序所对应的菜单项,那么能够选择【Browse】菜单项,而后找到TSTCON32.EXE程序并选中便可。

 

最后,单击【OK】按钮,这时将打开ActiveX Control Test 
Container应用程序,以下图

 

 

 

 

因而咱们就能够加载特定的ActiveX控件,方法是选择【Edit\Insert New 
Control…】菜单项,这时将弹出以下对话框

 

 

 

 

而后在该对话框左边的列任意选中一个控件,接着快速连续地按下键盘上的【C】、【L】、【O】键,就能够直接定位到咱们刚刚生成的Clock控件。

 

 

 

 

而后单击对话框上的【OK】按钮关闭该对话框,这时,在ActiveX Control Test 
Container应用程序中就加载了Clock控件,这个ActiveX控件当前的功能就是绘制一个椭圆,以下图所示:

 

 

 

 

固然,咱们也能够新建一个VC++对话框工程来进行测试,该工程取名为ClockTest。若是想要在对话框资源上添加一个ActiveX控件,方法是:在对话框资源上单击鼠标右键,从弹出的快捷菜单中选择【Insert 
ActiveX 
Control…】菜单项,这时将显示以下对话框,在此对话框中找到Clock控件并选中,而后单击【OK】按钮关闭该对话框便可。

 

 

 

 

这时,在对话框资源上就插入了Clock控件。

 

在VC++中,另外一种插入ActiveX控件的方法是,选择【Project\Add to 
Project\Componets and Controls…】菜单项,将显示以下对话框:

 

 

 

 

在此对话框中,双击“Registered ActiveX 
Controls”目录,并在此目录下找到Clock控件并选中,以下所示:

 

 

 

 

 

而后单击【Insert】按钮,并单击随后显示的确认对话框上的的【肯定】按钮,这时将弹出以下对话框:

 

 

 

 

当经过这种方法插入ActiveX控件时,会在工程中为该控件生成一个类,这里就为Clock控件生成了一个类,类名为CClock,其基类是CWnd。该类是控件的封装类。它封装了对这个ActiveX控件进行访问的一些操做。单击【OK】按钮关闭该对话框,这时,在ClockTest工程的ClassView选项卡上,能够看到增长了一个类:CClock,该类提供了一些函数,咱们只须要调用这些函数就能够访问Clock这个ActiveX控件的方法和属性。同时,在工具箱上也增长了Clock控件的图标,以下图所示:

 

 

 

 

咱们只须要单击该图标,就能够在对话框资源上拖放一块合适的区域放置一个Clock控件。以下图:

 

 

 

 

1.5 ActiveX控件的注册

 

全部的ActiveX控件必须注册才能使用。实际上,当在VC++开发环境中生成Clock控件程序时,输出窗口以下图所示:

 

 

 

 

咱们看到Registering ActiveX 
Control…,代表在生成Clock控件程序时,VC++环境已经帮咱们注册了该控件。实际上,VC++是编译器是经过调用regsvr32程序去完成这个操做的。

 

若是想要删除ActiveX控件的注册信息,能够利用Regsvr32程序的/u选项来实现。咱们能够选择系统的【开始\运行】命令,而后在对话框上的打开编辑框控件中输入regsvr32 
/u,再在其后输入想要删除的ActiveX控件的完整路径,以下图所示。

 

 

 

 

单击【肯定】按钮,这时会弹出以下所示的信息对话框:

 

 

 

 

该信息框中提示“DllUnregisterServer in 
D:\Code\Clock\Debug\Clock.ocx succeeded”。这里DllUnregisteredServer是一个函数,而且是ActiveX控件提供的一个函数。“regsvr32 /u”这一命令执行是实际上调用的是指定控件的DllUnregisterServer函数来删除控件的注册信息,由于对于regsvr32程序来讲,它并不知道须要删除哪些信息,因此它只是调用控件的DllUnregisterServer函数,由后者来删除该控件在注册表中的注册信息。

 

当删除了Clock控件在注册表中的信息以后,若是在ActiveX Control Test 
Container程序中再想加载Clock控件时,在控件列表中就找不到这个控件了。

 

若是想再次注册Clock控件,仍能够选择regsvr32程序,但不须要使用/u选项,其余同上。这时将显示以下所示对话框

 

 

 

 

在该信息框中提示:“DllRegisterServer in 
D:\Code\Clock\Debug\Clock.ocx succeeded”。一样的,DllRegisterServer也是ActiveX控件提供的一个函数。当执行regsvr32这一命令时,它其实是调用指定控件的DllRegisterServer函数,将该控件的信息写入注册表。所以,实际上,ActiveX控件的注册和取消注册都是利用该控件自身提供的两个函数来完成的,regsvr32程序只是调用这两个函数而已。当注册完成后,在ActiveX Control Test 
Container程序的控件列表中就能够找到Clock控件了。

 

1.6 时间控件的实现

 

下面继续完成Clock控件的实现,让该控件显示系统当前时间,这能够在CClockCtrl类的OnDraw函数中完成。这时,该函数中已经自动生成了两行代码,分别用来填充控件的背景和绘制椭圆,咱们先将这两代码注释起来,而后添加以下代码:





void 
CClockCtrl::OnDraw(

 


                     CDC* pdc, const CRect& 
rcBounds, const CRect& rcInvalid)


{


       // TODO: Replace the following code with your 
own drawing code.


       //pdc->FillRect(rcBounds, 
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));


       //pdc->Ellipse(rcBounds);


       CTime 
time=CTime::GetCurrentTime();


       CString 
str=time.Format(“%H:%M:%S”);


       pdc->TextOut(0,0,str);


}

 

 

 

若是想要得到当前系统时间,可使用CTime类的静态方法:GetCurrentTime,该函数将返回表示系统当前时间的CTime对象,以后就能够利用CTime对象的Format方法对获得的CTime类型的时间进行格式化,返回一个CString对象,而后将显示时间的字符串显示在控件窗口中。

 

在VC++开发环境中,利用Build命令生成Clock控件程序,并运行,以下图所示:

 

 

 

 

但是,这时控件显示的时间是静止的,为了让该时间“动起来”,咱们须要设置一个定时器,让它每隔一秒钟发送一个WM_TIMER消息,在响应该定时器的消息处理函数中,让该控件刷新,从新办理出当前系统时间。这里,咱们须要在控件窗口建立完成以后设置定时器,为此咱们须要为CClockCtrl类增长WM_CREATE消息的处理函数,而后在些函数中,在控件窗口建立完成以后,调用SetTimer函数建立定时器。具体代码以下所示:





int 
CClockCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 

 


{


       if (COleControl::OnCreate(lpCreateStruct) == 
-1)


              return -1;


       


       // TODO: Add your specialized creation code 
here


       


       SetTimer(1,1000,NULL);


       return 0;


}

 

接下来,再为CClockCtrl类增长Windows消息:WM_TIMER的处理,在其响应函数OnTimer中调用Invalidate函数,使窗口无效,这样就可使窗口重绘。具体实现代码以下所示:





void CClockCtrl::OnTimer(UINT nIDEvent) 
{

 


       // TODO: Add your message handler code here 
and/or call default


       Invalidate();


       
COleControl::OnTimer(nIDEvent);


}

 

 

 

Build并运行Clock控件,将会看到这时这个时钟控件显示的时间随系统当前时间变化而变化了。

 

1.7 属性

 

读者能够发现,在VB中提供了一个以下图所示的属性面板,经过此面板,能够修改控件属性的值。该面板的左边列出了控件的一些属性,对Clock控件来讲,当前咱们没有为它添加任一属性,都是MFC ActiveX 
ControlWizard自动生成的属性;面板的右边就是属性对应的值,例如,控件的Name(名称)属性是Clock1。若是咱们想要改变Clock控件的前景色和背景色,却发现Clock控件的前景色和背景色,却发如今Clock控件的属性面板中没有看到前景色和背景色这两种属性。可是若是在VB程序的窗体上放置一个列表框控件,而后在属性面板上就能够看到该控件有BackColor(背景色)和ForeColor(前景色)属性,能够用来设置该控件的背景色和前景色。若是但愿为Clock控件也提供这样的属性,让用户能够设置该控件的前景色和背景色,那么就须要在VC++开发环境中继续完善Clock控件,为它添加这样的属性。

 

 

1.7.1   标准属性

 

在VC++开发环境中,若是想要为控件添加属性,能够经过ClassWizard来完成。首先打开ClassWizard对话框,而后选择Automation选项卡,接着单击该选项卡上的【Add 
Property…】按钮,将出现以下图所示的添加属性对话框:

 

 

 

 

在此对话框上,单击External 
name(外部名称)下拉列表框,将会看到在出现的列表框中有许多属性,这些都是MFC为ActiveX控件提供的标准属性,其中就有BackColor(背景色)和ForeColor(前景色)属性,若是想要为控件添加某种标准属性,只要从该列表中选择该属性,例如选择BackColor,并保持默认的Stock选项选中状态,单击【OK】按钮即为控件添加了背景色属性。而后按照一样的方法为Clock控件添加ForeColor属性。这时,Clock控件的Automation选项卡内容以下图所示:

 

 

 

 

能够看到,新添加的这两个属前面都有一个“S”标志,并且下面的提示说明它们是一个“Stock Property”,即常规的,或储备的属性。在ActiveX控件中有四种属性。

 

l         Stock为每一个控件提供的标准属性,如字体或颜色。

 

l         Ambient围绕控件的环境属性——已被置入容器的属性。这些属性不能被更改,但控件可使用它们调整本身的属性。

 

l         Extended这些是由容器处理的属性,通常包括大小和在屏幕上的位置

 

l         Custom由控件开发者添加的属性。

 

单击上图所示对话框上的【OK】按钮关闭ClassWizard对话框,而后,在VC++开发环境中的Class View选项上的_DClock接口下,能够看到添加了两个属性:BackColor和ForeColor。

 

再次利用Build命令生成Clock控件。

 

1.7.2   自定义属性

 

当前Clock控件是每隔1秒更新一次时间的显示,接下,咱们给Clock控件增长一个自定义的属性:时间间隔,在用户设置了该属性的值之后,Clock控件就按照用户指定的时间间隔值来更新显示的时间。

 

这时一样须要利用ClassWizard来了Clock控件添加属性,而且也是选择ClassWizard对话框上的Automation选项卡,而后单击【Add 
Property】按钮,将弹出增长属性对话框。在该对话框中有几项内容,其中External 
name(外部名称)是在像VB这样的集成开发环境中所看到的控件属性名称,而Variable 
name(变量名称)是在VC++集成开发环境中开发这个控件时使用的该控件类的成员变量。也就是说,在开发程序中使用Variable 
name访问控件属性,而在外部使用该控件时,使用的是External 
name访问控件的属性。这里,咱们将新添加的时间间隔属性的外部名称设置为Inverval,类型选择为short类型,变量名称自动被设置为m_interval,ClassWizard为该控件自动增长了一个通知函数:OnIntervalChanged(以下图所示),当在外部修改该属性时,这个函数将被调用。

 

 

 

 

 

能够看到,在添加属性对话框上为咱们提供了三个单选按钮,但这时只有两个选项可供选择,默认选择的是Member variable,当选择该选项后,ClassWizard会为该属性生成一个成员变量,并生成一个通知函数,正如上图所示的那样;若是选择Get/Set 
methods选项,这时添加属性对话框就变成了下图所示的样子。能够看到,这时在添加属性对话框中就没有成员变量和通知函数这两个选项了,ClassWizard会为该属性自动生成两个函数:SetInterval和GetInterval。在程序中,若是想要设置Interval属性的值,能够调用SetInterval函数;若是想要获得该属性的值,能够调用GetInterval函数。但在控件内部,若是想要保存Interval这个属性的值,须要咱们自已定义一个成员变量来实现。刚才咱们已经看到,若是选择Member variable选项,ClassWizard会自动生成一个这样的成员变量,本例保持默认设置,即选择Member variable选项。

 

 

 

 

而后,单击添加属性对话框上的【OK】按钮完成Interval属性的添加,并单击ClassWizaard对话框上的【OK】按钮关闭ClassWizard对话框。这时,在VC开发环境中,在ClassView选项卡上,能够看到_DClock接口中又增长了一个属性:Interval,而且在CClockCtrl类中增长了一个函数:OnIntervalChanged。当Interval这一外部属性被修改时,就会调用这个OnIntervalChanged函数。该函数的默认实现代码以下所示:





void 
CClockCtrl::OnIntervalChanged() 

 


{


       // TODO: Add notification handler 
code


 


       SetModifiedFlag();


}


 

 

 

 

能够看到,此函数中调用了一个名为SetModifiedFlag函数,根据字面的意思,能够猜想到该函数是用来设置属性被修改的标记。

 

另外,能够发现,ClassWizard还为CClockCtrl类提供了一个成员变量:m_interval,其定义代码以下所示:





// Dispatch maps

 


       //{{AFX_DISPATCH(CClockCtrl)


       short m_interval;


       afx_msg void 
OnIntervalChanged();


       //}}AFX_DISPATCH


       DECLARE_DISPATCH_MAP()


 

 

能够看到,增长的m_interval和OnIntervalChanged函数的定义都位于CClockCtrl类的调度映射中。前面已经介绍过,调度映射主要是为了让外部应用程序能够方便地访问控件的属性和方法。

 

接下来,咱们就在OnIntervalChanged函数中根据用户输入的时间间隔值控制Clock控件的显示更新。具体代码以下所示:





void 
CClockCtrl::OnIntervalChanged() 

 


{


       // TODO: Add notification handler 
code


       if 
(m_interval<0||m_interval>6000)


       {


              m_interval=1000;


       }


       else


       {


              
m_interval=m_interval/1000*1000;


       }


       KillTimer(1);


       SetTimer(1,m_interval,NULL);


       SetModifiedFlag();


}


 

 

 

 

由于时间间隔不能为负数,也不能太大。因此在OnIntervalChanged函数中,首先对m_interval变量的值进行判断,若是用户设置的时间间隔属性值小于0,或者大于6000,则就将这个间隔值设置为1000。不然,进行调整,即对用户输入的值取整,获得一个整数的秒数。接下来,调用KillTimer函数销毁先前设置的定时器(其标识是1),时间间隔用Clock控件的m_interval属性值来设置。

 

利用Build命令生成最新的Clock控件,而后利用ActiveX Control Test 
Container容器测试该控件。在利用【Edit\Insert New 
Control…】命令插入该控件后,为了测试控件的属性,须要选中该控件,而后单击【Control\Invoke 
Methods…】菜单项,这时将显示以下的对话框:

 

 

 

 

在此对话框中有一个方法名称(Method 
Name)下拉列表,在此列表中列出了当前控件提供的方法,以下图所示:

 

 

 

 

若是想要获得某个属性值,应该选择PropGet类型的方法:若是想要设置某个属性的值,则应该选择PropPut类型的方法。这里咱们想要设置Clock控件的Interval属性的值,所以应该选择Interval(PropPut)项,并在随后出现的对话框的Parameter编辑框中输入数值:2000,单击【Set 
Value】按钮,这时就把Interval属性的值设置为2000了,以下图所示

 

 

 

 

可是,这时这个属性值仍未生效,须要单击【Invoke】按钮才行。以后就会发现Clock控件显示的时间每隔2秒跳动一次,说明设置生效了。

 

1.8 方法

 

下面,为Clock控件添加一个自定义的方法。一样,这也是利用ClassWizard来完成的。首先打开ClassWizard对话框,选择Automation选项卡,注意:在此属性页上,class name这一选项必定要选择CClockCtrl。而后,【Add 
Method】按钮,这时将出现以下对话框:

 

 

 

 

该对话框中提供了几个选项,其中外部名称(External 
name)是给外部程序使用控件的方法时使用的,这里,咱们能够将其设置为Hello。读者能够看到,系统自动为该方法提供了一个内部名称(Internal name):Hello,这个内部名称是在控件内部使用的方法名称,它能够与外部名称不同。而后将返回类型(Return type)设置为void,不用给这个方法设置参数。以下所示:

 

 

而后单击【OK】按钮关闭添加方法对话框,并单击ClassWizard对话框上的【OK】按钮关闭该对话框。这时,在ClassView选项中,能够看到在_DClock接口下增长了一个方法:Hello,该方法前面是用一个绿色的小方块表示的。同时,在CClockCtrl类中提供了该方法的实现,这时该方法的实现代码是空的。在此方法中,咱们可使用MessageBox函数显示一个消息框,其中显示字符串:“Hello 
world!”。具体代码以下:





void CClockCtrl::Hello() 

 


{


       // TODO: Add your dispatch handler code 
here


       MessageBox(“Hello world!”);


}


 

 

 

 

利用Build命令生成最新的Clock控件,再次利用ActiveX Control Test 
Container容器测试该控件。在该容器中调用控件方法的步骤是:选中Clock控件,选择【Control\Invoke 
Methods…】菜单项,这时将打开Invoke Methods对话框,在此对话框的Method 
Name下拉列表框中选择“Hello”方法,而后单击【Invoke】按钮,就会调用Clock控件的Hello方法,将出现以下图所示的消息框:

 

 

 

 

1.9 事件

 

ActiveX控件有两种事件:标准事件和自定义事件。

 

1.9.1   标准事件

 

在VC++中,若是想要为Clock控件添加一个事件,能够利用ClassWizard来完成。首先打开ClassWizard对话框,并打开它的ActiveX Events选项卡,在此选项卡上,确保Class name组合框中选择的是CClockCtrl。而后单击【Add 
Event】按钮,将显示添加事件对话框,在此对话框上有一个名称为External 
name的组合框,当单击其右边向下的箭头时,将会看到该列表框中列出了一些预先准备好的事件(以下图所示),即MFC提供的一些标准的事件,例如Click事件。这里,咱们先为Clock控件增长一个标准事件,也就是一个Stock事件。在External 
name下拉列表中选择Click,保持默认的Stock选项不变,而后单击【OK】按钮关闭Add 
Event对话框,并单击ClassWizard对话框上的【OK】按钮,关闭该对话框。

 

 

 

 

这时,在ClassView选项卡中能够看到,在_DClockEvents接口下面增长了一个方法:Click,该方法就是刚刚添加的Click事件。为何添加的事件增长到_DClockEvents接口中,而没有放到_DClock接口中呢?读者能够在Clock.odl文件的最后看到以下代码段:





//  Class information for 
CClockCtrl

 


 


       [ 
uuid(8377E215-598D-4F31-8BDE-0E16AFF83A9A),


         helpstring("Clock Control"), control 
]


       coclass Clock


       {


              [default] dispinterface 
_DClock;


              [default, source] dispinterface 
_DClockEvents;


       };


 

 

在上述所示的代码中,能够看到在说明_DClockEvents接口时,其前面有一个“source”标识,而_DClock接口前面并无此标识。“source”标识代表_DClockEvents接口是一个源接口。源接口表示控件将使用这个接口来发送通知事件,这个接口不是控件自己实现的接口。前面已经提过,做为利用接口进行通讯的双方,确定是一方调用接口所暴露出来的方法,另外一方实现该接口所提供的方法。咱们如今所实现的Clock控件正是调用_DClockEvents接口提供的方法,向容器发出事件通知。既然是控件使用_DClockEvents接口提供的方法,那么谁负责实现这个方法呢?实际上,_DClockEvents接口中的方法是由容器实现的。容器经过一种机制知道控件中定义了一个源接口,因而它就实现该接口。这里,读者可能会有这样的疑问,为何容器实现的接口由控件定义呢。一方面,对于每一个控件来讲,它能够有本身的事件接口,而容器是没法预先知道控件将使用哪个事件接口发出通知,所以咱们在编写控件的同时指定事件接口,并将其标识为源接口。另外一方面,接口由谁来定义是无所谓的,例如,主板与显卡进行通讯,那么是主板厂商去定义接口,仍是由显卡产商去定义接口,或者它们一块儿来定义接口,这都是同样的,关键是通讯的双方可以遵守一个接口进行通讯就能够了。

 

如今,咱们已经为Clock控件增长了一个标准事件:Click,再次利用ActiveX Control Test 
Container容器测试该控件。当插入Clock控件后,在此控件上单击鼠标左键,这时,在该容器下面的窗口中能够看到这样一句话:Clock Control:Click,即触发了Clock控件的Click事件,以下图所示:

 

 

 

 

咱们也能够用前面新建的VC++工程ClockTest来测试,打开【ClassWizard】,选择【Message 
Maps】选项卡,选中IDC_CLOCKCTRL1,咱们发现它对应一个Click消息,就是咱们刚才为Clock控件添加的Click事件。以下图所示:

 

 

 

 

点击【Add Function…】,为其添加一个消息处理,以下所示:

 

 

 

 

点击【OK】关闭对话框,再点击ClassWizard上的【Edit Code】按钮,添加消息响应代码:





void 
CClockTestDlg::OnClickClockctrl1() 

 


{


       // TODO: Add your control notification handler 
code here


       MessageBox(“Clock Clicked”);


}

 

 

 

编译运行该程序,咱们在Clock控件上单击鼠标左键,弹出以下消息框:

 

 

这是由于当在Clock控件上单击鼠标左键时,该控件接收到该单击消息,因而它就利用_DClockEvents接口中的方法(即Click方法)向容器(即ClockTest对话框)发出事件通知,由于_DClockEvents这个源接口是容器实现的,至关于控件调用了容器的Click方法,实际上就是调用了OnClickClockctrl1这个消息响应函数中的代码。

 

 

 

1.9.2   自定义事件

 

在VC++中,为了给ActiveX控件增长自定义事件,一样能够利用ClassWizard来完成,与上面添加标准事件的过程是同样的。另外,也能够在工程的ClassView选项卡上,用鼠标右键单击_DClockEvents接口,并从弹出的快捷菜单中选择【Add 
Event…】菜单项,从而也能够打开添加事件对话框。利用该对话框,咱们为Clock控件添加一个自定义的事件,新添加的这个事件的外部名称设置为:NewMinute,系统将自动将该事件的内部名称设置为:FireNewMinute,结果以下所示:

 

 

单击【OK】按钮对话框。这时,在ClassView选项卡中,能够看到_DClockEvents接口下又增长了一个方法:NewMinute,而且在CClockCtrl类中增长了一个FireNewMinute方法。这样,在控件内部,就能够调用FireNewMinute方法向容器发出事件通知,而在此方法内部,它会调用_DClockEvents接口中的NewMinute方法向容器发出事件通知。咱们发现,在ClockCtrl.h中,自动生成的FireNewMinute方法的代码以下:





// Event maps

 


       //{{AFX_EVENT(CClockCtrl)


       void FireNewMinute()


              
{FireEvent(eventidNewMinute,EVENT_PARAM(VTS_NONE));}


       //}}AFX_EVENT


       DECLARE_EVENT_MAP()


 

 

 

 

对于上面添加的Click事件来讲,由于它是MFC提供的一个标准事件,它的触发过程被底层屏蔽了,因此咱们没有看到。而对于自定义的事件来讲,必须在某个条件到来时,显式地调用某个函数发出该事件通知。本例中,咱们能够在新的一分钟到达时,发出NewMinute事件通知。所以,在CClockCtrl类的OnDraw函数中,在调用GetCurrentTime函数获得系统时间以后,添加下述代码:





if 
(0==time.GetSecond())       {

 


              FireNewMinute();


       }

 

 

 

也就是说,在获得当前系统时间以后,首先应对秒数进行判断,若是秒数为0,即到达了新的一分钟,就调用FireNewMinute方法,向容器发出NewMinute事件通知。而NewMinute事件是由容器实现的。

 

咱们经过前面新面的ClockTest程序来测试,打开【ClassWizard】,选择【Message 
Maps】选项卡,选中IDC_CLOCKCTRL1,咱们发现它对应一个NewMinute消息,就是咱们刚才为Clock控件添加的NewMinute事件。以下图所示:

 

 

 

 

单击【Add Function】,弹出以下对话框:

 

 

 

 

单击【OK】关闭对话框,并在ClassWizard上单击【EditCode】,为其添加消息响应代码:





void 
CClockTestDlg::OnNewMinuteClockctrl1() 

 


{


       // TODO: Add your control notification handler 
code here


       MessageBox(“New Minute”);


}


 

 

 

 

编译运行程序,咱们发现新的一分钟到来的时候会弹出以下消息框:

 

 

 

 

这是由于当新的一分钟到来时,Clock控件就会调用FireNewMinute方法,向容器(即ClockTest对话框)发出NewMinute事件,而容器接收到这一事件后,会调用OnNewMinuteClockctrl1来响应。

 

一样,也能够用ActiveX Control Test 
Container容器测试该控件。当插入该控件后,能够看到当该控件上显示的时间一旦到达新的一分钟时,该容器下面的窗口中就会显示这样一句话:Clock Control:NewMinute,即触发了一个NewMinute事件。以下图所示:

 

 

 

 

到此为止,咱们为Clock控件添加了一个标准事件:Click,和一个自定义事件:NewMinute。读者必定要注意,对标准事件来讲,其触发过程由MFC底层实现。但对自定义事件来讲,必需要在某个条件到来时,在代码中显式地调用某个函数发出该事件通知。

 

 

 

1.10    ActiveX控件与网页的交互

 

当咱们在互联网上畅游的时候,常常会碰到IE浏览器提示咱们下载安装某些插件,这些就是所谓的ActiveX插件,那么ActiveX控件是如何嵌入到到网页中的,以及如何与网页通讯的呢,这就是本节咱们要讲的内容。

 

咱们用记事本编辑一个html文件,代码以下:






 


<html>


    <head>


        <title>时钟控件测试</title>


        <meta http-equiv=”Content-Type” content=”text/html; 
charset=gb2312″>


        <meta name=”GENERATOR” content=”Microsoft FrontPage 
4.0″>


        <meta name=”ProgId” 
content=”FrontPage.Editor.Document”>


    </head>


  


    <script type=”text/javascript” 
language=”javascript”>


    <!–


        function On_PageLoad()


        {


        }


 


        function Hello_onclick()


        {


            ClockCtrl.Hello();


        }


      


    </script>


 


    <body onLoad=”return On_PageLoad()”>


  


        <SCRIPT LANGUAGE=”JScript” EVENT=”NewMinute()” 
FOR=”ClockCtrl”>


           alert(“new minute”);


        </SCRIPT>


        <OBJECT name=”Clock” id=”ClockCtrl” height=”80″ 
width=”180″ classid=”clsid:8377E215-598D-4F31-8BDE-0E16AFF83A9A”>


        </OBJECT>


        <hr/>


              <INPUT id=”hello” type=”button” value=”执行Hello” 
name=”ButtonStop” onClick=”return Hello_onclick()”/>


    </body>


</html>


 


 

 

 

 

下面解释一下这段代码:





<OBJECT name=”Clock” 
id=”ClockCtrl” height=”80″ width=”180″ 
classid=”clsid:8377E215-598D-4F31-8BDE-0E16AFF83A9A”>       
</OBJECT>

 


 

 

 

 

 

 

这段代码表示初始一个ActiveX控件对象,前面咱们已经讲过ActiveX控件其实是一个COM组件,他是须要在注册表中注册以后才能使用的,其中clsid:8377E215-598D-4F31-8BDE-0E16AFF83A9A表明在注册表中注册的classid,咱们能够打开注册表编辑器,搜索“Clock”,找到Clock在注册表的注册信息,以下图所示:

 

 

 

 

实际上,这个classid值也能够在咱们的Clock控件程序代码中找到,咱们打开Clock.odl文件,在其最下面就能够找到,以下所示:





//  Class information for 
CClockCtrl

 


 


       [ 
uuid(8377E215-598D-4F31-8BDE-0E16AFF83A9A),


         helpstring("Clock Control"), 
control ]


       coclass Clock


       {


              [default] dispinterface 
_DClock;


              [default, source] 
dispinterface _DClockEvents;


       };


 

 

 






 


<SCRIPT LANGUAGE=”JScript” EVENT=”NewMinute()” 
FOR=”ClockCtrl”>


alert(“new minute”);


</SCRIPT>

 

 

 

这段代码表示网页测试程序去订阅Clock时钟控件暴露出来的事件NewMinute,一旦新的一分钟到来的时候就会弹出以下对话框:

 

 

这样就完成了Clock控件与网页程序的通讯。

 

 





<INPUT 
id=”hello” type=”button” value=”执行Hello” name=”ButtonStop” onClick=”return 
Hello_onclick()”/>

 


 

 

 

 

这段代码表明单击标题为“执行Hello”的按钮,将执行Hello_onclick()函数:






 


function Hello_onclick()


{


    ClockCtrl.Hello();


}

 

 

 

这段代码执行时钟控件暴露出来的方法Hello(),弹出以下对话框:

 

 

 

 

 

这样就完成了网页程序与时钟控件的通讯。

 

总结:ActiveX控件与网页程序的通讯是经过暴露事件,网页程序去订阅该事件,一旦事件发生的条件知足,ActiveX控件就会通知给网页程序,从而实现了ActiveX控件与网页程序的通讯。网页程序与ActiveX控件的通讯是经过网页程序调用ActiveX控件暴露出来的方法。

 

 

from:http://www.cppcourse.com/activex.html

相关文章
相关标签/搜索