Как избежать исключений при использовании TGeckoBrowser в приложении Delphi

Вчера здесь, по предложению aq, я пытаюсь заново ознакомиться с TGeckoBrowser здесь: http://sourceforge.net/p/d-gecko/wiki/Home.

(Примечание: требуется установить пакет Mozilla XulRunner)

Кажется, все изменилось с тех пор, как я в последний раз пытался в эпоху WinXP, так как с минимальным проектом D7 для перехода по URL я получаю ошибки, которые раньше не видел. Я включил свой код ниже. Это ошибки, с которыми я сталкивался при переходе на такие сайты, как www.google.com, news.bbc.co.uk и, конечно, здесь.

  1. Первое исключение - "Исключение в методе 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 имущество.

Другие вопросы по тегам