接收事件的途徑
依靠開發(fā)工具你創(chuàng)建客戶應(yīng)用程序,你可以接收事件通過不同的途徑. 顯然, 在Vb中接收事件同在VC中接收事件相比是如此不同和容易.在 C++ 應(yīng)用中,你可以用不同的技術(shù),通過使用 ATL, MFC, 或者標(biāo)準(zhǔn)C++.
Visual Basic 中接收事件
Visual Basic是創(chuàng)建大多數(shù)類型應(yīng)用的最輕松的工具, 所以我告訴你VB是處理事件最溶的工具時也不要驚奇. ATL 和 Visual Basic 示例我們同樣的工作,但是ATL花費(fèi)了我4個小時, 而 Visual Basic 例子僅僅只花20 分鐘.別說我錯了—我是ATL, 和 MFC, C++的忠實(shí)信徒, 尤其是你建立一個接口的時候.但是 Visual Basic當(dāng)建立客戶應(yīng)用程序從類似IE這樣的服務(wù)器接收事件時是偉大的工具.
OK,如何從Visual Basic 應(yīng)用程序中接收事件?當(dāng)宿主WebBrowser 控件,你不必做任何特別的事. Visual Basic 在form上為WebBrowser 控件接收事件.你所需要做的全部事情就是未你要接收的任何事件創(chuàng)建一個事件處理句柄.
你象創(chuàng)建其他事件句柄一樣創(chuàng)建句柄 (例如Form_Load event). 從Procedure下拉列表框中選擇你象控制的句柄, 在事件句柄中,加入任何你型在事件激發(fā)時執(zhí)行的任何代碼.
當(dāng)自動化服務(wù)器時候接收事件, 例如在VB應(yīng)用中的Internet Explorer,過程直截了當(dāng).首先設(shè)置對服務(wù)器的類型庫的引用, 你可以訪問Project/References 菜單.之后,采用WithEvents 關(guān)鍵字聲明服務(wù)器對象的變量.舉例, 如果你自動化Internet Explorer, 你將聲明變量如下:
Dim WithEvents InternetExplorer1 As InternetExplorer
下一步,采用new或者其他 關(guān)鍵字創(chuàng)建實(shí)例變量 ,如下:
Set InternetExplorer1 = CreateObject("InternetExplorer.Application.1")
或者:
Set InternetExplorer1 = New InternetExplorer
當(dāng)你采用以上途徑生成實(shí)例接收事件, Visual Basic 自動為你初始化和管理事件接收.你不必?fù)?dān)心連接點(diǎn)問題,VB為你處理它們.
在你輸入建立服務(wù)器的代碼之后,你插入符合服務(wù)器事件的方法調(diào)用. 舉例來說, 如果你想控制由IE激活的DownloadBegin event, 你應(yīng)當(dāng)聲明類似如下的方法聲明:
Private Sub InternetExplorer1_DownloadBegin()
' Insert your best Visual Basic code here.
End Sub
當(dāng)你不再想接收來自服務(wù)器的事件,簡單設(shè)置變量為Nothing:
Set InternetExplorer1 = Nothing
C++中接收事件
C++ 應(yīng)用程序中接收事件比Vb中多一些工作.但如果你在MFC對話框程序中宿主過WebBrowser控件, 你可以在classwizard中選擇你想控制的事件.使用C++的其他應(yīng)用程序宿主WebBrowser 或者自動化Internet Explorer 需要多一點(diǎn)的工作,但是仍然不需要更多的工作.在C++客戶接收事件,僅僅需要以下5個步驟:
1. 獲取連接點(diǎn)容器的指針 (IConnectionPointContainer).
2. 調(diào)用 IconnectionPointContainer 的方法 FindConnectionPoint 找出你想接收的事件。對 Internet Explorer 來講 , 你應(yīng)當(dāng)為 DWebBrowserEvents2 連接點(diǎn)接口實(shí)現(xiàn)事件 . ( 作為可選 , 你可以調(diào)用 EnumConnectionPoints 以枚舉服務(wù)器支持的全部連接點(diǎn) )
3. 實(shí)現(xiàn)接入你想接收事件的連接點(diǎn)的通報( Advise )。 當(dāng)實(shí)現(xiàn)通告時 , 傳遞一個事件接收槽的 Iunknown 接口的指針。 記住,事件接收槽必須實(shí)現(xiàn) IDispatch 接口以接收來自 WebBrowser 的事件。 Advise 方法將返回一個 cookie ,該 Cookie 在你調(diào)用 Unadvise 方法的時候攜帶上。
4. 實(shí)現(xiàn) IDispatch::Invoke 以控制任何激發(fā)的事件。 . ( 開發(fā)工具如 MFC 及 ATL 能夠容易為你做到 .)
5. 當(dāng)你不再接受事件,調(diào)用 Unadvise , 并且傳遞 cookie.
以上步驟如果采用VB和MFC \ATL等可能不很明顯,但是當(dāng)你采用標(biāo)準(zhǔn)C++創(chuàng)建應(yīng)用程序的時候就應(yīng)當(dāng)很明顯了.
以下 C++ 代碼允許你在自動化IE的時候接收事件. 留意注釋代碼實(shí)現(xiàn)了哪一個步驟. 假定當(dāng)你想連接事件時ConnectEvents 方法被調(diào)用,且當(dāng)應(yīng)用程序退出時候Exit 方法被調(diào)用. 同樣的,類 CSomeClass 繼承自IDispatch,且m_pIE 數(shù)據(jù)成員為通過CoCreateInstance 方法創(chuàng)建的IE的實(shí)例
void CSomeClass::ConnectEvents()


{
IConnectionPointContainer* pCPContainer;// Step 1: 獲取連接點(diǎn)的指針.
HRESULT hr = m_pIE->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPContainer);

if (SUCCEEDED(hr))

{
// m_pConnectionPoint is defined like this:
// IConnectionPoint* m_pConnectionPoint;
// Step 2: 選找連接點(diǎn).
//
hr = pCPContainer->FindConnectionPoint(DIID_DWebBrowserEvents2, &m_pConnectionPoint);
if (SUCCEEDED(hr))

{
// Step 3: 實(shí)現(xiàn)連接點(diǎn)地事件接收
//
hr = m_pConnectionPoint->Advise(this, &m_dwCookie);
if (FAILED(hr))

{
::MessageBox(NULL, "Failed to Advise", "C++ Event Sink", MB_OK);
}
}
pCPContainer->Release();
}
}

void CSomeClass::Exit()


{
// Step 5: Unadvise. 注意m_pConnectionPoint 應(yīng)當(dāng)在CSomeClass的析構(gòu)函數(shù)中釋放
//
if (m_pConnectionPoint)

{
HRESULT hr = m_pConnectionPoint->Unadvise(m_dwCookie);
if (FAILED(hr))

{
::MessageBox(NULL, "Failed to Unadvise", "C++ Event Sink", MB_OK);
}
}
}

注意此處少了step4:客戶端的 IDispatch::Invoke 方法實(shí)現(xiàn). 我將很快討論此點(diǎn). 每一次服務(wù)器激發(fā)事件會調(diào)用此. 當(dāng)事件被激發(fā),服務(wù)器傳遞事件的DISPID 到Invoke. 對于 Internet Explorer 5, 以下DISPIDs 定義于ExDispID.h 頭文件.
· DISPID_BEFORENAVIGATE2
- DISPID_COMMANDSTATECHANGE
- DISPID_DOCUMENTCOMPLETE
- DISPID_DOWNLOADBEGIN
- DISPID_DOWNLOADCOMPLETE
- DISPID_NAVIGATECOMPLETE2
- DISPID_NEWWINDOW2
- DISPID_ONFULLSCREEN
- DISPID_ONMENUBAR
- DISPID_ONQUIT
- DISPID_ONSTATUSBAR
- DISPID_ONTHEATERMODE
- DISPID_ONTOOLBAR
- DISPID_ONVISIBLE
- DISPID_PROGRESSCHANGE
- DISPID_PROPERTYCHANGE
- DISPID_STATUSTEXTCHANGE
- DISPID_TITLECHANGE
現(xiàn)在我們返回討論Invoke. 該方法有8個參數(shù), 但我們將僅僅討論其中的兩個: dispidMember 和pDispParams. (其余的參見MSDN中的IDispatch::Invoke.)
dispidMember 參數(shù)將告訴你哪一個事件被激發(fā).如果客戶應(yīng)用程序接收來自Internet Explorer的事件, dispidMember 參數(shù)的值應(yīng)當(dāng)是DISPIDs 列表中的某個.
pDispParams 輸入?yún)?shù)是指向容器結(jié)構(gòu)的指針, 存儲事件激發(fā)時的其他項(xiàng). 傳遞到事件句柄的參數(shù)存儲在pDispParams->rgvarg ,逆序存放. 舉例來說, Internet Explorer 激發(fā)NavigateComplete2 事件如下所示:
NavigateComplete2(pDisp, URL)
當(dāng) Invoke 被調(diào)用, pDispParams->cArgs 將包含兩個值, URL 參數(shù)在 pDispParams->rgvarg[0] 以及pDisp 參數(shù)存儲在 pDispParams->rgvarg[1]. 這些就是COM次序傳遞參數(shù)給Invoke 方法的方式.
以下為 NavigateComplete2 事件的處理.注意采用ATL的CComVariant 處理從 VARIANT到 BSTR包裝.
#include <strstrea.h>
STDMETHODIMP CSomeClass::Invoke(DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pvarResult,
EXCEPINFO* pExcepInfo,
UINT* puArgErr)


{
USES_CONVERSION;
strstream strEventInfo;
if (!pDispParams)
return E_INVALIDARG;

switch (dispidMember)

{
// The parameters for this DISPID:
// [0]: URL navigated to - VT_BYREF|VT_VARIANT
// [1]: An object that evaluates to the top-level or frame
// WebBrowser object corresponding to the event.
//
case DISPID_NAVIGATECOMPLETE2:
// Check the argument's type.
if (pDispParams->rgvarg[0].vt == (VT_BYREF|VT_VARIANT))

{
CComVariant varURL(*pDispParams->rgvarg[0].pvarVal);
varURL.ChangeType(VT_BSTR);
// strEventInfo is an object of type strstream.
//
strEventInfo << "NavigateComplete2: " << OLE2T(vtURL.bstrVal) << ends;
::MessageBox(NULL, strEventInfo.str(), "Invoke", MB_OK);
}
break;

default:
break;
}

return S_OK;
}

在ATL中接收事件
連同實(shí)現(xiàn)了缺省的COM 接口實(shí)現(xiàn), ATL提供了兩個函數(shù)—AtlAdvise 和 AtlUnadvise—使得任何課連接對象的事件接收簡單化.
AtlAdvise 函數(shù)告訴一個可連接對象客戶想從此可連接對象接收事件.該函數(shù)封裝實(shí)現(xiàn)接收事件的步驟1到3. AtlAdvise 理所當(dāng)然省了大量的時間.就像IConnectionPoint::Advise 方法, AtlAdvise 返回一個cookie供你稍后調(diào)用 AtlUnadvise. AtlUnadvise 告訴可連接對象客戶不再接收事件.
讓我們行說吧, 舉個例子, ATL應(yīng)用程序自動化Internet Explorer, 所以你想知道任何IE激發(fā)的事件. 為了告知Internet Explorer客戶想接收事件,發(fā)出對AtlAdvise的以下調(diào)用:
HRESULT hr = AtlAdvise(m_spInetExplorer, GetUnknown(),
DIID_DWebBrowserEvents2, &m_dwCookie);
|
四個參數(shù)傳遞給AtlAdvise. 第一個參數(shù)是指向可連接對象的IUnknown 接口的指針. m_spInetExplorer 數(shù)據(jù)成員是一個經(jīng)過我們自動化當(dāng)前運(yùn)行的Internet Explorer實(shí)例的指針. 因?yàn)?em>m_spInetExplorer 指向的對象直接或者間接繼承自IUnknown, 編譯器自動轉(zhuǎn)換m_spInetExplorer 為當(dāng)前運(yùn)行的 IE實(shí)例的IUnknown 接口指針.
AtlAdvise 第二個參數(shù)必須指向提供事件的對象的IUnknown 接口. GetUnknown 函數(shù)返回此接口.記住,提供事件的類必須通過某種途徑實(shí)現(xiàn) IDispatch in.在此例子中,該類繼承自 IDispatch.
第三個參數(shù)為你象接收的事件的IID, Internet Explorer 事件的可連接對象的IIS是 DIID_DWebBrowserEvents2.
最后一個參數(shù)指向DWORD的指針,該DWORD接收返回的Cookie. 該 cookie 將用于調(diào)用 AtlUnadvise.
客戶必須實(shí)現(xiàn) IDispatch::Invoke 以控制Internet Explorer 激發(fā)的事件. 當(dāng)你的應(yīng)用程序完成從IE接收事件, 只需要調(diào)用 call AtlUnadvise, 如下:
HRESULT hr = AtlUnadvise(m_spInetExplorer,
DIID_DWebBrowserEvents2,
m_dwCookie);
|
Figure 7-3. ATLIEEvtSpy.
以下展示如何自動化IE:
hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER,
IID_IWebBrowser2, (void**)&m_spInetExplorer);
if (SUCCEEDED(hr))
{
m_spInetExplorer->put_Visible(TRUE);
m_spInetExplorer->GoHome();
|
接下來, AtlAdvise 調(diào)用以接收事件, 如下:
hr = AtlAdvise(m_spInetExplorer, GetUnknown(),
DIID_DWebBrowserEvents2, &m_dwCookie);
|
CIEEvtObj 類繼承自IDispatch,所以 CIEEvtObj 類可以作為事件接收對象. Invoke 實(shí)現(xiàn)控制事件. 每當(dāng)Internet Explorer 激發(fā)一個事件, 在listBox中顯示一個消息.以西為invoke的代碼:
STDMETHODIMP CIEEvtObj::Invoke(DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pvarResult,
EXCEPINFO* pExcepInfo,
UINT* puArgErr)
{
_ASSERT(m_spInetExplorer);
USES_CONVERSION;
strstream strEventInfo;
if (!pDispParams)
return E_INVALIDARG;
switch (dispidMember)
{
//
// The parameters for this DISPID are as follows:
// [0]: Cancel flag - VT_BYREF|VT_BOOL
// [1]: HTTP headers - VT_BYREF|VT_VARIANT
// [2]: Address of HTTP POST data - VT_BYREF|VT_VARIANT
// [3]: Target frame name - VT_BYREF|VT_VARIANT
// [4]: Option flags - VT_BYREF|VT_VARIANT
// [5]: URL to navigate to - VT_BYREF|VT_VARIANT
// [6]: An object that evaluates to the top-level or frame
// WebBrowser object corresponding to the event
//
case DISPID_BEFORENAVIGATE2:
strEventInfo << "BeforeNavigate2: ";
if (pDispParams->cArgs >= 5
&& pDispParams->rgvarg[5].vt == (VT_BYREF|VT_VARIANT))
{
CComVariant vtURL(*pDispParams->rgvarg[5].pvarVal);
vtURL.ChangeType(VT_BSTR);
strEventInfo << OLE2T(vtURL.bstrVal);
}
else
strEventInfo << "NULL";
strEventInfo << ends;
break;
//
// The parameters for this DISPID:
// [0]: Enabled state - VT_BOOL
// [1]: Command identifier - VT_I4
//
case DISPID_COMMANDSTATECHANGE:
strEventInfo << "CommandStateChange: ";
if (pDispParams->cArgs == 0)
strEventInfo << "NULL";
else
{
if (pDispParams->cArgs > 1
&& pDispParams->rgvarg[1].vt == VT_I4)
{
strEventInfo << "Command = "
<< pDispParams->rgvarg[1].lVal;
}
if (pDispParams->rgvarg[0].vt == VT_BOOL)
{
strEventInfo << ", Enabled = "
<< ((pDispParams->rgvarg[0].boolVal == VARIANT_TRUE)
? "True" : "False");
}
}
strEventInfo << ends;
break;
case DISPID_DOCUMENTCOMPLETE:
strEventInfo << "DocumentComplete" << ends;
break;
case DISPID_DOWNLOADBEGIN:
strEventInfo << "DownloadBegin" << ends;
break;
case DISPID_DOWNLOADCOMPLETE:
strEventInfo << "DownloadComplete" << ends;
break;
//
// The parameters for this DISPID:
// [0]: URL navigated to - VT_BYREF|VT_VARIANT
// [1]: An object that evaluates to the top-level or frame
// WebBrowser object corresponding to the event
//
case DISPID_NAVIGATECOMPLETE2:
if (pDispParams->rgvarg[0].vt == (VT_BYREF|VT_VARIANT))
{
CComVariant vtURL(*pDispParams->rgvarg[0].pvarVal);
vtURL.ChangeType(VT_BSTR);
strEventInfo << "NavigateComplete2: "
<< OLE2T(vtURL.bstrVal)
<< ends;
}
break;
//
// The parameters for this DISPID:
// [0]: Maximum progress - VT_I4
// [1]: Amount of total progress - VT_I4
//
case DISPID_PROGRESSCHANGE:
strEventInfo << "ProgressChange: ";
if (pDispParams->cArgs == 0)
strEventInfo << "NULL";
else
{
if (pDispParams->cArgs > 1
&& pDispParams->rgvarg[1].vt == VT_I4)
{
strEventInfo << "Progress = "
<< pDispParams->rgvarg[1].lVal;
}
if (pDispParams->rgvarg[0].vt == VT_I4)
strEventInfo << ", ProgressMax = "
<< pDispParams->rgvarg[0].lVal;
}
strEventInfo << ends;
break;
//
// The parameter for this DISPID:
// [0]: Name of property that changed - VT_BSTR
//
case DISPID_PROPERTYCHANGE:
strEventInfo << "PropertyChange: ";
if (pDispParams->cArgs > 0
&& pDispParams->rgvarg[0].vt == VT_BSTR)
{
strEventInfo << OLE2T(pDispParams->rgvarg[0].bstrVal);
}
else
{
strEventInfo << "NULL";
}
strEventInfo << ends;
break;
//
// The parameters for this DISPID:
// [0]: New status bar text - VT_BSTR
//
case DISPID_STATUSTEXTCHANGE:
LPOLESTR lpStatusText;
m_spInetExplorer->get_StatusText(&lpStatusText);
strEventInfo << "StatusTextChange: ";
if (!strcmp(OLE2T(lpStatusText), ""))
strEventInfo << "NULL";
else
strEventInfo << OLE2T(lpStatusText);
strEventInfo << ends;
break;
case DISPID_NEWWINDOW2:
strEventInfo << "NewWindow2" << ends;
break;
//
// The parameter for this DISPID:
// [0]: Document title - VT_BSTR
//
case DISPID_TITLECHANGE:
strEventInfo << "TitleChange: ";
if (pDispParams->cArgs > 0
&& pDispParams->rgvarg[0].vt == VT_BSTR)
{
strEventInfo << OLE2T(pDispParams->rgvarg[0].bstrVal);
}
else
{
strEventInfo << "NULL";
}
strEventInfo << ends;
break;
// The user has told Internet Explorer to close.
//
case DISPID_ONQUIT:
return Stop();
default:
// Note: This class acts only as an event sink, so
// there's no reason to call the base class version of Invoke.
strEventInfo << "Unknown Event" << dispidMember << ends;
break;
}
AddEventToList(strEventInfo.str());
return S_OK;
}
|
請注意此使用了標(biāo)準(zhǔn)C++ 庫的 strstream 類來建立字符串.這么做是因?yàn)锳TL 不提供像Cstring的類. 每一次從IE接收到事件,建立一個包含事件的名稱和參數(shù)的字符串. 然后顯示在列表框中.
退出時候調(diào)用AtlUnadvise:
STDMETHODIMP CIEEvtObj::Stop()
{
if (m_spInetExplorer)
{
HRESULT hr = AtlUnadvise(m_spInetExplorer,
DIID_DWebBrowserEvents2,
m_dwCookie);
if (FAILED(hr))
ATLTRACE("Failed to Unadvise\n");
}
PostQuitMessage(0);
return S_OK;
}
|
在 MFC中接收事件
MFC提供了數(shù)個宏使得你可以接收從自動化的對象或者宿主的控件的事件。在兩種情況中, 接收事件的類必須直接或者間接繼承自CCmdTarget. CCmdTarget 實(shí)現(xiàn)接收事件的IDispatch 接口. 另外, 你必須在你的應(yīng)用中調(diào)用EnableAutomation 初始化包含在CCmdTarget 中的IDispatch.
在MFC中自動化一個COM 對象時接收事件
在mfc中接收事件很容易.全部要做的就是在代碼中調(diào)用AfxConnectionAdvise 函數(shù)以通告連接點(diǎn)客戶需要接收事件.當(dāng)客戶不許要接收事件,調(diào)用AfxConnectionUnadvise. AfxConnectionAdvise 和 AfxConnectionUnadvise 函數(shù)定義于afxctl.h 頭文件。
AfxConnectionAdvise 函數(shù)查詢連接點(diǎn)容器, 尋找可連接點(diǎn),并且通告連接點(diǎn). 函數(shù)的5個參數(shù)如下:
Table 7-3 Parameters of the AfxConnectionAdvise Function
Parameter
|
Description
|
pUnkSrc
|
指向激發(fā)事件的 com 對象的 IUnknown 接口的指針 . pUnkSrc 是由 CoCreateInstance . 建立的對象的指針
|
pUnkSink
|
指向事件接收的 IUnknown 接口
|
iid
|
連接點(diǎn)的 IID. 例如對 IE 來說,是 DIID_DWebBrowserEvents2 .
|
bRefCount
|
傳遞 TRUE 表示建立連接點(diǎn)將導(dǎo)致 pUnkSink 的引用將增加。 FALSE 表示不會增加 .
|
pdwCookie
|
表示此連接。由 AfxConnectionAdvise 將傳遞給 AfxConnectionUnadvise
|
處理事件也很容易。記住MFC事件接收類必須繼承自CCmdTarget. CCmdTarget 使用派遣映射檢測當(dāng)接收到事件時調(diào)用處理函數(shù).你必須首先在頭文件中聲明派遣映射 然后再實(shí)現(xiàn)文件中 (.cpp) 實(shí)現(xiàn). 幸運(yùn)地, MFC提供了宏來幫助聲明和處理派遣映射。.
為了定義派遣映射, 首先在聲明接收事件類的頭文件中簡單定義DECLARE_DISPATCH_MAP. 這些宏聲明派遣映射和CCmdTarget 訪問的函數(shù). 一旦你定義了派遣映射,你應(yīng)當(dāng)在實(shí)現(xiàn)文件中實(shí)現(xiàn)宏. 第一個宏放在BEGIN_DISPATCH_MAP 宏.它指定事件接收類的基礎(chǔ)類.舉例來說,如果事件類是CEventSink 繼承自 CCmdTarget, BEGIN_DISPATCH_MAP 將看起來如下:
BEGIN_DISPATCH_MAP(CEventSink, CCmdTarget)
|
接下來用DISP_FUNCTION_ID來聲明派遣ID。此宏的六個參數(shù):
Table 7-4 Parameters of the DISP_FUNCTION_ID Macro
Parameter
|
Description
|
theClass
|
事件類的名稱
|
szExternalName
|
函數(shù)的名字 .
|
dispid
|
事件的 DISPID
|
pfnMember
|
指向處理事件的成員函數(shù) .
|
vtRetval
|
成員函數(shù)的返回值類型,是 VARENUM 的每局類型,定義于 wtypes.h 頭文件
|
vtsParams
|
空格分隔的參數(shù)類型的列表 .
|
假設(shè)你想控制DownloadComplete 事件. 告訴CCmdTarget 你將控制處理DownloadComplete, 如下使用:
DISP_FUNCTION_ID(CIE5Events, "DownloadComplete",
DISPID_DOWNLOADCOMPLETE, OnDownloadComplete,
VT_EMPTY, VTS_NONE)
|
最終采用 END_DISPATCH_MAP宏關(guān)閉.完整如下:
BEGIN_DISPATCH_MAP(CEventSink, CCmdTarget)
DISP_FUNCTION_ID(CIE5Events, "DownloadComplete",
DISPID_DOWNLOADCOMPLETE, OnDownloadComplete,
VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()
|
在MFC中寄宿 ActiveX 控件時處理事件
這類似于處理COM對象的事件.主要區(qū)別在于你不需要通告或者解除通告連接點(diǎn). CCmdTarget 未你控制了他.
在寄宿一個Activex控件情形中, CCmdTarget 使用事件接收宏代理派遣宏.就像你猜想的一樣, MFC 提供初始化事件接收通告映射. 聲明此宏類似聲明派遣宏—派 DECLARE_EVENTSINK_MAP 宏存放在頭文件中.另外的聲明映射, DECLARE_EVENTSINK_MAP 聲明 CCmdTarget 訪問映射的類
接下來在類中實(shí)現(xiàn)事件接收.開始于EGIN_EVENTSINK_MAP 宏.指定事件接收類的 和它的基類。舉例,此處為實(shí)例:
BEGIN_EVENTSINK_MAP(CMyDlg, CDialog)
|
現(xiàn)在實(shí)用ON_EVENT*宏來處理是按接收.。大多數(shù)情形,你將使用帶有5個參數(shù)的ON_EVENT.攜帶的參數(shù)如下:
ON_EVENT(CMyDlg, IDC_WEBBROWSER, DISPID_DOWNLOADCOMPLETE,
OnDownloadComplete, VTS_NONE)
|
如果你象多個成員函數(shù)處理此事件, 使用ON _EVENT_RANGE宏.
Table 7-5 Parameters of the ON_EVENT Macro
Parameter
|
Description
|
theClass
|
在那個類中接收事件
|
id
|
控件的資源 ID 號
|
dispid
|
有控件激活的事件的 ID.
|
pfnHandler
|
事件的成員函數(shù),用來處理事件句柄 . 此函數(shù)應(yīng)當(dāng)有 BOOL 來型的返回值以及匹配事件的參數(shù)。當(dāng)事件函數(shù)被處理則返回 TRUE
|
vtsParams
|
VTS_ constants 的類型
|
你引剛才用 END_EVENTSINK_MAP 宏.完整的定義如下:
BEGIN_EVENTSINK_MAP(CMyDlg, CDialog)
ON_EVENT(CMyDlg, IDC_WEBBROWSER, DISPID_DOWNLOADCOMPLETE,
OnDownloadComplete, VTS_NONE)
END_EVENTSINK_MAP()
|
對于 DocumentComplete 事件,你應(yīng)當(dāng)如下聲明:
// Declare the event sink map. This declaration goes
// in the class declaration of CMFCIEEvtSpyDlg in the
// MFCIEEvtSpyDlg.h header file.
//
DECLARE_EVENTSINK_MAP()
// Initialize the event sink map. These macros
// go in the implementation file _ MFCIEEvtSpyDlg.cpp.
//
BEGIN_EVENTSINK_MAP(CMFCIEEvtSpyDlg, CDialog)
ON_EVENT(CMFCIEEvtSpyDlg, IDC_WEBBROWSER, DISPID_DOCUMENTCOMPLETE,
OnDocumentComplete, VTS_DISPATCH VTS_PVARIANT)
END_EVENTSINK_MAP()
|
Figure 7-4. MFCIEEvtSpy.
當(dāng)WebBrowser 控件基于對話框應(yīng)用,你通常不需要插入默認(rèn)的宏, 因?yàn)?ClassWizard 可為你做這一切.而在SDI或者M(jìn)DI工程中,需要加上此宏。
現(xiàn)在事件接收映射已經(jīng)聲明, 每當(dāng)WebBrowser 控件激發(fā)了DocumentComplete 事件, OnDocumentComplete 方法將被調(diào)用.在CMFCIEEvtSpyDlg的 OnDocumentComplete 方法中, 包含URL和事件名稱的字符串被創(chuàng)建。之后字符串加入到列表框中展示W(wǎng)ebBrowser 控件的事件.
以下代碼解釋如何接收處理DocumentComplete 事件.:
void CMFCIEEvtSpyDlg::OnDocumentComplete(LPDISPATCH pDisp, VARIANT* URL)
{
USES_CONVERSION;
CString strEvt("DocumentComplete: ");
strEvt += OLE2T(URL->bstrVal);
AddEventToList(WBListBox, strEvt);
}
|
當(dāng)啟動后,采用CoCreateInstance 創(chuàng)建的ie實(shí)例傳遞LSID_InternetExplorer接口..
以下為代碼:
void CMFCIEEvtSpyDlg::OnStartIE()
{
if (m_pInetExplorer == NULL) // Can start only one instance
{
// Create an instance of Internet Explorer.
//
HRESULT hr = CoCreateInstance(CLSID_InternetExplorer,
NULL,
CLSCTX_LOCAL_SERVER,
IID_IWebBrowser2,
(void**)&m_pInetExplorer);
if (SUCCEEDED(hr))
{
// Set up the event sink.
//
BOOL bAdvised = AfxConnectionAdvise(m_pInetExplorer,
DIID_DWebBrowserEvents2,
m_pIE5Events->GetInterface(&IID_IUnknown),
TRUE, &m_dwCookie);
// Disable the Start IE5 button so that the
// user knows that only one instance of
// Internet Explorer can be started at a time.
//
m_btnStartIE.EnableWindow(FALSE);
// Make Internet Explorer visible and go home.
//
m_pInetExplorer->put_Visible(VARIANT_TRUE);
m_pInetExplorer->GoHome();
}
}
}
|
為接收Internet Explorer 的事件,你應(yīng)當(dāng)聲明派遣接口且在實(shí)現(xiàn)文中:
// Declare the dispatch map. This
// declaration is placed in the class declaration
// for the CIE5Events class, which is in the
// CIE5Events.h header file.
//
DECLARE_DISPATCH_MAP()
// Initialize the dispatch map in the
// implementation file for CIE5Events _ CIE5Events.cpp.
//
BEGIN_DISPATCH_MAP(CIE5Events, CCmdTarget)
DISP_FUNCTION_ID(CIE5Events, "DocumentComplete",
DISPID_DOCUMENTCOMPLETE, OnDocumentComplete,
VT_EMPTY, VTS_DISPATCH VTS_PVARIANT)
END_DISPATCH_MAP()
|
現(xiàn)在無論如何接收到的自動化 Internet Explorer 的事件 DocumentComplete, OnDocumentComplete方法將被調(diào)用. OnDocumentComplete 方法創(chuàng)建一個包含事件名稱和URL的字符串,且加入到列表框通告Internet Explorer事件發(fā)生.同樣期它事件發(fā)生也會如此處理.此處為CIE5Events 類的OnDocumentComplete 方法代碼:
void CIE5Events::OnDocumentComplete(LPDISPATCH pDisp, VARIANT* URL)
{
USES_CONVERSION;
CString strEvt("DocumentComplete: ");
strEvt += OLE2T(URL->bstrVal);
m_pParent->AddEventToList(CMFCIEEvtSpyDlg::IE5ListBox, strEvt);
}
|