1

I'm new to automating webpage access, so forgive what is probably a remedial question. I'm using C#/Windows.Forms in a console app. I need to programmatically enter the value of an input on a webpage that I cannot modify and that is running javascript. I have successfully opened the page (triggering WebBrowser.DocumentCompleted). I set browser emulation mode to IE11 (in registry), so scripts run without errors. When DocumentCompleted() triggers, I am unable to access the document elements without first viewing the document content via MessageBox.Show(), which is clearly not acceptable for my unattended app.

What do I need to do so that my document elements are accessbile in an unattended session (so I can remove MessageBox.Show() from the code below)? Details below. Thank you.

The input HTML is:

<input class="input-class" on-keyup="handleKeyPress($key)" type="password">

My DocumentCompleted event handler is:

    private static void LoginPageCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        WebBrowser wb = ((WebBrowser)sender);

        var document = wb.Document;

    // I'm trying to eliminate these 3 lines
        var documentAsIHtmlDocument = (mshtml.IHTMLDocument)document.DomDocument;
        var content = documentAsIHtmlDocument.documentElement.innerHTML;
        MessageBox.Show(content);

        String classname = null;
        foreach (HtmlElement input in document.GetElementsByTagName("input"))
        {
            classname = input.GetAttribute("className");

            if (classname == "input-class")
            {
                input.SetAttribute("value", password);
                break;
            }
        }
   }
5
  • It is possible that when you try to access the document, its completion is only partial. The DocumentCompleted() event can be triggered by IFrames, for example. Try adding a check: if (webBrowser1.ReadyState == WebBrowserReadyState.Complete) { (...) }; before attempting to parse it. Commented Mar 17, 2018 at 3:54
  • @Jimi - Thank you very much for your suggestion. Your suggested check confirms that the page ReadyState is Complete; however, the document elements are still not accessible until after the MessageBox.Show() statement. Commented Mar 17, 2018 at 14:04
  • Try it with the code in the example. I just tested it with a WebForm login page, an the password is correctly inserted in Input container. Commented Mar 17, 2018 at 16:24
  • @Jimi - I'm trying to do this in a ConsoleApp. Is it possible that WebBrowser controls don't work properly in a ConsoleApp? If so, that would explain a bit. Commented Mar 17, 2018 at 21:13
  • I'm going to test this in a Console project. I don't recall any difference in behaviour, but a fresh double check doesn't hurt. Commented Mar 17, 2018 at 21:29

2 Answers 2

1

The problem for me was that the page I'm accessing is being created by javascript. Even though documentComplete event was firing, the page was still not completely rendered. I have successfully processed the first page by waiting for the document elements to be available and if not available, doing Application.DoEvents(); in a loop until they are, so I know now that I'm on the right track.

This SO Question helped me: c# WebBrowser- How can I wait for javascript to finish running that runs when the document has finished loading?

Note that checking for DocumentComplete does not accurately indicate the availability of the document elements on a page generated by javascript. I needed to keep checking for the elements and running Application.DoEvents() until they became available (after the javascript generated them).

Sign up to request clarification or add additional context in comments.

1 Comment

My completed solution using the lessons learned in this thread is posted here for further discussion: stackoverflow.com/questions/49350908/…
0

If the problem comes from the creation of a STAThread, necessary to instantiate the underlying Activex component of WebBrowser control, this is a modified version of Hans Passant's code as shown in the SO Question you linked.

Tested in a Console project.

class Program
{
    static void Main(string[] args)
    {
        NavigateURI(new Uri("[SomeUri]", UriKind.Absolute), "SomePassword");
        Console.ReadLine();
    }

    private static string SomePassword = "SomePassword";

    private static void NavigateURI(Uri url)
    {
        Thread thread = new Thread(() => {
            WebBrowser browser = new WebBrowser();
            browser.DocumentCompleted += browser_DocumentCompleted;
            browser.Navigate(url);
            Application.Run();
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
    }

    protected static void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        WebBrowser browser = ((WebBrowser)sender);
        if (browser.Url == e.Url)
        {
            while (browser.ReadyState != WebBrowserReadyState.Complete)
            { Application.DoEvents(); }

            HtmlDocument Doc = browser.Document;
            if (Doc != null)
            {
                foreach (HtmlElement input in Doc.GetElementsByTagName("input"))
                {
                    if (input.GetAttribute("type") == "password")
                    {
                        input.InnerText = SomePassword;
                        //Or
                        //input.SetAttribute("value", SomePassword);
                        break;
                    }
                }
            }
            Application.ExitThread();
        }
    }
}

11 Comments

Thank you very much for your help. This has gotten more interesting. My code works when I call MessageBox.Show() with any string (doesn't need to be content). My responses to your suggested steps are as follows:
My code is virtually identical to yours with the exception of the MessageBox.Show() and a change in the way I set the password value (I found that I needed to set the InnerText rather than the "value" Attribute). Let me confirm what I have working and post my code. Stay tuned...
@Tony I've posted this code so that, if you use it, we're on the same page on what's going on and it's possible to proceed from there. Setting InnerText or Value is the same thing.
The code you see now, like the previous version, is working (when I wrote "Tested in a Console project" I meant it). You could share the link to the problematic Html page, so it can be analyzed. If you think it could be useful, edit your question and post the relevant code that better describes the problem, stating what's expected and what happens instead, in a defined context. Should you come up with a working solution, post the answer to your own question.
@Tony Well, I'm glad you made it :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.