Использование TwebBrowser (статья)
Материал из DRKB
Использование TWebBrowser[править | править код]
Во многих современных программах требуется работа с данными в формате HTML. В качестве средства для просмотра таких данных в Delphi используется ActiveX компонент TWebBrowser, который использует компонент WebBrowser, входящий в состав Microsoft Internet Explorer. Таким образом, он имеется на любом компьютере, на котором установлен Internet Explorer. Все последние версии Windows включают этот компонент в стандартной поставке и, более того, практически неработоспособны без него.
Базовые операции[править | править код]
Для того, чтобы использовать 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:// - загрузка из ресурса.
Остальные параметры не являются обязательными и служат для передачи дополнительной информации.
- Flags - Целое число, представляющее из себя битовую маску из следующих флагов:
- 1 - Открыть ресурс в новом окне
- 2 - Не добавлять страницу в историю просмотренных
- 4 - Не загружать страницу из кэша
- 8 - Не сохранять страницу в кэше
- TargetFrameName - Задает имя фрейма, в который будет загружена страница
- PostData - Задает данные для запроса с сервера методом HTTP POST. Если этот параметр пустой - используется метод GET
- Headers - Задает дополнительные заголовки HTTP
Наиболее интересным является параметр PostData, позволяющий передать на Web-Server данные, полученные в результате заполнения формы, если этот сервер требует 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 может принимать одно из следующих четырех значений:
- OLECMDEXECOPT_DODEFAULT - Выполнить команду с настройками «по умолчанию»
- OLECMDEXECOPT_PROMPTUSER - Запросить у пользователя настройки для выполнения команды (например, при печати - вывести диалог Print Setup)
- OLECMDEXECOPT_DONTPROMPTUSER - Выполнить команду не запрашивая пользователя
- OLECMDEXECOPT_SHOWHELP - Вывести справку о команде
Параметры pvaIn и pvaOut - дополнительные и зависят от конкретной команды.
Имеется возможность запросить у TWebBrowser доступность той или иной команды при помощи функции:
function TWebBrowser.QueryStatusWB(
cmdID: OLECMDID // идентификатор команды
): OLECMDF; safecall;
Функция возвращает битовую маску из следующих значений:
- OLECMDF_SUPPORTED - Команда поддерживается
- OLECMDF_ENABLED - Команда поддерживается и разрешена
- OLECMDF_LATCHED - Команда - переключатель и сейчас включена
- OLECMDF_NINCHED - Зарезервировано
Таким образом, можно настраивать интерфейс, в зависимости от поддерживаемых текущей версией 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.
function ShowContextMenu(const dwID: DWORD; const ppt: PPOINT;
const pcmdtReserved: IUnknown;
const pdispReserved: IDispatch): HRESULT;
Эта функция вызывается, когда TWebBrowser должен показать контекстное меню. Если Вы отображаете собственное меню, либо хотите подавить меню - функция должна вернуть S_OK, если меню должен показать TWebBrowser - S_FALSE.
В неё передаются следующие параметры:
- 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;
в зависимости от значения идентификатора Вы можете вывести подходящее меню
- ppt - координаты, в которых должно быть показано меню
- pcmdtReserved - интерфейс IOleCommandTarget, позволяющий запросить состояние команд и их выполнение
- 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.
function GetHostInfo(var pInfo: TDocHostInfo): HRESULT; stdcall;
Приложение может заполнить структуру pInfo, определенную как:
TDocHostInfo = packed record
cbSize: ULONG;
dwFlags: DWORD;
dwDoubleClick: DWORD;
end;
- dwFlags - битовая маска из следующих флагов:
- DOCHOSTUIFLAG_DIALOG - Запрещает выделение текста в форме
- DOCHOSTUIFLAG_DISABLE_HELP_MENU - Запрещает контекстное меню
- DOCHOSTUIFLAG_NO3DBORDER - Подавляет вывод 3-мерной рамки вокруг компонента
- DOCHOSTUIFLAG_SCROLL_NO - Отключает полосы прокрутки
- DOCHOSTUIFLAG_DISABLE_SCRIPT_INACTIVE - Запрещает исполнение скриптов
- DOCHOSTUIFLAG_OPENNEWWIN - Открывает ссылки в новых окнах
- DOCHOSTUIFLAG_FLAT_SCROLLBAR - Использует плоский стиль для полос прокрутки
- DOCHOSTUIFLAG_DIV_BLOCKDEFAULT - При вводе возврата каретки в режиме редактирования будет использоваться тег , вместо
- DOCHOSTUIFLAG_ACTIVATE_CLIENTHIT_ONLY - Компонент получает фокус только при щелчке мышью в клиентской области окна. При щелчке в не клиентской области (например на полосе прокрутке) компонент фокуса не получает.
- dwDoubleClick задает реакцию на двойной щелчок мышью и может принимать одно из следующих значений:
- DOCHOSTUIDBLCLK_DEFAULT - Выполнять действие «по умолчанию»
- DOCHOSTUIDBLCLK_SHOWPROPERTIES - Показывать окно свойств страницы
- DOCHOSTUIDBLCLK_SHOWCODE DOCHOSTUIFLAG_DIALOG - Показывать HTML-код страницы
Метод должен вернуть S_OK или код ошибки OLE
Например, чтобы создать окно с плоскими полосами прокрутки и без 3-мерной рамки необходимо реализовать этот метод следующим образом:
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.
function TranslateURL(dwTranslate: DWORD; pchURLIn: PWideChar;
var ppchURLOut: PWideChar): HRESULT; stdcall;
Позволяет изменить URL по которому осуществляется загрузка страницы.
- 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;
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 документы и предоставлять их пользователю.
Author: Тенцер А. Л.
ID: 03443