Как избежать исключений при использовании TGeckoBrowser в приложении Delphi
Вчера здесь, по предложению aq, я пытаюсь заново ознакомиться с TGeckoBrowser здесь: http://sourceforge.net/p/d-gecko/wiki/Home.
(Примечание: требуется установить пакет Mozilla XulRunner)
Кажется, все изменилось с тех пор, как я в последний раз пытался в эпоху WinXP, так как с минимальным проектом D7 для перехода по URL я получаю ошибки, которые раньше не видел. Я включил свой код ниже. Это ошибки, с которыми я сталкивался при переходе на такие сайты, как www.google.com, news.bbc.co.uk и, конечно, здесь.
- Первое исключение - "Исключение в методе Safecall" - возникает при первом отображении моей формы, прежде чем вообще перемещаться в любом месте. У меня есть обходной путь в виде обработчика TApplication.OnException.
Мой вопрос: а) Кто-нибудь знает, как избежать этого, во-первых, или б) Есть ли более аккуратный способ поймать его, чем установить обработчик TApplication.Exception, который всегда кажется мне чем-то вроде признания поражения? (Я имею в виду наличие одного, чтобы пользователь не увидел исключение, вообще не имея обработчика всего приложения).
Это исключение происходит в этом коде:
procedure TCustomGeckoBrowser.Paint;
var
rc: TRect;
baseWin: nsIBaseWindow;
begin
if csDesigning in ComponentState then
begin
rc := ClientRect;
Canvas.FillRect(rc);
end else
begin
baseWin := FWebBrowser as nsIBaseWindow;
baseWin.Repaint(True);
end;
inherited;
end;
в вызове baseWin.Repaint, так что, предположительно, это происходит с другой стороны интерфейса. Я получаю это только в первый раз. Пейнт называется. Я заметил, что в этот момент baseWin возвращает False для GetVisibility, отсюда и экспериментальный код в моем TForm1.Loaded, чтобы увидеть, если этого избежать. Это не.
2.a После вызова GeckoBrowser1.LoadURI я получаю "Недопустимая операция с плавающей запятой" один или несколько раз в зависимости от загружаемого URL.
2.b Опять же, в зависимости от URL, я получаю: "Нарушение доступа по адресу 556318B3 в модуле js3250.dll. Чтение адреса 00000008." или похожие. На некоторых страницах это происходит каждые несколько секунд (спасибо, что я представляю код таймера JS на странице).
2a и 2b избегаются вызовом Set8087CW в TForm1.OnCreate ниже, но я упоминаю их главным образом в том случае, если кто-то их распознает, и 1 вместе указывают на наличие какой-либо системной проблемы, но также и для того, чтобы Google нашел этот q для других которые сталкиваются с этими симптомами.
Возвращаясь к моему q 1b), "Исключение в методе Safecall" происходит из StdWndProc-> TWinControl.MainWndProc->[...]->TCustomGeckoBrowser.Paint. Вместо использования обработчика TApplication.OnException, есть ли способ перехватить исключение дальше по цепочке вызовов, чтобы избежать изменения кода TCustomGeckoBrowser.Paint, помещая туда обработчик?
Обновление: комментарий привлек мое внимание к этой документации, касающейся SafeCall:
ESafecallException вызывается, когда обработчик ошибок safecall не был настроен, а подпрограмма safecall возвращает ненулевой результат HResult или если обработчик ошибок safecall не вызывает исключение. Если возникает это исключение, модуль Comobj, вероятно, отсутствует в списке использований приложения (Delphi) или не включен в исходный файл проекта (C++). Возможно, вы захотите удалить соглашение о вызовах safecall из процедуры, которая привела к исключению.
Исходный код GeckoBrowser поставляется с модулем BrowserSupports, который выглядит как модуль импорта библиотеки типов, за исключением того, что кажется, что он был подготовлен вручную. Он содержит интерфейс, который включает метод Repaint, который создает исключение SafeCall.
nsIBaseWindow = interface(nsISupports)
['{046bc8a0-8015-11d3-af70-00a024ffc08c}']
procedure InitWindow(parentNativeWindow: nativeWindow; parentWidget: nsIWidget; x: PRInt32; y: PRInt32; cx: PRInt32; cy: PRInt32); safecall;
procedure Create(); safecall;
procedure Destroy(); safecall;
[...]
procedure Repaint(force: PRBool); safecall;
[...]
end;
Следуя предложению в соответствующей документации, я изменил th "safecall" на StdCall на члене Repaint (но только на этом участнике) и, presto!, исключение прекратилось. Если он не появится в ближайшие пару дней, я опубликую это как ответ, если кто-то не придумал лучшего.
Код моего проекта:
uses
BrowserSupports;
procedure TForm1.FormCreate(Sender: TObject);
begin
Set8087CW($133F);
Application.OnException := HandleException;
end;
procedure TForm1.HandleException(Sender: TObject; E: Exception);
begin
Inc(Errors);
Caption := Format('Errors %d, msg: %s', [Errors, E.Message]);
Screen.Cursor := crDefault;
end;
type
TMyGeckoBrowser = class(TGeckoBrowser);
procedure TForm1.Loaded;
begin
inherited;
GeckoBrowser1.HandleNeeded;
(TMyGeckoBrowser(GeckoBrowser1).WebBrowser as nsIBaseWindow).SetVisibility(True);
end;
procedure TForm1.btnLoadUrlClick(Sender: TObject);
begin
try
GeckoBrowser1.LoadURI(edUrl.Text);
except
end;
end;
1 ответ
Глядя на заголовки, прототип для Repaint
эффективно следующим образом:
HRESULT __stdcall Repaint(PRBool force);
а это значит что
procedure Repaint(force: PRBool); safecall;
разумная декларация Помни что safecall
выполняет перезапись параметров для преобразования кодов ошибок COM в исключения.
Это означает, что если вызов Repaint
возвращает значение, которое указывает на ошибку, тогда механизм безопасного вызова будет отображать это как исключение. Если вы хотите игнорировать это конкретное исключение, тогда лучше сделать это в источнике:
try
baseWin.Repaint(True);
except
on EOleException do
; // ignore
end;
Если вы хотите избежать исключений, вы можете переключиться на stdcall
, но вы должны помнить, чтобы отменить перезапись параметра.
function Repaint(force: PRBool): HRESULT; stdcall;
Теперь вы можете написать это так:
if Failed(baseWin.Repaint(True)) then
; // handle the error if you really wish to, or just ignore it
Обратите внимание, что Failed
определяется в ActiveX
Блок.
Если вы хотите устранить проблему дальше, вы можете посмотреть на код ошибки:
var
hres: HRESULT;
....
hres := baseWin.Repaint(True);
// examine hres
Или, если вы собираетесь оставить функцию как безопасный вызов, вы можете получить код ошибки из EOleException
экземпляра ErrorCode
имущество.