0

I want to automate the following process: I log in to a website. On the search page, enter the information I want to search and click on the search button. The click loads my result. From the resulting page, I select the option to preview the pdf file, and click the button. This click uploads my pdf file.

If I split these commands into buttons, and I press it one by one everything is working fine, but when I want to execute all the commands in a row I get the an error. (I think the error is because WebBrowser does not load everything.) Please help.

procedure TMainForm.Button10Click(Sender: TObject);
var doc :IHTMLDocument2;
    FileStream: TFileStream;
    StreamAdapter: IStream;
    PersistStreamInit: IPersistStreamInit;
    i, a, b : integer;
    sir: string;
    MergiInainte:boolean;'

begin
    MergiInainte:=false;
    WebBrowser1.Navigate('https://website/externallogin.aspx');'

    repeat
      if ((WebBrowser1.ReadyState = READYSTATE_COMPLETE) and (WebBrowser1.Busy = false)) then
        MergiInainte:=true;
    until MergiInainte;'   // This repeat is not working. Dont know why?

    //After webBrowser is fully loaded, the next code has to be run
    doc := WebBrowser1.Document AS IHTMLDocument2;
    WebFormSetFieldValue(doc,0,'txtNume','user');
    WebFormSetFieldValue(doc,0,'txtPwd','psswd');
    WebBrowser1.OleObject.Document.GetElementByID('tibAutentification').click;'

    //after the above code is fully executat, next code is in line
    WebBrowser1.Navigate('https://website/Pages/SearchTitle.aspx');

    //after the above code is executed, next i need to complete form element with what the nexd data
    doc := WebBrowser1.Document AS IHTMLDocument2;
    WebBrowser1.OleObject.Document.GetElementByID('chkTitleNo').click;
    WebFormSetFieldValue(doc,0,'txtTitleNo','28972');   //nr titlu de proprietate
    WebBrowser1.OleObject.Document.GetElementByID('tibSearch').click;

    WebBrowser1.OleObject.Document.GetElementByID('fdgMain:_ctl3:imbView').click;

    WebBrowser1.OleObject.Document.GetElementByID('tibViewPDF').click;  
end;


// But i'm getting error:  Access violation....read of adress 0000000.'

procedure TForm1.WebFormSetFieldValue(const document: IHTMLDocument2; const formNumber: integer; const fieldName, newValue: string) ;
var form : IHTMLFormElement;
    field: IHTMLElement;
begin
  form := WebFormGet(formNumber, WebBrowser1.Document AS IHTMLDocument2) ;
  field := form.Item(fieldName,'') as IHTMLElement;
  if field = nil then Exit;
  if field.tagName = 'INPUT' then (field as IHTMLInputElement).value := newValue;
  if field.tagName = 'SELECT' then (field as IHTMLSelectElement).value := newValue;
  if field.tagName = 'TEXTAREA' then (field as IHTMLTextAreaElement).value := newValue;
end;

  • We readers cannot see your screen. On which **exact** line do your get the AV? Please add this info to your q. – MartynA Apr 11 '20 at 17:59
  • Look at [this](https://stackoverflow.com/a/11890332/12763954) (although I'm not sure why he handles 3 events and not just DocumentComplete). – Olivier Apr 11 '20 at 18:04

1 Answers1

2

Submitting a web form is a navigation, so you have to wait on the ReadyState and Busy properties again after calling tibAutentification.click (asuming that is the webform's submit button). Same with the 2nd Navigate() call. Also, you need to pump the calling thread's message queue while waiting for the browser to be ready so that it can actually process its state changes correctly.

procedure TMainForm.Button10Click(Sender: TObject);
var
  doc: IHTMLDocument2;
  doc3: IHTMLDocument3;
  FileStream: TFileStream;
  StreamAdapter: IStream;
  PersistStreamInit: IPersistStreamInit;
  i, a, b : integer;
  sir: string;

  procedure WaitForReady;
  begin
    while WebBrowser1.ReadyState <> READYSTATE_COMPLETE do
      Application.ProcessMessages;
  end;

begin
  WebBrowser1.Navigate('https://website/externallogin.aspx');

  WaitForReady;
  doc := WebBrowser1.Document as IHTMLDocument2;
  doc3 := doc as IHTMLDocument3;
  WebFormSetFieldValue(doc,0,'txtNume','user');
  WebFormSetFieldValue(doc,0,'txtPwd','psswd');
  doc3.GetElementByID('tibAutentification').click;

  WaitForReady;
  WebBrowser1.Navigate('https://website/Pages/SearchTitle.aspx');

  WaitForReady;
  doc := WebBrowser1.Document as IHTMLDocument2;
  doc3 := doc as IHTMLDocument3;
  doc3.GetElementByID('chkTitleNo').click;
  WebFormSetFieldValue(doc,0,'txtTitleNo','28972');   //nr titlu de proprietate
  doc3.GetElementByID('tibSearch').click;

  WaitForReady; // ?
  doc := WebBrowser1.Document as IHTMLDocument2;
  doc3 := doc as IHTMLDocument3;
  doc3.GetElementByID('fdgMain:_ctl3:imbView').click;

  WaitForReady; // ?
  doc := WebBrowser1.Document as IHTMLDocument2;
  doc3 := doc as IHTMLDocument3;
  doc3.GetElementByID('tibViewPDF').click;  
end;

procedure TForm1.WebFormSetFieldValue(const document: IHTMLDocument2;
  const formNumber: integer; const fieldName, newValue: string);
var
  form : IHTMLFormElement;
  field: IHTMLElement;
begin
  if document = nil then Exit;
  form := WebFormGet(formNumber, document);
  if form = nil then Exit;
  field := form.Item(fieldName,'') as IHTMLElement;
  if field = nil then Exit;
  if field.tagName = 'INPUT' then
    (field as IHTMLInputElement).value := newValue
  else if field.tagName = 'SELECT' then
    (field as IHTMLSelectElement).value := newValue
  else if field.tagName = 'TEXTAREA' then
    (field as IHTMLTextAreaElement).value := newValue;
end;

Because of reentry issues related to pumping the message queue manually, serializing this code is not a good idea, you should use the browser's OnDocumentComplete event instead, let the VCL handle the message processing for you, eg (may require some tweaking):

procedure TMainForm.Button10Click(Sender: TObject);
begin
  WebBrowser1.Navigate('https://website/externallogin.aspx');
  WebBrowser1.Tag := 1;
end;

procedure TMainForm.WebBrowser1DocumentComplete(ASender: TObject;
  const Disp: IDispatch; const URL: OleVariant);
var
  doc: IHTMLDocument2;
  doc3: IHTMLDocument3;
begin
  case WebBrowser1.Tag of
    1: begin
      doc := WebBrowser1.Document as IHTMLDocument2;
      doc3 := doc as IHTMLDocument3;
      WebFormSetFieldValue(doc,0,'txtNume','user');
      WebFormSetFieldValue(doc,0,'txtPwd','psswd');
      doc3.GetElementByID('tibAutentification').click;
      WebBrowser1.Tag := 2;
    end;

    2: begin
      WebBrowser1.Navigate('https://website/Pages/SearchTitle.aspx');
      WebBrowser1.Tag := 3;
    end;

    3: begin
      doc := WebBrowser1.Document as IHTMLDocument2;
      doc3 := doc as IHTMLDocument3;
      doc3.GetElementByID('chkTitleNo').click;
      WebFormSetFieldValue(doc,0,'txtTitleNo','28972');   //nr titlu de proprietate
      doc3.GetElementByID('tibSearch').click;
      WebBrowser1.Tag := 4;
    end;

    4: begin
      doc := WebBrowser1.Document as IHTMLDocument2;
      doc3 := doc as IHTMLDocument3;
      doc3.GetElementByID('fdgMain:_ctl3:imbView').click;
      WebBrowser1.Tag := 5;
    end;

    5: begin
      doc := WebBrowser1.Document as IHTMLDocument2;
      doc3 := doc as IHTMLDocument3;
      doc3.GetElementByID('tibViewPDF').click;
      WebBrowser1.Tag := 0;
    end;
  end;
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Indeed OnDocumentComplete, is the correct choice, watch out though, it is fired multiple times if the webpage has frames (once per frame). – whosrdaddy Apr 11 '20 at 20:45
  • @whosrdaddy that is easy to differentiate, using the `Disp` parameter to see if the completed document belongs to a frame document or the top level document. – Remy Lebeau Apr 11 '20 at 21:13
  • Remy, In know, doing these things for a loong time, just wanted to warn future readers of this culprit :) – whosrdaddy Apr 11 '20 at 21:15
  • WaitForReady; --> is not working. OnDocumentComplete --> is working fine. Thank you. Sometime WebBrowser is showing a MessageBox with ok button, any idea how to capture/ close it automaticaly. – Rotaru Cadastru Apr 12 '20 at 11:10