完成本章學習後,您將可以:javascript
重點:ActiveX控件的實現、測試及註冊。html
本章將介紹ActiveX控件的應用與工做原理。咱們能夠把ActiveX控件看作是一個極小的服務器應用程序,它不能獨立運行,必須嵌入到某個容器程序中,與該容器一塊兒運行。那麼,該容器就至關於客戶程序,它使用ActiveX提供的服務。java
注:本文改編自孫鑫教程,在此基礎上加入了Active控制與網頁的交互。編程
Activex控件是微軟提供的功能強大的程序設計和開發技術。ActiveX是基於OLE和COM的一門開發技術,它既是一個自動化對象,也是一個COM對象。根據微軟權威的軟件開發指南MSDN(Microsoft Developer
Network)的定義,ActiveX控件之前也叫作OLE控件或OCX控件,它是一些軟件組件或對象,能夠將其插入到WEB網頁或其它應用程序中。在形態上ActiveX控件是後綴名爲ocx的控件,可是,讀者應該注意的是,Activex控件對應的文件也能夠是其餘後綴名,如.dll。瀏覽器
一個典型的ActiveX控件,它具備方法、屬性、事件這三種特性。服務器
在實際編程中,咱們能夠將經常使用的功能封裝在一個ActiveX控件中,而後將該控件提供給VB或Delphi的開發人員使用。例如,咱們開發了一箇中國地圖控件,正好有一個公司有許多分支機構,如麥當勞,它會不斷地在全國各地增長它的加盟店,而麥當勞公司總部須要實時地觀測它每個月新增的這些加盟店的地理位置。因而麥當勞公司就能夠直接購買咱們開發的這個地圖控件,在地圖上顯示它們各分支機構的位置,而不須要再自行開發這種控件了。如今不少公司都在作Activex控件的開發,將一些經常使用功能封裝到一個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 // DO NOT EDIT what you see in these //}}AFX_MSG DECLARE_MESSAGE_MAP()
// //{{AFX_DISPATCH(CClockCtrl) // NOTE – ClassWizard will add and remove // DO NOT EDIT what you see in these //}}AFX_DISPATCH DECLARE_DISPATCH_MAP()
afx_msg void AboutBox();
// //{{AFX_EVENT(CClockCtrl) // NOTE – ClassWizard will add and remove // DO NOT EDIT what you see in these //}}AFX_EVENT |
咱們能夠看到,在該文件中不只提供了一個消息映射,它還提供了一個調度映身和事件映射。其中調度映射是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控件文件。在使用時,只須要將這個文件傳遞給使用方,通過註冊後就可使用該控件了。
咱們在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控件。以下圖:
全部的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控件了。
下面繼續完成Clock控件的實現,讓該控件顯示系統當前時間,這能夠在CClockCtrl類的OnDraw函數中完成。這時,該函數中已經自動生成了兩行代碼,分別用來填充控件的背景和繪製橢圓,咱們先將這兩代碼註釋起來,而後添加以下代碼:
void CClockCtrl::OnDraw(
CDC* pdc, const CRect& { // TODO: Replace the following code with your //pdc->FillRect(rcBounds, //pdc->Ellipse(rcBounds); CTime CString 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) == return -1;
// TODO: Add your specialized creation code
SetTimer(1,1000,NULL); return 0; } |
接下來,再爲CClockCtrl類增長Windows消息:WM_TIMER的處理,在其響應函數OnTimer中調用Invalidate函數,使窗口無效,這樣就可使窗口重繪。具體實現代碼以下所示:
void CClockCtrl::OnTimer(UINT nIDEvent) {
// TODO: Add your message handler code here Invalidate(); } |
Build並運行Clock控件,將會看到這時這個時鐘控件顯示的時間隨系統當前時間變化而變化了。
讀者能夠發現,在VB中提供了一個以下圖所示的屬性面板,經過此面板,能夠修改控件屬性的值。該面板的左邊列出了控件的一些屬性,對Clock控件來講,當前咱們沒有爲它添加任一屬性,都是MFC ActiveX
ControlWizard自動生成的屬性;面板的右邊就是屬性對應的值,例如,控件的Name(名稱)屬性是Clock1。若是咱們想要改變Clock控件的前景色和背景色,卻發現Clock控件的前景色和背景色,卻發如今Clock控件的屬性面板中沒有看到前景色和背景色這兩種屬性。可是若是在VB程序的窗體上放置一個列表框控件,而後在屬性面板上就能夠看到該控件有BackColor(背景色)和ForeColor(前景色)屬性,能夠用來設置該控件的背景色和前景色。若是但願爲Clock控件也提供這樣的屬性,讓用戶能夠設置該控件的前景色和背景色,那麼就須要在VC++開發環境中繼續完善Clock控件,爲它添加這樣的屬性。
在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控件。
當前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
SetModifiedFlag(); }
|
能夠看到,此函數中調用了一個名爲SetModifiedFlag函數,根據字面的意思,能夠猜想到該函數是用來設置屬性被修改的標記。
另外,能夠發現,ClassWizard還爲CClockCtrl類提供了一個成員變量:m_interval,其定義代碼以下所示:
// Dispatch maps
//{{AFX_DISPATCH(CClockCtrl) short m_interval; afx_msg void //}}AFX_DISPATCH DECLARE_DISPATCH_MAP()
|
能夠看到,增長的m_interval和OnIntervalChanged函數的定義都位於CClockCtrl類的調度映射中。前面已經介紹過,調度映射主要是爲了讓外部應用程序能夠方便地訪問控件的屬性和方法。
接下來,咱們就在OnIntervalChanged函數中根據用戶輸入的時間間隔值控制Clock控件的顯示更新。具體代碼以下所示:
void CClockCtrl::OnIntervalChanged()
{ // TODO: Add notification handler if { m_interval=1000; } else { } 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秒跳動一次,說明設置生效了。
下面,爲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 MessageBox(「Hello world!」); }
|
利用Build命令生成最新的Clock控件,再次利用ActiveX Control Test
Container容器測試該控件。在該容器中調用控件方法的步驟是:選中Clock控件,選擇【Control\Invoke
Methods…】菜單項,這時將打開Invoke Methods對話框,在此對話框的Method
Name下拉列表框中選擇「Hello」方法,而後單擊【Invoke】按鈕,就會調用Clock控件的Hello方法,將出現以下圖所示的消息框:
ActiveX控件有兩種事件:標準事件和自定義事件。
在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
[ helpstring("Clock Control"), control coclass Clock { [default] dispinterface [default, source] dispinterface };
|
在上述所示的代碼中,能夠看到在說明_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 MessageBox(「Clock Clicked」); } |
編譯運行該程序,咱們在Clock控件上單擊鼠標左鍵,彈出以下消息框:
這是由於當在Clock控件上單擊鼠標左鍵時,該控件接收到該單擊消息,因而它就利用_DClockEvents接口中的方法(即Click方法)向容器(即ClockTest對話框)發出事件通知,由於_DClockEvents這個源接口是容器實現的,至關於控件調用了容器的Click方法,實際上就是調用了OnClickClockctrl1這個消息響應函數中的代碼。
在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() //}}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 MessageBox(「New Minute」); }
|
編譯運行程序,咱們發現新的一分鐘到來的時候會彈出以下消息框:
這是由於當新的一分鐘到來時,Clock控件就會調用FireNewMinute方法,向容器(即ClockTest對話框)發出NewMinute事件,而容器接收到這一事件後,會調用OnNewMinuteClockctrl1來響應。
一樣,也能夠用ActiveX Control Test
Container容器測試該控件。當插入該控件後,能夠看到當該控件上顯示的時間一旦到達新的一分鐘時,該容器下面的窗口中就會顯示這樣一句話:Clock Control:NewMinute,即觸發了一個NewMinute事件。以下圖所示:
到此爲止,咱們爲Clock控件添加了一個標準事件:Click,和一個自定義事件:NewMinute。讀者必定要注意,對標準事件來講,其觸發過程由MFC底層實現。但對自定義事件來講,必需要在某個條件到來時,在代碼中顯式地調用某個函數發出該事件通知。
當咱們在互聯網上暢遊的時候,常常會碰到IE瀏覽器提示咱們下載安裝某些插件,這些就是所謂的ActiveX插件,那麼ActiveX控件是如何嵌入到到網頁中的,以及如何與網頁通訊的呢,這就是本節咱們要講的內容。
咱們用記事本編輯一個html文件,代碼以下:
<html> <head> <title>時鐘控件測試</title> <meta http-equiv=」Content-Type」 content=」text/html; <meta name=」GENERATOR」 content=」Microsoft FrontPage <meta name=」ProgId」 </head>
<script type=」text/javascript」 <!– function On_PageLoad() { }
function Hello_onclick() { ClockCtrl.Hello(); }
</script>
<body onLoad=」return On_PageLoad()」>
<SCRIPT LANGUAGE=」JScript」 EVENT=」NewMinute()」 alert(「new minute」); </SCRIPT> <OBJECT name=」Clock」 id=」ClockCtrl」 height=」80″ </OBJECT> <hr/> <INPUT id=」hello」 type=」button」 value=」執行Hello」 </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
[ helpstring("Clock Control"), coclass Clock { [default] dispinterface [default, source] };
|
<SCRIPT LANGUAGE=」JScript」 EVENT=」NewMinute()」 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控件暴露出來的方法。