Application locks while trying to access TWebbrows

2019-01-20 12:47发布

问题:

Edit Narrowed it down to this 1 line,

HTML := wb.OleObject.Document.documentElement.innerHTML;

which consumes the time... how can that be speed up?

Using the following code my application can hang for 1-2 seconds while it tries to access the HTML of a page (Delphi XE).

function Button1Click(Sender : TObject);
begin
   wb.navigate('http://10.0.0.154/stats');
   // Use a timer to poll the page - dont wait and process app messages
   timer1.enabled := true;
end;

procedure Timer1Timer(Sender : TObject);
var
  HTML : WideString;
begin
   If GetHTML(HTML) = true then
   begin
      Timer1.enabled := false;
      { do something }
   end;
end;


function GetHTML(var HTML : WideString) : boolean;
var
  Document : IHTMLDocument2;
begin
  HTML := '';
  Result := false;

  Document := wb.DOcument as IHTMLDocument2;
  If Assigned(Document) then
  begin
    try
      HTML := wb.OleObject.Document.documentElement.innerHTML;
      Result := true;
    except
      Result := false;
    end;
  end;
end;

However I notice in my GetHTML method can take 1-2 seconds to return something and it locks UI. Looking at the AQTime with Delphi XE it says that method call is slow (1-2 seconds). It's sporatic and I wonder if it fails when the page is still mid load.

The page I am loading is an inhouse page, full of javascript and 500k big, I can't use the OnDocumentComplete because it fires before the page is even ready, even if I do a check on the ReadyState it still fires to early.

Anyone able to shed some light, if their a faster way I can access the HTML of TWebbrowser?

回答1:

Please remember that OnDocumentComplete can fire multiple times (frames) when navigating a page.

How to properly implement OnDocumentComplete:

procedure YourForm.OnDocumentComplete(
  Sender: TObject;
  const pDisp: IDispatch;
  var URL: OleVariant);
var
  currentBrowser: IWebBrowser;
  topBrowser: IWebBrowser;
  document: OleVariant;
  windowName: string;
begin
  currentBrowser := pDisp as IWebBrowser;
  topBrowser := (Sender as TWebBrowser).DefaultInterface;
  if currentBrowser = topBrowser then
    ShowMessage('Complete document was loaded')
  else
  begin
    document := currentBrowser.Document;
    windowName := document.ParentWindow.Name;
    ShowMessage(Format('Frame "%s" was loaded', [windowName]));
  end;
end;

source:

http://www.cryer.co.uk/brian/delphi/twebbrowser/twebbrowser_events.htm#OnDocumentComplete



回答2:

Your problem appears to be that you are not allowing TWebBrowser to complete the loading of the page before you try to get the HTML. This is only a guess because you do not show how the code where you call wb.Navigate and you are having to deal with exceptions getting the InnerHTML.

You should try the following:

procedure TForm1.GetHTML(URL: string; var HTML: string);
begin
  wb.Navigate(URL);
  Application.ProcessMessages;
  while wb.Busy do
    Application.ProcessMessages;
  HTML := wb.OleObject.Document.documentElement.innerHTML;
end;


回答3:

As with @crefird's answer, I suspect you are attempting to access the InnerHTML before the browser has completed its work...

If ReadState/Busy are not returning accurate representations of the TWebBrowser's busy state, you can do this:

1) Create either a global variable, or a private member of your form... such as "FBrowserBusy: Boolean" (DON'T FORGET TO INITIALIZE IT TO TRUE JUST PRIOR TO CALLING ".Navigate") 2) As @crefird demonstrated in his answer, use a "while" loop, only substitute "wb.Busy" for "FBrowserBusy". 3) Add an OnDocumentComplete event to your TWebBrowser instance, and have this set FBusy := False;

This will eliminate any collision, and ensure that the TWebBrowser object has completed loading the document before your outer routine proceeds to interrogate it.

Hope you find this helpful!