MFC DECLARE_EVENTSINK_MAP отлов событий в WebBrowser

Рубрика: MFC, Дата: 19 August, 2015, Автор:

Здорова! Сегодня разберем такой макрос как DECLARE_EVENTSINK_MAP. В WebBrowser существуют события DWebBrowserEvents2, почитать за этот интерфейс можно тут.  За DECLARE_EVENTSINK_MAP читаем тут.

Приступим к реализации отлова событий. Создаем приложение MFC на основе диалоговых окон, подключаем и WebBrowser, вообще директиву #import можно не использовать, WebBrowser походу уже подключен по умлочанию имеются интерфейсы, потому что когда мы подключаем с помощью #import у нас происходит конфликт имен и нужно обязательно определять пространство имен для #import. Нам нужно только подключать файл для работы с DOM файл mshtml.h . В общем добавили WebBrowser и у вас получилось такое окошко

WebBrowserЭто я загрузил страницу microsoft.com по нажатию на кнопку “start”, вот код обработчика кнопки старт

Ctest_webbrowser_eventsDlg::OnBnClickedButton1 »

void Ctest_webbrowser_eventsDlg::OnBnClickedButton1()
{
	// TODO: добавьте свой код обработчика уведомлений
	CRect rectClient(10,10,500,400);//размер окошка браузера
	if (!m_wndBrowser.CreateControl(CLSID_WebBrowser, _T("Window"),
	WS_VISIBLE | WS_CHILD, rectClient, this, AFX_IDW_PANE_FIRST))
	{
		DestroyWindow();
	}
	if (m_pWeb = m_wndBrowser.GetControlUnknown())
	{
		_bstr_t bstrURL = _T("http://www.microsoft.com");
		m_pWeb->Navigate(bstrURL, NULL, NULL, NULL, NULL);
	}
}

И так я хочу обрабатывать два события, первое событие это BeforeNavigate2 посылается перед загрузкой документа (вызовом Navigate) и второй событие это DocumentComplete посылается после полной загрузки документа.

Эти сообщения их обработчики строятся двумя разными способами, в зависимости от того как мы подключаем браузер и что используем, если через #import то нужно функции вызывать по другом, они отличаются DISPID, его нужно самому смотреть в функции
Мы пока разберем способ без #import, просто браузер по умолчанию подключен. Нам для начала нужно подключить файлы
#include "exdisp.h"//находятся интерфейсы событий
#include "exdispid.h"//находятся DISPID

теперь добавляем в самый конец определения класса в котором создан наш диалог макрос DECLARE_EVENTSINK_MAP()

Дальше добавим определения двух обработчиков с теми же параметрами которые есть в функциях BiforeNavigate2 и DocumentComplete в том же классе где находится наш WebBrowser

afx_msg void OnBeforeNavigate2 ( IDispatch * pDisp, VARIANT * URL, VARIANT * Flags, 
			VARIANT * TargetFrameName, VARIANT * PostData, VARIANT * Headers, VARIANT_BOOL * Cancel );
afx_msg void OnDocumentComplete(LPDISPATCH lpDisp,
                                    VARIANT FAR* URL);

И добавим их определение в файл cpp где находятся определения функций класса

void Ctest_webbrowser_eventsDlg::OnBeforeNavigate2 ( IDispatch * pDisp, VARIANT * URL, VARIANT * Flags, 
			VARIANT * TargetFrameName, VARIANT * PostData, VARIANT * Headers, VARIANT_BOOL * Cancel )
{
	AfxMessageBox(L"BeforeNavigate2");
}
void Ctest_webbrowser_eventsDlg::OnDocumentComplete(LPDISPATCH lpDisp,
                                    VARIANT FAR* URL)
{
	AfxMessageBox(L"DocumentComplete");
}

Дальше нужно составить карту сообщений событий

BEGIN_EVENTSINK_MAP(Ctest_webbrowser_eventsDlg, CDialogEx)

ON_EVENT(Ctest_webbrowser_eventsDlg, AFX_IDW_PANE_FIRST, DISPID_BEFORENAVIGATE2 , 
OnBeforeNavigate2, VTS_DISPATCH VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PBOOL)
//DWebBrowserEvents2::DocumentComplete ( IDispatch * pDisp, VARIANT * URL )
ON_EVENT(Ctest_webbrowser_eventsDlg, AFX_IDW_PANE_FIRST, DISPID_DOCUMENTCOMPLETE /* DocumentComplete */,
            OnDocumentComplete, VTS_DISPATCH VTS_PVARIANT)

END_EVENTSINK_MAP()

ON_EVENT принимает первый параметр это класс в котором будут обрабатываться события, второй параметр я уже не помню это вроде идентификатор окошка браузера, когда мы создавали браузер мы его использовали

if (!m_wndBrowser.CreateControl(CLSID_WebBrowser, _T("Window"),
	WS_VISIBLE | WS_CHILD, rectClient, this, AFX_IDW_PANE_FIRST))

можете записать и там и там какое нибудь число мб. заработает,  третий параметр это DISPID и мы его смотрим в файле который мы подключили exdispid.h, четвертый это название функции обработчика и пятым параметром идет перечисление всех переменных которые принимает функция, посмотреть их можно в файле afxdisp.h

afxdisp.hВсе компилируем и все у нас работает. При нажатии на кнопку start у нас запускается сразу несколько событий NavigateComplate2, и потом несколько после загрузки документа documentComplate. Увы они вызываться могут по нескольку раз. Чтобы они раз вызвались нужно добавить следующий код в них

Ctest_webbrowser_eventsDlg::OnBeforeNavigate2 »

void Ctest_webbrowser_eventsDlg::OnBeforeNavigate2 ( IDispatch * pDisp, VARIANT * URL, VARIANT * Flags, 
			VARIANT * TargetFrameName, VARIANT * PostData, VARIANT * Headers, VARIANT_BOOL * Cancel )
{
	//AfxMessageBox(L"BeforeNavigate2");
	TRACE("BeforeNavigate2n");
		
	IUnknown*  pUnk;
	LPDISPATCH lpWBDisp;
	HRESULT    hr;

	pUnk = m_wndBrowser.GetControlUnknown();
	ASSERT(pUnk);

	hr = pUnk->QueryInterface(IID_IDispatch, (void**)&lpWBDisp);
	ASSERT(SUCCEEDED(hr));

	if (pDisp == lpWBDisp )
	{
		// Top-level Window object, so document has been loaded
		   
		TRACE("Web document is [Navigate]n");
		AfxMessageBox(L"Web document is [Navigate]");
	}

	lpWBDisp->Release();
}

Ctest_webbrowser_eventsDlg::OnDocumentComplete »

void Ctest_webbrowser_eventsDlg::OnDocumentComplete(LPDISPATCH lpDisp,
                                    VARIANT FAR* URL)
{
//	AfxMessageBox(L"DocumentComplete");
	COleVariant v;
	v.Attach(*URL);
	BSTR s=v.bstrVal;
	TRACE("DocumentComplate s=%Sn",s);
		
	IUnknown*  pUnk;
	LPDISPATCH lpWBDisp;
	HRESULT    hr;

	pUnk = m_wndBrowser.GetControlUnknown();
	ASSERT(pUnk);

	hr = pUnk->QueryInterface(IID_IDispatch, (void**)&lpWBDisp);
	ASSERT(SUCCEEDED(hr));

	if (lpDisp == lpWBDisp )
	{
		// Top-level Window object, so document has been loaded
		AfxMessageBox(L"Web document is finished downloading");
		TRACE("Web document is finished downloadingn");
	}
	lpWBDisp->Release();
}

Ве теперь у наз раз вначале вызывается и раз в конце загрузки страницы.

 

Делаем через #import

Так если мы подключаем через #import, то у нас не будет файлов exdispid.h в котором есть все DISPID, что же делать в таком случае? А в таком случае мы просто заходим в папку Debug находим файл .tli, находим в нем определение функции обработчика события и оттуда берем DISPID. Ниже приведу пример кода где я так и делал

BEGIN_EVENTSINK_MAP(Ctest_brawser11Dlg, CDialogEx)
	ON_EVENT(Ctest_brawser11Dlg, AFX_IDW_PANE_FIRST, 0xfa , 
OnBeforeNavigate2, VTS_DISPATCH VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PBOOL)
//DWebBrowserEvents2::DocumentComplete ( IDispatch * pDisp, VARIANT * URL )
ON_EVENT(Ctest_brawser11Dlg, AFX_IDW_PANE_FIRST, 0x103 /* DocumentComplete */,
            OnDocumentComplete, VTS_DISPATCH VTS_PVARIANT)

END_EVENTSINK_MAP()

На этом все.

 

 

rss