4

We have HTML with:

<A target="_blank" href="http://blabla.com">

When the link is clicked the OnNewWindow2 is fired:

procedure TForm1.WebBrowser1NewWindow2(Sender: TObject;
  var ppDisp: IDispatch; var Cancel: WordBool);
begin
  Cancel := True;  
  // open in default browser
end;

I want to open the link in the default browser: Opening TWebBrowser link in default browser

But no BeforeNavigate2is fired in my case.
How can I know the intended URL in a TWebBrowser OnNewWindow2 event?


EDIT: In newer versions of Delphi there is a OnNewWindow3 event which provides the URL I need. currently I'm really struggling to begin to understand how to implement this event into an existing TWebBrowser.
If anyone has done this, it would be great to get some help.

zig
  • 4,524
  • 1
  • 24
  • 68

2 Answers2

4

You can override the InvokeEvent method of the browser and wait there for the DISPID_NEWWINDOW3 dispatch identifier. For an interposer class such OnNewWindow3 event could be implemented like this:

uses
  ActiveX, OleCtrls, SHDocVw;

const
  DISPID_NEWWINDOW3 = 273;

type
  TWebBrowserNewWindow3 = procedure(ASender: TObject; var ppDisp: IDispatch; var Cancel: WordBool;
    dwFlags: Longint; const bstrUrlContext: WideString; const bstrUrl: WideString) of object;

  TWebBrowser = class(SHDocVw.TWebBrowser)
  private
    FOnNewWindow3: TWebBrowserNewWindow3;
  protected
    procedure InvokeEvent(ADispID: TDispID; var AParams: TDispParams); override;
  public
    property OnNewWindow3: TWebBrowserNewWindow3 read FOnNewWindow3 write FOnNewWindow3;
  end;

implementation

procedure TWebBrowser.InvokeEvent(ADispID: TDispID; var AParams: TDispParams);
begin
  if (ADispID = DISPID_NEWWINDOW3) and Assigned(FOnNewWindow3) then
  begin
    FOnNewWindow3(Self, AParams.rgvarg^[4].pdispVal^, AParams.rgvarg^[3].pbool^,
      AParams.rgvarg^[2].lVal, WideString(AParams.rgvarg^[1].bstrVal), WideString(AParams.rgvarg^[0].bstrVal));
  end
  else
    inherited;
end;
TLama
  • 75,147
  • 17
  • 214
  • 392
  • 3
    Very elegant and interesting solution! – Josef Švejk Nov 27 '19 at 18:21
  • 1
    This is almost exactly the code I was writing after you posted your comment, but dealing with the `TDispParams` was just too much for me. well done! :) One question, how did you figured out that the Params are in revers order in the array? – zig Nov 27 '19 at 19:01
  • Well, I've just copy pasted this code from my post on a forum I used to participate :) How did I discover that reverse order (nor exact Delphi version for which that code was for) I can't remember. [P.S. I don't have any Delphi by hand right now] – TLama Nov 27 '19 at 19:43
2

How can I know the intended URL in a TWebBrowser OnNewWindow2 event?

You can use DOM's getAttribute method. But before you should load needed page and after this done replace particular events with your own. See code below:

uses
  ...
  SHDocVw, MSHTML;

type
  TForm1 = class(TForm)
    ...
    // Your auto-generated event handler
    procedure WebBrowser1DocumentComplete(ASender: TObject; 
      const pDisp: IDispatch; const URL: OleVariant);

  private
    // Your self-written event handlers
    procedure New_BeforeNavigate2(ASender: TObject;
      const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData, Headers: OleVariant; 
      var Cancel: WordBool);
    procedure New_NewWindow2(ASender: TObject; var ppDisp: IDispatch; var Cancel: WordBool);
  end;

...

// Assign event handler in design-time
procedure TForm1.WebBrowser1DocumentComplete(ASender: TObject; const pDisp: IDispatch; 
  const URL: OleVariant);
begin
  (ASender as TWebBrowser).OnBeforeNavigate2 := New_BeforeNavigate2;
  (ASender as TWebBrowser).OnNewWindow2 := New_NewWindow2;
end;

procedure TForm1.New_BeforeNavigate2(ASender: TObject; const pDisp: IDispatch; 
  const URL, Flags, TargetFrameName, PostData, Headers: OleVariant; 
  var Cancel: WordBool);
begin
  ShowMessage('New URL will be: ' + URL);
  Cancel := true;
end;

procedure TForm1.New_NewWindow2(ASender: TObject; var ppDisp: IDispatch; var Cancel: WordBool);
var
  S: String;
begin
  S := ((TWebBrowser(ASender).Document as IHTMLDocument2).ActiveElement as IHTMLElement).GetAttribute('href', 0);
  ShowMessage('New window''s URL is: ' + S);
  Cancel := true;
end;

Now you can get URL even with OnNewWindow2 event. For the case target="_self" there is also OnBeforeNavigate2 event handler. It should work if I correctly got your question.

Josef Švejk
  • 1,047
  • 2
  • 12
  • 23
  • 1
    This is indeed a nice hack. I will do some tests to see how consistent it is. – zig Nov 27 '19 at 16:51
  • 1
    This worked for the cases I tested and it is really an original idea! (although I wonder if the `ActiveElement` might be somehow changed from the moment you clicked the link and the moment the event fired...) but I think that @TLama's solution is the ultimate approach. Thanks :) – zig Nov 27 '19 at 19:07