Использование компонента TWebBrowser
Доступ к документной модели TWebBrowser
Во многих современных программах необходимо работать с данными в формате HTML. В качестве средства для просмотра таких данных в Delphi применяется компонент TWebBrowser, который использует элемент управления ActiveX WebBrowser, входящий в состав Microsoft Internet Explorer. Таким образом, этот компонент имеется на любом компьютере, на котором установлен Internet Explorer. Все последние версии Windows содержат TWebBrowser в своем составе и без него практически неработоспособны.
Базовые операции
Для того чтобы использовать TWebBrowser в своей программе, следует разместить на форме соответствующий компонент, имеющийся на закладке Internet. Затем, для отображения в нем страницы HTML, необходимо вызвать его метод Navigate:
procedure TForm1.Button1Click(Sender: TObject);
var
Flags, TargetFrameName, PostData, Headers: OleVariant;
begin
WebBrowser1.Navigate(‘http://www.borland.com’, Flags,
TargetFrameName, PostData, Headers);
end;
Рассмотрим подробнее параметры, передаваемые в метод Navigate.
Первым параметром является строка с URL, указывающим адрес, из которого должна осуществляться загрузка. Поддерживаются все протоколы, доступные в IE, например file:// — загрузка файла, res:// — загрузка из ресурса.
Остальные параметры не являются обязательными и служат для передачи дополнительной информации (табл. 1).
Наиболее интересным является параметр PostData, позволяющий передать на Web-сервер данные, полученные в результате заполнения формы, если этот сервер требует HTTP — транзакции POST. Так, следующий фрагмент кода передает на сервер имя пользователя и пароль, заполненные в форме Delphi:
var
LoginDialog: TLoginDialog;
Flags, TargetFrameName, PostData, Headers: OleVariant;
S: String;
...
with TLoginDialog.Create(Application) do
try
if ShowModal = mrOk then
begin
S := Format(‘UserName=%s&Password=%s’, _
[Edit1.Text, Edit2.Text]);
PostData := VarArrayCreate([1, _
Length(S) + 1], varByte);
System.Move(S[1], VarArrayLock(PostData)^, _
Length(S) + 1);
VarArrayUnlock(PostData);
Headers :=
‘Content-Type: application/x-www-form-urlencoded’#10#13;
WebBrowser1.Navigate(‘http://intranetserver/secretpage’, Flags,
TargetFrameName, PostData, Headers);
end;
finally
Free;
end;
Например, на Web-сервере этот запрос может быть обработан, следующим ASP-скриптом:
Dim sConnect
Dim sUserName
Dim sPassword
sUserName = Request.Form(“User”)
sPassword = Request.Form(“Pass”)
sConnect = “Provider=SQLOLEDB.1;Persist Security Info=True;” & _
“Initial Catalog=Katren;Data Source=DBSERVER;” & _
“Password=” & sPassword & _
“;User ID=” & sUserName
Session(“ConnectString”) = sConnect
После того как данные получены, необходимо предоставить пользователю возможность работы с ними. Многие функции TWebBrowser доступны через метод ExecWB, предоставляющий простой способ обращения к интерфейсу IOleCommandTarget. Этот метод имеет такой вид:
procedure TWebBrowser.ExecWB(
cmdID: OLECMDID; // Идентификатор команды
cmdexecopt: OLECMDEXECOPT; // Параметры выполнения
var pvaIn, // Дополнительные параметры,
pvaOut: OleVariant // зависящие от команды
); safecall;
CmdID может быть одной из констант OLECMDID, определенных в файле ShDocVw.pas.
Параметр cmdexecopt может принимать одно из четырех значений, приведенных в табл. 2.
Параметры pvaIn и pvaOut являются дополнительными и зависят от конкретной команды.
Имеется возможность запросить у TWebBrowser доступность той или иной команды при помощи функции:
function TWebBrowser.QueryStatusWB( cmdID: OLECMDID // Идентификатор команды ): OLECMDF; safecall;
Функция возвращает битовую маску из значений, приведенных в табл. 3.
Следовательно, можно настраивать интерфейс в зависимости от поддерживаемых текущей версией TWebBrowser возможностей:
var
Flags: OLECMDF;
...
Flags := WebBrowser1.QueryStatusWB(OLECMDID_COPY);
ActionCopy.Visible := (Flags and OLECMDF_SUPPORTED) =
OLECMDF_SUPPORTED;
ActionCopy.Enabled := (Flags and OLECMDF_ENABLED) =
OLECMDF_ENABLED;
Для печати содержимого TWebBrowser служит команда OLECMDID_PRINT. Метод печати может выглядеть, в частности, следующим образом:
procedure TForm1.ActionPrintExecute(Sender: TObject);
var
A, B: OleVariant;
UserAction: Cardinal;
begin
if Sender = ActionPrintWithSetup then
UserAction := OLECMDEXECOPT_PROMPTUSER
else
UserAction := OLECMDEXECOPT_DONTPROMPTUSER;
try
WebBrowser1.ExecWB(OLECMDID_PRINT, UserAction, A, B);
except
end;
end;
Блок try … except … end необходим по той причине, что TWebBrowser при выполнении любой команды при помощи ExecWB генерирует исключение EOleException с кодом:
-2147221248 ($80040100) Trying to revoke _
a drop target that has not been registered.
Начиная с Internet Explorer 5 документированы дополнительные команды, поддерживаемые через интерфейс IOleCommandTarget. Они существенно расширяют возможности по управлению компонентом, однако недоступны либо не документированы в версии 4. Это создает определенные сложности при программировании. Так, чтобы организовать поиск внутри загруженной страницы, необходим следующий код:
const
// Недокументированная константа
CGID_IE4: TGUID = ‘{ed016940-bd5b-11cf-ba4e-00c04fd70816}’;
// Документировано в IE5 SDK
CGID_MSHTML: TGUID = ‘{DE4BA900-59CA-11CF-9592-444553540000}’;
IDM_FIND = 67; procedure TForm1.ActionFindExecute(Sender: TObject);
var
A, B: OleVariant;
Target: IOleCommandTarget;
OleCmd: TOLECMD;
begin
// Получаем интерфейс IOleCommandTarget
Target := wbMain.Document as IOLECommandtarget;
with OleCmd do
begin
cmdId := IDM_FIND;
cmdf := 0;
end;
// Запрашиваем, поддерживается ли команда
Target.QueryStatus(@CGID_MSHTML, 1, @OleCmd, NIL);
if (OleCmd.cmdf and OLECMDF_SUPPORTED) = OLECMDF_SUPPORTED then
// Да, у нас IE5+, поэтому вызываем документированным способом
Target.Exec(@CGID_MSHTML, IDM_FIND,
OLECMDEXECOPT_DODEFAULT, A, B)
else
// Нет, у нас IE4, поэтому вызываем недокументированным способом
Target.Exec(@CGID_IE4, 1, OLECMDEXECOPT_DODEFAULT, A, B);
end;
Использование недокументированного вызова в данном случае оправданно, так как в версии 4 этот вызов уже не будет изменяться, а в версии 5 мы обнаруживаем и используем документированный метод. В то же время IE4 пока еще достаточно распространен, так что будет нецелесообразно полностью лишать программу возможности поиска на таких компьютерах.
![]() |
![]() |
Тонкая настройка
Если требуется более тонкая настройка компонента, то необходимо реализовать интерфейс IDocHostUIHandler, позволяющий программисту взять под контроль поведение TWebBrowser.
Интерфейс объявлен как:
type
TDocHostInfo = packed record
cbSize: ULONG;
dwFlags: DWORD;
dwDoubleClick: DWORD;
end; const
DOCHOSTUIFLAG_DIALOG = 1;
DOCHOSTUIFLAG_DISABLE_HELP_MENU = 2;
DOCHOSTUIFLAG_NO3DBORDER = 4;
DOCHOSTUIFLAG_SCROLL_NO = 8;
DOCHOSTUIFLAG_DISABLE_SCRIPT_INACTIVE = 16;
DOCHOSTUIFLAG_OPENNEWWIN = 32;
DOCHOSTUIFLAG_DISABLE_OFFSCREEN = 64;
DOCHOSTUIFLAG_FLAT_SCROLLBAR = 128;
DOCHOSTUIFLAG_DIV_BLOCKDEFAULT = 256;
DOCHOSTUIFLAG_ACTIVATE_CLIENTHIT_ONLY = 512; const
DOCHOSTUIDBLCLK_DEFAULT = 0;
DOCHOSTUIDBLCLK_SHOWPROPERTIES = 1;
DOCHOSTUIDBLCLK_SHOWCODE = 2; type
IDocHostUIHandler = interface(IUnknown)
[‘{bd3f23c0-d43e-11cf-893b-00aa00bdce1a}’]
function ShowContextMenu(const dwID: DWORD; const ppt: PPOINT;
const pcmdtReserved: IUnknown;
const pdispReserved: IDispatch): HRESULT; stdcall;
function GetHostInfo(var pInfo: TDOCHOSTUIINFO): HRESULT;
stdcall;
function ShowUI(const dwID: DWORD;
const pActiveObject: IOleInPlaceActiveObject;
const pCommandTarget: IOleCommandTarget;
const pFrame: IOleInPlaceFrame;
const pDoc: IOleInPlaceUIWindow): HRESULT; stdcall;
function HideUI: HRESULT; stdcall;
function UpdateUI: HRESULT; stdcall;
function EnableModeless(const fEnable: BOOL): HRESULT; stdcall;
function OnDocWindowActivate(const fActivate: BOOL): HRESULT;
stdcall;
function OnFrameWindowActivate(const fActivate: BOOL): HRESULT;
stdcall;
function ResizeBorder(const prcBorder: PRECT;
const pUIWindow: IOleInPlaceUIWindow;
const fRameWindow: BOOL): HRESULT; stdcall;
function TranslateAccelerator(const lpMsg: PMSG;
const pguidCmdGroup: PGUID;
const nCmdID: DWORD): HRESULT; stdcall;
function GetOptionKeyPath(var pchKey: POLESTR;
const dw: DWORD): HRESULT; stdcall;
function GetDropTarget(const pDropTarget: IDropTarget;
out ppDropTarget: IDropTarget): HRESULT; stdcall;
function GetExternal(out ppDispatch: IDispatch): HRESULT;
stdcall;
function TranslateUrl(const dwTranslate: DWORD;
const pchURLIn: POLESTR; var ppchURLOut: POLESTR): HRESULT;
stdcall;
function FilterDataObject(const pDO: IDataObject;
out ppDORet: IDataObject): HRESULT; stdcall;
end;
Наследник TWebBrowser, реализующий этот интерфейс, должен быть объявлен так:
type
TCustomizedWebBrowser = class(TWebBrowser, IDocHostUIHandler)
// Реализация методов IDocHostUIHandler
end;
На нашем CD-ROM приведен код такого компонента, реализующего минимальную функциональность. Вы можете использовать его как основу для создания своих расширенных наследников TWebBrowser.
А теперь рассмотрим наиболее интересные, с точки зрения программиста, методы интерфейса IDocHostUIHandler.
Начнем с метода ShowContextMenu:
function ShowContextMenu(const dwID: DWORD; const ppt: PPOINT;
const pcmdtReserved: IUnknown;
const pdispReserved: IDispatch): HRESULT;
Эта функция вызывается в том случае, когда TWebBrowser должен показать контекстное меню. Если вы отображаете собственное меню или хотите подавить меню, то функция должна вернуть S_OK, а если меню должен показать TWebBrowser — то S_FALSE.
В функцию передаются следующие параметры:
1. DwID — идентификатор меню, который может принимать одно из следующих значений:
const
CONTEXT_MENU_DEFAULT = 0;
CONTEXT_MENU_IMAGE = 1;
CONTEXT_MENU_CONTROL = 2;
CONTEXT_MENU_TABLE = 3;
CONTEXT_MENU_DEBUG = 4;
CONTEXT_MENU_1DSELECT = 5;
CONTEXT_MENU_ANCHOR = 6;
CONTEXT_MENU_IMGDYNSRC = 7;
В зависимости от значения идентификатора вы можете вывести подходящее меню.
2. ppt — координаты, в которых должно быть показано меню.
3. pcmdtReserved — интерфейс IOleCommandTarget, позволяющий запросить состояние команд и их выполнение.
4. pdispReserved — интерфейс IDispatch объекта, для которого вызывается меню.
Простейшая реализация этого метода может выглядеть следующим образом:
function TcustomizedWebBrowser.ShowContextMenu(const dwID: DWORD;
const ppt: PPOINT; const pcmdtReserved: IUnknown;
const pdispReserved: IDispatch): HRESULT;
begin
// Предполагаем, что поле FPopupMenu хранит ссылку
// на компонент TPopupMenu
if Assigned(FPopupMenu) then begin
pmContext.Popup(ppt.X, ppt.Y);
Result := S_OK;
end
else Result := S_FALSE;
end;
Для полного запрета контекстного меню метод должен всегда возвращать S_OK.
Следующий метод, который мы рассмотрим — GetHostInfo:
unction GetHostInfo(var pInfo: TDocHostInfo): HRESULT; stdcall;
Приложение может заполнить структуру pInfo, определенную как:
TDocHostInfo = packed record
cbSize: ULONG;
dwFlags: DWORD;
dwDoubleClick: DWORD;
end;
Здесь dwFlags — битовая маска из следующих флагов, приведенных в табл. 4, а dwDoubleClick задает реакцию на двойной щелчок мышью и может принимать одно из значений, приведенных в табл. 5.
Метод должен вернуть S_OK или код ошибки OLE.
Например, чтобы создать окно с плоскими полосами прокрутки и без трехмерной рамки, необходимо реализовать этот метод следующим образом:
function TCustomizedWebBrowser.GetHostInfo(
var pInfo: TDocHostInfo): HRESULT; stdcall;
begin
with pInfo do
dwFlags := dwFlags or DOCHOSTUIFLAG_NO3DBORDER or
DOCHOSTUIFLAG_FLAT_SCROLLBAR;
Result := S_OK;
end;
Метод
function TranslateAccelerator(const lpMsg: TMsg;
const pguidCmdGroup: TGUID; nCmdID: DWORD): HRESULT; stdcall;
позволяет перехватить исполнение команд и обработку «горячих» клавиш и заменить ее на свою.
Метод
function GetOptionKeyPath(var pchKey: PWideChar;
dwReserved: DWORD): HRESULT; stdcall;
позволяет задать путь в реестре, который TWebBrowser будет использовать для хранения настроек. Это дает возможность, в частности, сделать используемый в программе компонент независимым от текущих настроек Internet Explorer.
Путь должен содержаться в ключе реестра HKEY_CURRENT_USER.
Этот метод должен выделить память под строку функцией CoTackMemAlloc. Даже в случае ошибки параметр pchKey нужно инициализировать значением NIL или адресом строки. Метод возвращает S_OK в случае успеха, а в противном случае — S_FALSE.
Типичная реализация этого метода может выглядеть так:
function TCustomizedWebBrowser.GetOptionKeyPath(
var pchKey: PWideChar; dwReserved: DWORD): HRESULT;
var
ResultLen: Integer;
begin
Result := S_FALSE;
// В поле TCustomizedWebBrowser.FOptionKeyPath: String
// хранится путь к настройкам
if Length(FOptionKeyPath) > 0 then
begin
// Получаем длину строки UNICODE
ResultLen := MultiByteToWideChar(CP_ACP, 0,
PChar(FOptionKeyPath), -1, NIL, 0);
// Выделяем память под буфер
pchKey := CoTaskMemAlloc(ResultLen * SizeOf(WideChar));
// Если выделение успешно, копируем строку в буфер
if Assigned(pchKey) then
begin
MultiByteToWideChar(CP_ACP, 0, PChar(FOptionKeyPath), -1,
pchKey, ResultLen);
Result := S_OK;
end;
end else begin
// Свойство не задано — инициализируем параметр в NIL
pchKey := NIL;
end;
end;
Существует ряд настроек, которые, несмотря на наличие обработчика GetOptionKeyPath, в любом случае берут из стандартных параметров Internet Explorer. Наиболее важными из них являются колонтитулы, используемые при печати. В версиях Internet Explorer до 5.5 включительно единственным способом изменить (или подавить) колонтитулы является запись новых значений в ключ реестра:
HKCU\Software\Microsoft\Internet Explorer\PageSetup
перед печатью и восстановление их после печати.
А теперь поговорим о методе
function GetExternal(var ppDispatch: IDispatch): HRESULT; stdcall;
Он позволяет вернуть указатель на реализованный в вашем приложении интерфейс IDispatch, который будет доступен для скриптов в TWebBrowser. Если вы не реализуете этот интерфейс, то параметр ppDispatch должен быть установлен равным NIL. Метод возвращает S_OK в случае успеха или код ошибки OLE в случае ошибки.
Методы этого интерфейса доступны из скриптов, которые выполняют в TWebBrowser следующим образом:
window.external.MethodName
Реализовать IDispatch можно, например, при помощи класса TAutoObject.
Метод TranslateURL позволяет изменить URL, по которому осуществляется загрузка страницы.
function TranslateURL(dwTranslate: DWORD; pchURLIn: PWideChar; var ppchURLOut: PWideChar): HRESULT; stdcall;
pchURLIn указывает на строку, содержащую исходный URL. Если ваше приложение осуществляет трансляцию, то оно должно выделить память под новое значение, используя функцию CoTaskMemAlloc, заполнить буфер новым значением URL и вернуть S_OK.
В противном случае вы должны присвоить ppchURLOut значение NIL и вернуть S_FALSE. При возникновении ошибки метод должен вернуть OLE-код ошибки.
Обработчик вызывается только при интерактивном переходе по ссылке из TWebBrowser и не вызывается при переходе с помощью метода Navigate.
![]() |
![]() |
Доступ к документной модели TWebBrowser
В Internet Explorer реализовано расширение HTML, называемое Dynamic HTML (DHTML). Эта модель представляет все элементы HTML-документа в виде набора коллекций объектов, доступных для изменения. Скрипты, встроенные в страницы и приложения и имеющие доступ к этим коллекциям, могут находить и изменять их элементы, а также добавлять новые, причем изменения будут немедленно отражены в окне TWebBrowser. Иерархическое объектное представление HTML-объектов называется DOM (Document Object Model).
Программисту DOM в элементе управления IE ActiveX доступна в виде набора COM-интерфейсов. Отправной точкой для доступа к ней служит свойство:
property Document: IDispatch;
Это свойство обеспечивает доступ к интерфейсу IHtmlDocument2, позволяющему работать с содержимым документа. Для получения интерфейса необходимо запросить его при помощи оператора as:
var Document: IHtmlDocument2; ... Document := WebBrowser.Document as IHtmlDocument2;
Документ в DOM представляет собой набор коллекций элементов. Для доступа к коллекции служит интерфейс IHtmlElementCollection, а к элементу коллекции — IHtmlElement. Следующий пример выводит все тэги, имеющиеся в текущем документе и текст внутри тэгов:
procedure TForm1.Button1Click(Sender: TObject);
var
HtmlDocument: IHtmlDocument2;
HtmlCollection: IHtmlElementCollection;
HtmlElement: IHtmlElement;
I: Integer;
begin
Memo1.Lines.Clear;
HtmlDocument := WebBrowser.Document as IHtmlDocument2;
HtmlCollection := HtmlDocument.All;
for I := 0 to HtmlCollection.Length - 1 do begin
HtmlElement := HtmlCollection.Item(i, 0) as IHtmlElement;
Memo1.Lines.Add(HtmlElement.TagName + ‘ ‘ +
HtmlElement.InnerText);
end;
Кроме того, возможно динамическое создание документов в памяти, без необходимости записи их на диск и вызова метода Navigate с протоколом ‘file://’
Проиллюстрируем работу с документной модели TWebBrowser на конкретном примере. Расположим на форме компоненты TWebBrowser, TMemo и три TButton, а потом создадим следующие обработчики событий:
uses MSHTML, ActiveX; procedure TForm1.FormCreate(Sender: TObject);
begin
// Инициализируем пустой документ в TWebBrowser
WebBrowser1.Navigate(‘about:blank’);
end; procedure TForm1.Button1Click(Sender: TObject);
var
Document: IHTMLDocument2;
V: OleVariant;
begin
// Этот метод переписывает в TWebBrowser HTML-
// документ из TMemo
Document := WebBrowser1.Document as IHtmlDocument2;
V := VarArrayCreate([0, 0], varVariant);
V[0] := Memo1.Text;
Document.Write(PSafeArray(TVarData(v).VArray));
Document.Close;
end; procedure TForm1.Button3Click(Sender: TObject);
var
Document: IHTMLDocument2;
Collection: IHTMLElementCollection;
Element: IHTMLElement;
I: Integer;
begin
// Этот метод модифицирует текст документа при помощи DHTML
Document := WebBrowser1.Document as IHtmlDocument2;
Collection := Document.all;
Collection := Collection.Tags(‘BODY’) as IHTMLElementCollection;
Element := Collection.Item(NULL, 0) as IHTMLElement;
Element.InnerText := ‘Modifyed by DHTML’;
end; procedure TForm1.Button2Click(Sender: TObject);
var
Document: IHTMLDocument2;
begin
// Этот метод позволяет просмотреть в TMemo код HTML
// документа из TWebBrowser
Document := WebBrowser1.Document as IHtmlDocument2;
Memo1.Text := (Document.all.Item(NULL, 0)
as IHTMLElement).OuterHTML;
end;
В Memo1.Lines запишем такой текст:
<HTML>
<HEAD>
<TITLE>Hello World</TITLE>
</HEAD>
<BODY>
Hello again !
</BODY>
</HTML>
Итак, мы получили возможность динамически создавать HTML-документы и предоставлять их пользователю.
КомпьютерПресс 6'2001