В данном направлении я абсолютный ноль. Необходимо написать плагин для IE, который бы фильтровал загружаемые страницы по ключевым словам ( <META name="keywords" ). С чего начать? Структуру и иерархию COM объектов представляю очень приблизительно, а времени в обрез. Ткните носом плз, что читать, где найти примеры и т.д!!!
Если мне не изменяет память, по такая технология называеться BHO(Browser Helper Object) На сайте msdn, есть описание этой технологии хелперов, с примерами и всеми поддерживаемыми командами.
Да-да!! это оно. Но я не могу для себя уяснить с какой стороны к этому подойти. Какой-нибудь минимальный пример можна? Сейчас вообще каша в голове. Пересмотрел кучу статей, все представляется очень приблизительно
Можно BHO использовать как подгрузку плагина в экземпляры IE, необходимый минимум - реализовать интерфейс IObjectWithSite. В IObjectWithSite::SetSite, которая вызывается при загрузке плагина, сохраняем указатель на интерфейс браузера и подписываемся на его события (DWebBrowserEvents2). Полный доступ к документу можно получить после DWebBrowserEvents2:ocumentComplete, получаешь интерфейс IWebBrowser2 из передаваемого IDispatch, из свойства IWebBrowser2:ocument берёшь IHTMLDocument и дальше уже работаешь с коллекциями html-элементов.
Пробую переделать пример. Он реализует кнопку на панели инструментов. Почему DllGetClassObject вызывается только после нажатия на нее, а не при загрузке плагина? В оригинале кроме IObjectWithSite класс наследовал еще и IOleCommandTarget. Я так понимаю, что второй интерфейс служит именно для работы с кнопкой?
Взял другой пример на переработку, немного продвинулся. Теперь уже проблема с SetSite Код (Text): STDMETHODIMP IMyIEExtention::SetSite(IUnknown *pUnkSite) { if (pUnkSite) { IServiceProviderPtr pServProv(pUnkSite); HRESULT hRes; hRes = pServProv->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, (void**)&m_pWebBrowser2); //А вот здесь нужно подписаться на события? Как? } return S_OK; } Пробую подписаться через AtlAdvise. Получаю 0х80040202. Кажеться, проблема в том, какие интерфейсы наследует мой класс Код (Text): class IMyIEExtention : public IObjectWithSite, public IContextMenu, public IDeskBand, public IDispatch, public IConnectionPointContainer Судя по тому, что прочитал по этой ошибке необходимо следующее: Код (Text): BEGIN_COM_MAP(IMyIEExtention) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY_IID( DIID_DWebBrowserEvents2, IDispatch) END_COM_MAP() Но не компилиться, синтаксическая ошибка DWebBrowserEvents2 Помогите! А то хана мне..
Лучше б теорию почитал, чем копать чужой код. На рсдн всё расписано. IServiceProvider не обязателен, можно сразу pUnkSite->QueryInterface(IID_IWebBrowser2); Подписываться - через IConnectionPointContainer и IConnectionPoint. Ну, или через AtlAdvise, если ATL используешь. Вообще, подписка реализуется через IDispatch. Её можно оформлять двумя способами: через реализацию соответствующего интерфейса (DWebBrowserEvents2) или просто через IDispatch. В первом случае будет "статическое" связывание и будут вызываться соответствующие функции, во втором - у тебя будет только IDispatch::Invoke и вызовы функций будешь определять по DISPID. Плюсы первого - у тебя функции как в оригинальном интерфейсе с нужными параметрами, но нужно будет реализовывать полноценный СОМ-объект. Во втором случае простота реализации, но поимеешь небольшой гемор с распаковкой аргументов из IDispatch::Invoke.
Ну а все же, откуда ошибка? Вот, гляньте кто-нибудь Внимание обратить только на SetSite и обьявление класса IMyIEExtention, все остальное "родное" осталось
Ну вот пример первого способа: Код (Text): // используется в DISPID_FILEDOWNLOAD static _ATL_FUNC_INFO FileDownloadInfo = { CC_STDCALL, VT_EMPTY, 2, { VT_BOOL,VT_BOOL } }; class ATL_NO_VTABLE CWebBrowserEvents2Sink : public CComObjectRootEx<CComMultiThreadModel>, public CComCoClass<CWebBrowserEvents2Sink>, public IDispEventImpl<0,CWebBrowserEvents2Sink,&DIID_DWebBrowserEvents2,&LIBID_SHDocVw,1,0> { public: /** @name Защита от преждевременного разрушения */ //@{ DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() {} void FinalRelease() { } //@} /** @name Карта интерфейсов СОМ */ //@{ BEGIN_COM_MAP(CWebBrowserEvents2Sink) COM_INTERFACE_ENTRY_IID(DIID_DWebBrowserEvents2,CWebBrowserEvents2Sink) END_COM_MAP() //@} /** @name Карта точек входа в обработчики событий DWebBrowserEvents2 */ //@{ BEGIN_SINK_MAP(CWebBrowserEvents2Sink) SINK_ENTRY_EX(0,DIID_DWebBrowserEvents2,DISPID_BEFORENAVIGATE2,BeforeNavigate2) SINK_ENTRY_EX(0,DIID_DWebBrowserEvents2,DISPID_CLIENTTOHOSTWINDOW,ClientToHostWindow) ....... SINK_ENTRY_INFO(0,DIID_DWebBrowserEvents2,DISPID_FILEDOWNLOAD,FileDownload,&FileDownloadInfo) END_SINK_MAP() private: /** @name Обработчики DWebBrowserEvents2 (объявления) */ //@{ STDMETHOD(BeforeNavigate2)(IDispatch *pDisp,VARIANT *url,VARIANT *Flags,VARIANT *TargetFrameName, VARIANT *PostData,VARIANT *Headers,VARIANT_BOOL *Cancel); STDMETHOD(ClientToHostWindow)(long *&CX,long *&CY); ........ // debug assert (passed 2 parameters instead of 1) [TypeLib documented bug] STDMETHOD(FileDownload)(VARIANT_BOOL *&ActiveDocument,VARIANT_BOOL *&Cancel); ....... STDMETHOD(WindowSetWidth)(long Width); //@} }; FileDownloadInfo нужна, чтобы явно объявить параметры OnFileDownload из-за ошибки в TypeLib shdocvw.dll
Ленивый ты. Второй способ тривиален: класс, реализующий IDispatch (с реализованными методами AddRef, Release, QueryInterface и Invoke, остальным можно возвращать E_NOTIMPL). Либо у тебя ошибка где-то, либо в SetSite может что-то другое передаваться. Поставь проверку на запрос интерфейса IWebBrowser2 и подписывайся уже к нему.
Еще немного продвинулся. Теперь Код (Text): AtlAdvise(m_pWebBrowser2, (IDispatch*)this, DIID_DWebBrowserEvents2, &m_dwCookie); возвращает 80040202
Ура. Заработало! Продолжаю спрашивать. После загрузки документа необходимо запустить новый экземпляр браузера с заданным урлом, разммером окна и позицией на экране. Код (Text): CComPtr<IWebBrowser2> spBrowser; CoInitialize( 0 ); hRes = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, __uuidof(IWebBrowser2), (PVOID *)&spBrowser); if (SUCCEEDED(hRes)) { VARIANT var; var.vt = VT_EMPTY; spBrowser->Navigate(CComBSTR(L"http://www.rsdn.ru"), &var, &var, &var, &var); spBrowser->put_Visible(VARIANT_TRUE); } После CoCreateInstance текущий экземпляр висит довольно уверенно, потом функция возвращает E_INOINTERFACE. Если не проверять hRes, то появляется окно "Следующая надстройка была включена, когда возникла неполадка". ( надстройка моя ). В чем проблема?
Попап? Или именно браузер? В любом случае размер окна и позиция задаются либо через IHTMLWindow2::open, либо через соответствующие свойства IWebBrowser2. Какая функция?
CoCreateInstance Нужен попап. Смотреть в сторону IHTMLWindow2? ... Получилось частично. Код (Text): hr = pWebBrowser->get_Document( &pDisp ); if( SUCCEEDED( hr ) ) { IHTMLDocument2* doc = (IHTMLDocument2* )pDisp; hr = doc->get_parentWindow( &Popup ); if( SUCCEEDED( hr ) ) { IHTMLWindow2* res; hr = Popup->open( bstr_t( "http://www.google.com" ), bstr_t( "_blank" ), bstr_t( "left=0,top=0" ), 0, &res ); } } на Popup->open браузер подвисает, потом возвращается E_INOINTERFACE. Весь этот код вызывается из Invoke при dispid==DISPID_DOCUMENTCOMPLETE. Может где-то здесь причина? Попробывал поменять весь этот код на m_pWebBrowser->Navigate - абсолютно то же самое в результате