Здорова! Сегодня разберем такой макрос как DECLARE_EVENTSINK_MAP. В WebBrowser существуют события DWebBrowserEvents2, почитать за этот интерфейс можно тут. За DECLARE_EVENTSINK_MAP читаем тут.
Приступим к реализации отлова событий. Создаем приложение MFC на основе диалоговых окон, подключаем и WebBrowser, вообще директиву #import можно не использовать, WebBrowser походу уже подключен по умлочанию имеются интерфейсы, потому что когда мы подключаем с помощью #import у нас происходит конфликт имен и нужно обязательно определять пространство имен для #import. Нам нужно только подключать файл для работы с DOM файл mshtml.h . В общем добавили WebBrowser и у вас получилось такое окошко
Это я загрузил страницу microsoft.com по нажатию на кнопку «start», вот код обработчика кнопки старт
Ctest_webbrowser_eventsDlg::OnBnClickedButton1 »
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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 посылается после полной загрузки документа.
[tip]Эти сообщения их обработчики строятся двумя разными способами, в зависимости от того как мы подключаем браузер и что используем, если через #import то нужно функции вызывать по другом, они отличаются DISPID, его нужно самому смотреть в функции[/tip] Мы пока разберем способ без #import, просто браузер по умолчанию подключен. Нам для начала нужно подключить файлы
1 2 |
#include "exdisp.h"//находятся интерфейсы событий #include "exdispid.h"//находятся DISPID |
теперь добавляем в самый конец определения класса в котором создан наш диалог макрос DECLARE_EVENTSINK_MAP()
Дальше добавим определения двух обработчиков с теми же параметрами которые есть в функциях BiforeNavigate2 и DocumentComplete в том же классе где находится наш WebBrowser
1 2 3 4 |
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 где находятся определения функций класса
1 2 3 4 5 6 7 8 9 10 |
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"); } |
Дальше нужно составить карту сообщений событий
1 2 3 4 5 6 7 8 9 |
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 принимает первый параметр это класс в котором будут обрабатываться события, второй параметр я уже не помню это вроде идентификатор окошка браузера, когда мы создавали браузер мы его использовали
1 2 |
if (!m_wndBrowser.CreateControl(CLSID_WebBrowser, _T("Window"), WS_VISIBLE | WS_CHILD, rectClient, this, AFX_IDW_PANE_FIRST)) |
можете записать и там и там какое нибудь число мб. заработает, третий параметр это DISPID и мы его смотрим в файле который мы подключили exdispid.h, четвертый это название функции обработчика и пятым параметром идет перечисление всех переменных которые принимает функция, посмотреть их можно в файле afxdisp.h
Все компилируем и все у нас работает. При нажатии на кнопку start у нас запускается сразу несколько событий NavigateComplate2, и потом несколько после загрузки документа documentComplate. Увы они вызываться могут по нескольку раз. Чтобы они раз вызвались нужно добавить следующий код в них
Ctest_webbrowser_eventsDlg::OnBeforeNavigate2 »
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
void Ctest_webbrowser_eventsDlg::OnBeforeNavigate2 ( IDispatch * pDisp, VARIANT * URL, VARIANT * Flags, VARIANT * TargetFrameName, VARIANT * PostData, VARIANT * Headers, VARIANT_BOOL * Cancel ) { //AfxMessageBox(L"BeforeNavigate2"); TRACE("BeforeNavigate2\n"); 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 »
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
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=%S\n",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 downloading\n"); } lpWBDisp->Release(); } |
Ве теперь у наз раз вначале вызывается и раз в конце загрузки страницы.
Делаем через #import
Так если мы подключаем через #import, то у нас не будет файлов exdispid.h в котором есть все DISPID, что же делать в таком случае? А в таком случае мы просто заходим в папку Debug находим файл .tli, находим в нем определение функции обработчика события и оттуда берем DISPID. Ниже приведу пример кода где я так и делал
1 2 3 4 5 6 7 8 |
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() |
На этом все.