В сети есть небольшое количество примеров, посвященных использованию ActiveX компонента WebBrowser в приложениях не использующих MFC и ATL. Это пара примеров на C и C++, причем 2й базируется на 1м: http://www.mvps.org/user32/ (1); http://www.codeguru.com/cpp/i-n/ieprogram/article.php/c4379/ (2). И один пример на масме http://www.kakeeware.com/i_kb.php (3). Какие-то комментарии есть только в (2), но они малоинформативны, и там используется более сложный подход чем в (3). В этой теме будут выложены исходники примеров использования WebBrowser на фасме, с комментариями по всем ключевым моментам кода. В данных примерах код написан с уклоном на большую читаемость и понятность, нежели оптимальность. Предполагается что читатель знаком с фасмом, и знаком с базовыми понятиями COM и ООП.
1. Встраивание WebBrowser в окно В этом примере приводится минимальный код, необходимый для встраивания WebBrowser в окно. Для лучшей читаемости, в коде отсутствуют проверки ошибок и код освобождения ресурсов. Файл с исходным кодом находится в аттаче. Для встраивания WebBrowser в окно необходим следующий код: Код (Text): proc Browser_Embed, hwnd ; Инициализируем OLE вызовом OleInitialize(), при этом автоматически будет вызвана CoInitializeEx(). ; Для использования функций, связанных с встраиванием объектов (см. примечания к OleInitialize), ; необходимо вызывать именно OleInitialize, а не CoInitializeEx. invoke OleInitialize, 0 ; Cоздаем экземпляр компонента WebBrowser (CLSID_WebBrowser) в текущем процессе (CLSCTX_ INPROC) ; и получаем его интерфейс IOleObject. ; Здесь можно получить любой другой интерфейс, например IWebBrowser2, порядок получения интерфейсов не важен. local oleObject:IOleObject invoke CoCreateInstance, CLSID_WebBrowser, NULL, CLSCTX_INPROC, IID_IOleObject, addr oleObject ; Инициализируем объект окна; ; window - объект представляющий наше окно, требуется WebBrowser для обратной связи с окном. m2m [window.hwnd],[hwnd] ; Передаем объекту WebBrowser интерфейс IOleClientSite объекта нашего окна. cominvk oleObject, SetClientSite, window.IOleClientSite ; Вызываем команду активации WebBrowser. ; При этом передаются данные об окне и его области, в которой будет отображаться WebBrowser; используется вся клиентская область окна. local rect:RECT invoke GetClientRect, [hwnd], addr rect cominvk oleObject, DoVerb, OLEIVERB_INPLACEACTIVATE, NULL, window.IOleClientSite, 0, [hwnd], addr rect ; Теперь WebBrowser полностью работоспособен. ; Для проверки, получаем интерфейс IWebBrowser2, и переходим на какую-нибудь страничку: local webBrowser:IWebBrowser2 cominvk oleObject, QueryInterface, IID_IWebBrowser2, addr webBrowser cominvk webBrowser, Navigate, "ya.ru",0,0,0,0 ret endp Функция CoCreateInstance создает объект WebBrowser, и возвращает указатель на один из его интерфейсов. У объекта может быть несколько интерфейсов, соответственно если представить объект как структуру в памяти, указатели на разные интерфейсы будут указывать на разные части этой структуры. Каждый интерфейс (часть объекта) начинается с указателя на таблицу методов (VMT) этого интерфейса. Так как все интерфейсы наследуют от интерфейса IUnknown, имея указатель на один интерфейс, можно получить указатели на остальные интерфейсы. Для встраивания WebBrowser в окно, ему надо предоставить как минимум два интерфейса от этого окна - это IOleClientSite и IOleInPlaceSite. Указатель на интерфейс IOleClientSite окна передается WebBrowser при вызове его методов IOleObject::SetClientSite и IOleObject:oVerb. Указатель на IOleInPlaceSite WebBrowser получает сам, вызывая метод IUnknown::QueryInterface окна. В данном примере, объект-окно создается статически. Код (Text): ; Объявление класса Window struc Window { ; Класс наследует от интерфейсов IOleClientSite и IOleInPlaceSite, поэтому содержит указатели на VMT каждого интерфейса .IOleClientSite dd Window_IOleClientSite_VMT .IOleInPlaceSite dd Window_IOleInPlaceSite_VMT ; Хендл окна .hwnd dd ? } ; таблица виртуальных методов интерфейса IOleClientSite класса Window Window_IOleClientSite_VMT: ; IOleInPlaceSite наследует от IUnknown, ; соответственно первыми идут методы интерфейса IUnknown, затем методы IOleClientSite dd Window_IUnknown_QueryInterface dd stub_ret1 dd stub_ret1 ; методы IOleClientSite dd stub_ok dd stub_2d1p_notimpl dd stub_1p_nointerface dd stub_ok dd stub_1d_ok dd stub_notimpl ; таблица виртуальных методов интерфейса IOleInPlaceSite класса Window Window_VMT_IOleInPlaceSite: ; IOleInPlaceSite наследует от IOleWindow, который в свою очередь наследует от IUnknown. dd Window_IUnknown_QueryInterface dd stub_ret1 dd stub_ret1 ; методы IOleWindow dd Window_IOleWindow_GetWindow dd stub_1d_ok ; методы IOleInPlaceSite dd stub_ok dd stub_ok dd stub_ok dd Window_IOleInPlaceSite_GetWindowContext dd stub_1d_ok dd stub_1d_ok dd stub_ok dd stub_ok dd stub_ok dd stub_1d_ok ; глобальная переменная – объект типа Window window Window Полностью требуется реализовать только методы IUnknown::QueryInterface, IOleInPlaceSite::GetWindow и IOleInPlaceSite::GetWindowContext, для остальных методов нужны только заглушки, возвращающие успех или ошибку. Метод IUnknown::QueryInterface принимает указатель на GUID запрашиваемого интерфейса и указатель на переменную куда надо поместить указатель на этот интерфейс. В нашем случае, QueryInterface должна возвращать указатели на интерфейсы IUnknown, IOleClientSite, IOleWindow, IOleInPlaceSite. В данном примере, объект window только один и размещен статически, поэтому QueryInterface возвращает фиксированные адреса, и не использует this. Код (Text): proc Window_IUnknown_QueryInterface uses esi edi, this, riid, ppvObject ; GUID представляет собой 128-разрядное число, ; однако, почти у всех стандартных интерфейсов различается только младший dword, поэтому здесь проверяется только он. mov ecx, [riid] mov ecx, [ecx] .if ecx=[IID_IUnknown.Data1] | ecx=[IID_IOleClientSite.Data1] ; Возврат указателя на интерфейс IOleClientSite ; Так как реализации интерфейсов IUnknown у IOleClientSite и IOleInPlaceSite совпадают, можно возвращать указатель на любой из них. mov edx,[ppvObject] mov dword[edx],window.IOleClientSite xor eax,eax ;S_OK .elseif ecx=[IID_IOleWindow.Data1] | ecx=[IID_IOleInPlaceSite.Data1] ; Возврат указателя на интерфейс IOleInPlaceSite и IOleWindow mov edx,[ppvObject] mov dword[edx],window.IOleInPlaceSite xor eax,eax ;S_OK .else ; При ошибке должен быть возвращен нулевой указатель на интерфейс. mov edx,[ppvObject] mov dword[edx], NULL mov eax,E_NOINTERFACE .endif ret endp Методы IOleInPlaceSite::GetWindow и IOleInPlaceSite::GetWindowContext возвращают информацию об окне. Код (Text): proc Window_IOleWindow_GetWindow, this, phwnd mov edx,[phwnd] ; Вместо this используется статическая переменная window. m2m [edx],[window.hwnd] xor eax,eax ; S_OK ret endp proc Window_IOleInPlaceSite_GetWindowContext, this, ppFrame, ppDoc, lprcPosRect, lprcClipRect, lpFrameInfo ; Окно не предоставляет интерфейсов IOleInPlaceFrame и IOleInPlaceUIWindow. mov eax,[ppFrame] mov dword[eax],NULL mov eax,[ppDoc] mov dword[eax],NULL ; Используется вся клиентская область окна. invoke GetClientRect, [window.hwnd], [lprcPosRect] invoke GetClientRect, [window.hwnd], [lprcClipRect] mov eax,[lpFrameInfo] mov [eax+OLEINPLACEFRAMEINFO.fMDIApp], FALSE m2m [eax+OLEINPLACEFRAMEINFO.hwndFrame], [window.hwnd] mov [eax+OLEINPLACEFRAMEINFO.haccel], 0 mov [eax+OLEINPLACEFRAMEINFO.cAccelEntries], 0 xor eax,eax ; S_OK ret endp Методы IUnknown::AddRef и IUnknown::Release реализуются заглушкой, всегда возвращающей ненулевой счетчик экземпляров. Для остальных методов используются заглушки, либо возвращающие S_OK, либо ошибку. При этом если метод должен вернуть указатель на интерфейс (передается указатель на указатель на интерфейс), при ошибке метод должен вернуть нулевой указатель: Код (Text): proc stub_1p_nointerface, this, ptr1 mov eax,[ptr1] mov dword[eax],NULL mov eax,E_NOINTERFACE ret endp В качестве окна, в данном примере используется модальное диалоговое окно, загружающееся из шаблона (DialogBoxIndirect). Код встраивания WebBrowser выполняется при сообщении WM_INITDIALOG. Что осталось за бортом: 1) Задание заголовка окна, иконки окна. 2) Изменение размеров окна. 3) Многооконность, правильное освобождение ресурсов, проверка ошибок. 4) Правильный вызов IWebBrowser::Navigate, которая требует строку типа BSTR, и значения типа VARIANT* для остальных параметров.