1

昨天在 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 处理程序形式的解决方法。

我的问题是:a) 有没有人知道如何首先避免它或 b) 有没有比设置 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 的调用中,因此推测它可能来自接口的另一侧。我只在第一次调用 .Paint 时得到它。我注意到在这一点上,baseWin 为 GetVisibility 返回 False,因此我的 TForm1.Loaded 中的实验代码,看看这是否会避免它。它不是。

2.a 调用 GeckoBrowser1.LoadURI 后,根据正在加载的 URL,我得到一次或多次“无效浮点操作”。

2.b 同样,根据 URL,我得到:“模块 js3250.dll 中地址 556318B3 的访问冲突。读取地址 00000008。” 或类似的。在某些页面上,它每隔几秒钟发生一次(感谢我想象页面中的一些 JS 计时器代码)。

通过在下面的 TForm1.OnCreate 中调用 Set8087CW 可以避免 2a 和 2b,但我提到它们主要是为了以防有人将它们和 1 一起识别为某种系统性问题的症状,但谷歌也会为其他人找到这个 q谁遇到了这些症状。

回到我的问题 1b),“Safecall 方法中的异常”发生在 StdWndProc-> TWinControl.MainWndProc->[...]->TCustomGeckoBrowser.Paint。除了使用 TApplication.OnException 处理程序之外,有没有办法在调用链中进一步捕获异常,以避免通过在其中放置处理程序来修改 TCustomGeckoBrowser.Paint 的代码?

更新:一条评论提请我注意与 SafeCall 相关的此文档:

当安全调用错误处理程序尚未设置且安全调用例程返回非 0 HResult,或者安全调用错误处理程序未引发异常时,将引发 ESafecallException。如果发生此异常,则 Comobj 单元可能从应用程序的使用列表 (Delphi) 中丢失或未包含在项目源文件 (C++) 中。您可能需要考虑从导致异常的例程中删除 safecall 调用约定。

GeckoBrowser 源带有一个单元 BrowserSupports,它看起来像一个类型库导入单元,只是它似乎是手动准备的。它包含一个接口,该接口包括产生 SafeCall 异常的 Repaint 方法。

  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;

按照 quoyed 文档中的建议,我在 Repaint 成员(但仅限于该成员)上将“safecall”更改为 StdCall,并且,突然!异常停止发生。如果它在接下来的几天内没有再次出现,我会将其作为答案发布,除非有人提出更好的答案。

我的项目代码:

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;
4

1 回答 1

0

查看标题,原型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属性中检索错误代码。

于 2014-09-02T10:32:54.480 回答