【问题标题】:TWebBrowser: Zoom + "one window mode" incompatibleTWebBrowser:缩放+“单窗口模式”不兼容
【发布时间】:2012-06-28 16:58:33
【问题描述】:

我正在尝试什么:

我需要一个 TWebBrowser,它总是放大 (~140%) 并将所有链接保存在同一个 webbrowser 中(即 _BLANK 链接应该在同一个浏览器控件中打开)。

我是怎么做的:

我已将注册表中的FEATURE_BROWSER_EMULATION 设置为9999,因此网页使用IE9 呈现。我已经确认这是有效的。此外,我正在全新安装的带有 IE9 的 Windows 7 上运行编译后的程序,并通过 Windows Update 进行了全面更新。

缩放

procedure TForm1.WebBrowser1DocumentComplete(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
var
  ZoomFac: OLEVariant;
begin
  ZoomFac := 140;
  WebBrowser1.ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, ZoomFac);
end;

这很好用。

在同一浏览器控件中打开新窗口:

默认情况下,当 TWebBrowser 遇到设置为在新窗口中打开的链接时,它会打开一个新的 IE。我需要它留在我的程序/网络浏览器中。

我在这里尝试了很多东西。这对我有用:

procedure TFormWeb.WebBrowser1NewWindow3(ASender: TObject;
  var ppDisp: IDispatch; var Cancel: WordBool; dwFlags: Cardinal;
  const bstrUrlContext, bstrUrl: WideString);
begin
  Cancel := True;
  WebBrowser1.Navigate(bstrUrl);
end;

我取消了新窗口,而是直接导航到相同的 URL。

Internet 上各种页面上的其他来源建议我不要取消,而是将 ppDisp 设置为各种事物,例如 WebBrowser1.DefaultDispathWebBrowser1.Application 以及它们的变体。这对我不起作用。当我单击 _BLANK 链接时,没有任何反应。这是在两台计算机(Win7 和 IE9)上测试的。我不知道为什么它不起作用,因为这似乎适用于互联网上的其他人。也许这会解决问题?

现在的问题:

当我将这两段代码组合起来时,它会中断!

procedure TForm1.Button1Click(Sender: TObject);
begin
  WebBrowser1.Navigate('http://wbm.dk/test.htm');
  // This is a test page, that I created. It just contains a normal link to google.com
end;

procedure TForm1.WebBrowser1DocumentComplete(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
var
  ZoomFac: OLEVariant;
begin
  ZoomFac := 140;
  WebBrowser1.ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, ZoomFac);
end;

procedure TForm1.WebBrowser1NewWindow3(ASender: TObject; var ppDisp: IDispatch;
  var Cancel: WordBool; dwFlags: Cardinal; const bstrUrlContext,
  bstrUrl: WideString);
begin
  Cancel := True;
  WebBrowser1.Navigate(bstrUrl);
end;

运行时在浏览器中点击链接(无论是正常的还是_BLANK),都会产生这个错误:

First chance exception at $75F1B9BC. Exception class EOleException with message 'Unspecified error'. Process Project1.exe (3288)

如果我删除代码的任何一部分,它就可以工作(显然没有删除的代码)。

谁能帮我让这两个东西同时工作?

感谢您的宝贵时间!

更新:

现在这是正确捕获新窗口并将其保留在同一浏览器控件中的问题。据我所知,OnDocumentComplete 中的缩放代码与它无关。总的来说就是变焦。如果 WebBrowser 控件已被缩放(一次就足够了),NewWindow3 中的代码将失败并显示“未指定错误”。将缩放级别重置为 100% 没有帮助。

通过使用缩放代码 (ExecWB),WebBrowser 中的某些内容“永远”发生了变化,这使得它与 NewWindow3 中的代码不兼容。

有人能看出来吗?

新代码:

procedure TForm1.Button1Click(Sender: TObject);
var
  ZoomFac: OLEVariant;
begin
  ZoomFac := 140;
  WebBrowser1.ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, ZoomFac);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  WebBrowser1.Navigate('http://www.wbm.dk/test.htm');
end;

procedure TForm1.WebBrowser1NewWindow3(ASender: TObject; var ppDisp: IDispatch;
  var Cancel: WordBool; dwFlags: Cardinal; const bstrUrlContext,
  bstrUrl: WideString);
begin
  Cancel := True;
  WebBrowser1.Navigate(bstrUrl);
end;

尝试在单击 Button1 之前和之后单击链接。缩放后失败。

【问题讨论】:

  • 我看到了一些问题:您需要为每个弹出窗口创建新的 webbrowser 实例(考虑选项卡式浏览)。主要问题是 OnDocumentcomplete 事件可以触发多次(例如当页面有框架时),所以它不能执行 execwb 因为它仍然很忙。
  • 我需要创建一个新实例吗?我不能重复使用吗?
  • 你可以,但你为什么会呢?让我感到困惑的一件事是,这是您正在使用的普通 TWebbrowser,我签入了 XE,但我没有 NewWindow3 事件??
  • 我正在为一台 kiosk PC 编程,它需要显示一些不同的网站。对我来说,将所有内容保存在一个实例中是最有意义的。关于事件,我有 2 个不同: WebBrowser1NewWindow2(ASender: TObject; var ppDisp: IDispatch; var Cancel: WordBool);和 WebBrowser1NewWindow3(ASender:TObject;var ppDisp:IDispatch;var Cancel:WordBool;dwFlags:Cardinal;const bstrUrlContext,bstrUrl:WideString);我使用 NewWindow3 因为它给了我 URL。您的任何 NewWindow 事件是否为您提供 URL?
  • 您真的在使用 TWebbrowser(互联网标签)吗?正如我所说,我没有 OnNewWindow3 事件,我使用的版本比你的更高...

标签: delphi delphi-2010


【解决方案1】:

您可以在OnNewWindow2 事件中将ppDisp 设置为IWebBrowser2 实例,例如:

procedure TForm1.Button1Click(Sender: TObject);
begin
  WebBrowser1.Navigate('http://wbm.dk/test.htm');
end;

procedure TForm1.WebBrowser1DocumentComplete(Sender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
var
  ZoomFac: OleVariant;
begin
  // the top-level browser
  if pDisp = TWebBrowser(Sender).ControlInterface then
  begin
    ZoomFac := 140;
    TWebBrowser(Sender).ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, ZoomFac);
  end;
end;

procedure TForm1.WebBrowser1NewWindow2(Sender: TObject;
  var ppDisp: IDispatch; var Cancel: WordBool);
var
  NewWindow: TForm1;
begin
  // ppDisp is nil; this will create a new instance of TForm1:
  NewWindow := TForm1.Create(self);
  NewWindow.Show;
  ppDisp := NewWindow.Webbrowser1.DefaultDispatch;
end;

RegisterAsBrowser设置为true也是suggested by Microsoft
您可以更改此代码以在 Page 控件内的新选项卡中打开 TWebBrowser

我们不能将ppDisp 设置为TWebBrowser当前 实例 - 所以使用这个简单的代码:

ppDisp := WebBrowser1.DefaultDispatch; 无效。

如果我们想维护 UI 流程,我们需要“重新创建”当前/活动的TWebBrowser - 请注意,在以下示例中,TWebBrowser 是动态创建的,例如:

const
  CM_WB_DESTROY = WM_USER + 1;
  OLECMDID_OPTICAL_ZOOM = 63;

type
  TForm1 = class(TForm)
    Button1: TButton;        
    Panel1: TPanel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    function CreateWebBrowser: TWebBrowser;
    procedure WebBrowserDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
    procedure WebBrowserNewWindow2(Sender: TObject; var ppDisp: IDispatch; var Cancel: WordBool);
    procedure CMWebBrowserDestroy(var Message: TMessage); message CM_WB_DESTROY;
  public
    WebBrowser: TWebBrowser;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  WebBrowser := CreateWebBrowser;
end;

function TForm1.CreateWebBrowser: TWebBrowser;
begin
  Result := TWebBrowser.Create(Self);
  TWinControl(Result).Parent := Panel1;
  Result.Align := alClient;
  Result.OnDocumentComplete := WebBrowserDocumentComplete;
  Result.OnNewWindow2 := WebBrowserNewWindow2;
  Result.RegisterAsBrowser := True;
end;

procedure TForm1.WebBrowserDocumentComplete(Sender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
var
  ZoomFac: OleVariant;
begin
  // the top-level browser
  if pDisp = TWebBrowser(Sender).ControlInterface then
  begin
    ZoomFac := 140;
    TWebBrowser(Sender).ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, ZoomFac);
  end;
end;

procedure TForm1.WebBrowserNewWindow2(Sender: TObject; var ppDisp: IDispatch; var Cancel: WordBool);
var
  NewWB: TWebBrowser;
begin
  NewWB := CreateWebBrowser;
  ppDisp := NewWB.DefaultDispatch;
  WebBrowser := NewWB;

  // just in case...
  TWebBrowser(Sender).Stop;
  TWebBrowser(Sender).OnDocumentComplete := nil;
  TWebBrowser(Sender).OnNewWindow2 := nil;

  // post a delayed message to destory the current TWebBrowser
  PostMessage(Self.Handle, CM_WB_DESTROY, Integer(TWebBrowser(Sender)), 0);
end;

procedure TForm1.CMWebBrowserDestroy(var Message: TMessage);
var
  Sender: TObject;
begin
  Sender := TObject(Message.WParam);
  if Assigned(Sender) and (Sender is TWebBrowser) then
    TWebBrowser(Sender).Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  WebBrowser.Navigate('http://wbm.dk/test.htm');
end;

【讨论】:

  • 谢谢。正如您所建议的,我开始认为我需要销毁当前的 TWebBrowser 并创建一个新的。这似乎是一个不必要的额外步骤,但似乎网络浏览器就是为这种方式设计的?
  • 似乎如此......我也同意创建一个新实例并销毁旧实例有点混乱,但如果我想“重用”,这是我让它工作的唯一方法“活动”的 WebBrowser 并保持 UI 流畅。
  • 这似乎行得通!谢谢!如何从代码访问这个新的网络浏览器?是否可以在创建的时候给它起个名字,然后在运行时使用 FindComponent 来查找?
  • 是的,你可以这样做。或者更好地保存一个本地私有变量,或者更好的是,动态创建主 TWebBrowser(在表单创建时),然后重用它。
  • @Michael,我已经编辑了我的答案以演示如何使用动态 TWebBrowser。
【解决方案2】:

我认为问题在于有时 OnDocumentComplete 会在文档加载时触发多次(带有框架的页面)。

Here is the way to implement it properly.

【讨论】:

  • 谢谢,我一定会使用那个代码!似乎更安全。但它仍然为 _BLANK 链接提供“未指定错误”。
猜你喜欢
  • 2018-11-13
  • 2015-03-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-27
  • 2011-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多