Использование ActiveX компонента WebBrowser

Тема в разделе "WASM.PROJECTS", создана пользователем GoldFinch, 25 дек 2009.

  1. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    В сети есть небольшое количество примеров, посвященных использованию 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 и ООП.
     
  2. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    1. Встраивание WebBrowser в окно

    В этом примере приводится минимальный код, необходимый для встраивания WebBrowser в окно.
    Для лучшей читаемости, в коде отсутствуют проверки ошибок и код освобождения ресурсов.
    Файл с исходным кодом находится в аттаче.

    Для встраивания WebBrowser в окно необходим следующий код:
    Код (Text):
    1. proc Browser_Embed, hwnd
    2.     ; Инициализируем OLE вызовом OleInitialize(), при этом автоматически будет вызвана CoInitializeEx().
    3.     ; Для использования функций, связанных с встраиванием объектов (см. примечания к OleInitialize),
    4.     ; необходимо вызывать именно OleInitialize, а не CoInitializeEx.
    5.     invoke OleInitialize, 0
    6.  
    7.     ; Cоздаем экземпляр компонента WebBrowser (CLSID_WebBrowser) в текущем процессе (CLSCTX_ INPROC)
    8.     ; и получаем его интерфейс IOleObject.
    9.     ; Здесь можно получить любой другой интерфейс, например IWebBrowser2, порядок получения интерфейсов не важен.
    10.     local oleObject:IOleObject
    11.     invoke CoCreateInstance, CLSID_WebBrowser, NULL, CLSCTX_INPROC, IID_IOleObject, addr oleObject
    12.  
    13.     ; Инициализируем объект окна;
    14.     ; window - объект представляющий наше окно, требуется WebBrowser для обратной связи с окном.
    15.     m2m [window.hwnd],[hwnd]
    16.  
    17.     ; Передаем объекту WebBrowser интерфейс IOleClientSite объекта нашего окна.
    18.     cominvk oleObject, SetClientSite, window.IOleClientSite
    19.  
    20.     ; Вызываем команду активации WebBrowser.
    21.     ; При этом передаются данные об окне и его области, в которой будет отображаться WebBrowser; используется вся клиентская область окна.
    22.     local rect:RECT
    23.     invoke GetClientRect, [hwnd], addr rect
    24.     cominvk oleObject, DoVerb, OLEIVERB_INPLACEACTIVATE, NULL, window.IOleClientSite, 0, [hwnd], addr rect
    25.  
    26.     ; Теперь WebBrowser полностью работоспособен.
    27.     ; Для проверки, получаем интерфейс IWebBrowser2, и переходим на какую-нибудь страничку:
    28.     local webBrowser:IWebBrowser2
    29.     cominvk oleObject, QueryInterface, IID_IWebBrowser2, addr webBrowser
    30.     cominvk webBrowser, Navigate, "ya.ru",0,0,0,0
    31.  
    32.     ret
    33. endp
    Функция CoCreateInstance создает объект WebBrowser, и возвращает указатель на один из его интерфейсов.
    У объекта может быть несколько интерфейсов, соответственно если представить объект как структуру в памяти, указатели на разные интерфейсы будут указывать на разные части этой структуры. Каждый интерфейс (часть объекта) начинается с указателя на таблицу методов (VMT) этого интерфейса. Так как все интерфейсы наследуют от интерфейса IUnknown, имея указатель на один интерфейс, можно получить указатели на остальные интерфейсы.

    Для встраивания WebBrowser в окно, ему надо предоставить как минимум два интерфейса от этого окна - это IOleClientSite и IOleInPlaceSite. Указатель на интерфейс IOleClientSite окна передается WebBrowser при вызове его методов IOleObject::SetClientSite и IOleObject::lol: oVerb. Указатель на IOleInPlaceSite WebBrowser получает сам, вызывая метод IUnknown::QueryInterface окна.
    В данном примере, объект-окно создается статически.
    Код (Text):
    1. ; Объявление класса Window
    2. struc Window
    3. {
    4.     ; Класс наследует от интерфейсов IOleClientSite и IOleInPlaceSite, поэтому содержит указатели на VMT каждого интерфейса
    5.     .IOleClientSite dd Window_IOleClientSite_VMT
    6.     .IOleInPlaceSite dd Window_IOleInPlaceSite_VMT
    7.     ; Хендл окна
    8.     .hwnd dd ?
    9. }
    10. ; таблица виртуальных методов интерфейса IOleClientSite класса Window
    11. Window_IOleClientSite_VMT:
    12.     ; IOleInPlaceSite наследует от IUnknown,
    13.     ; соответственно первыми идут методы интерфейса IUnknown, затем методы IOleClientSite
    14.     dd Window_IUnknown_QueryInterface
    15.     dd stub_ret1
    16.     dd stub_ret1
    17.     ; методы IOleClientSite
    18.     dd stub_ok
    19.     dd stub_2d1p_notimpl
    20.     dd stub_1p_nointerface
    21.     dd stub_ok
    22.     dd stub_1d_ok
    23.     dd stub_notimpl
    24.  
    25. ; таблица виртуальных методов интерфейса IOleInPlaceSite класса Window
    26. Window_VMT_IOleInPlaceSite:
    27.     ; IOleInPlaceSite наследует от IOleWindow, который в свою очередь наследует от IUnknown.
    28.     dd Window_IUnknown_QueryInterface
    29.     dd stub_ret1
    30.     dd stub_ret1
    31.     ; методы IOleWindow
    32.     dd Window_IOleWindow_GetWindow
    33.     dd stub_1d_ok
    34.     ; методы IOleInPlaceSite
    35.     dd stub_ok
    36.     dd stub_ok
    37.     dd stub_ok
    38.     dd Window_IOleInPlaceSite_GetWindowContext
    39.     dd stub_1d_ok
    40.     dd stub_1d_ok
    41.     dd stub_ok
    42.     dd stub_ok
    43.     dd stub_ok
    44.     dd stub_1d_ok
    45.  
    46. ; глобальная переменная – объект типа Window
    47.     window Window
    Полностью требуется реализовать только методы IUnknown::QueryInterface, IOleInPlaceSite::GetWindow и IOleInPlaceSite::GetWindowContext, для остальных методов нужны только заглушки, возвращающие успех или ошибку.

    Метод IUnknown::QueryInterface принимает указатель на GUID запрашиваемого интерфейса и указатель на переменную куда надо поместить указатель на этот интерфейс.
    В нашем случае, QueryInterface должна возвращать указатели на интерфейсы IUnknown, IOleClientSite, IOleWindow, IOleInPlaceSite.
    В данном примере, объект window только один и размещен статически, поэтому QueryInterface возвращает фиксированные адреса, и не использует this.
    Код (Text):
    1. proc Window_IUnknown_QueryInterface uses esi edi, this, riid, ppvObject
    2.     ; GUID представляет собой 128-разрядное число,
    3.     ; однако, почти у всех стандартных интерфейсов различается только младший dword, поэтому здесь проверяется только он.
    4.     mov ecx, [riid]
    5.     mov ecx, [ecx]
    6.     .if ecx=[IID_IUnknown.Data1] | ecx=[IID_IOleClientSite.Data1]
    7.     ; Возврат указателя на интерфейс IOleClientSite
    8.     ; Так как реализации интерфейсов IUnknown у IOleClientSite и IOleInPlaceSite совпадают, можно возвращать указатель на любой из них.
    9.         mov edx,[ppvObject]
    10.         mov dword[edx],window.IOleClientSite
    11.         xor eax,eax ;S_OK
    12.     .elseif ecx=[IID_IOleWindow.Data1] | ecx=[IID_IOleInPlaceSite.Data1]
    13.     ; Возврат указателя на интерфейс IOleInPlaceSite и IOleWindow
    14.         mov edx,[ppvObject]
    15.         mov dword[edx],window.IOleInPlaceSite
    16.         xor eax,eax ;S_OK
    17.     .else
    18.     ; При ошибке должен быть возвращен нулевой указатель на интерфейс.
    19.         mov edx,[ppvObject]
    20.         mov dword[edx], NULL
    21.         mov eax,E_NOINTERFACE
    22.     .endif
    23.  
    24.     ret
    25. endp
    Методы IOleInPlaceSite::GetWindow и IOleInPlaceSite::GetWindowContext возвращают информацию об окне.
    Код (Text):
    1. proc Window_IOleWindow_GetWindow, this, phwnd
    2.     mov edx,[phwnd]
    3.     ; Вместо this используется статическая переменная window.
    4.     m2m [edx],[window.hwnd]
    5.     xor eax,eax ; S_OK
    6.     ret
    7. endp
    8.  
    9. proc Window_IOleInPlaceSite_GetWindowContext, this, ppFrame, ppDoc, lprcPosRect, lprcClipRect, lpFrameInfo
    10.     ; Окно не предоставляет интерфейсов IOleInPlaceFrame и IOleInPlaceUIWindow.
    11.     mov eax,[ppFrame]
    12.     mov dword[eax],NULL
    13.     mov eax,[ppDoc]
    14.     mov dword[eax],NULL
    15.  
    16.     ; Используется вся клиентская область окна.
    17.     invoke GetClientRect, [window.hwnd], [lprcPosRect]
    18.     invoke GetClientRect, [window.hwnd], [lprcClipRect]
    19.  
    20.     mov eax,[lpFrameInfo]
    21.     mov [eax+OLEINPLACEFRAMEINFO.fMDIApp], FALSE
    22.     m2m [eax+OLEINPLACEFRAMEINFO.hwndFrame], [window.hwnd]
    23.     mov [eax+OLEINPLACEFRAMEINFO.haccel], 0
    24.     mov [eax+OLEINPLACEFRAMEINFO.cAccelEntries], 0
    25.  
    26.     xor eax,eax ; S_OK
    27.     ret
    28. endp
    Методы IUnknown::AddRef и IUnknown::Release реализуются заглушкой, всегда возвращающей ненулевой счетчик экземпляров.
    Для остальных методов используются заглушки, либо возвращающие S_OK, либо ошибку. При этом если метод должен вернуть указатель на интерфейс (передается указатель на указатель на интерфейс), при ошибке метод должен вернуть нулевой указатель:
    Код (Text):
    1. proc stub_1p_nointerface, this, ptr1
    2.     mov eax,[ptr1]
    3.     mov dword[eax],NULL
    4.     mov eax,E_NOINTERFACE
    5.     ret
    6. endp
    В качестве окна, в данном примере используется модальное диалоговое окно, загружающееся из шаблона (DialogBoxIndirect). Код встраивания WebBrowser выполняется при сообщении WM_INITDIALOG.

    Что осталось за бортом:
    1) Задание заголовка окна, иконки окна.
    2) Изменение размеров окна.
    3) Многооконность, правильное освобождение ресурсов, проверка ошибок.
    4) Правильный вызов IWebBrowser::Navigate, которая требует строку типа BSTR, и значения типа VARIANT* для остальных параметров.