1

So I'm making some automated testing for some web apis. I'm trying to see if I can use System.Windows.Forms.WebBrowser class to navigate to a page, enter some text, and press a button, all from the console. I decided to use wikipedia for my test, because entering text into the search bar and hitting search is pretty much the same thing I'm trying to do.

I used code from this question to start off.

The problem I'm having is that I can't quite seem to chain the two actions together, 1) navigate to wikipedia, 2) Enter text into the search box and hit go. It navigates to wikipedia alright. I was told to try using state machines, but I don't quite know how that's supposed to look. As it is, when it comes back to the runBrowser method for the second time, the line br.Document.GetElementByID("searchInput") throws an InvalidCastException. So I have not the foggiest what's going on there. I assume something to do with creating a new thread while the old one is running, but when I kill the old one, it also seems to kill the WebBrowser and throws an error saying "Disconnected Context"

Here's my code:

static WebBrowser br = new WebBrowser();

static void runBrowserThread()
{

        var th = new Thread(() =>
        {

            br.DocumentCompleted += browser_DocumentCompleted;
            if (state == 0)
            {
                br.Navigate(new Uri("http://en.wikipedia.org/wiki/Main_Page"));
                state++;
            }
            else if (state == 1)
            {

                if (br.Document.GetElementById("searchInput") != null)
                {
                    HtmlElement search = br.Document.GetElementById("searchInput");
                    search.SetAttribute("value", searchterm);
                    foreach (HtmlElement ele in search.Parent.Children)
                    {
                        if (ele.TagName.ToLower() == "input" && ele.Name.ToLower() == "go")
                        {
                            ele.InvokeMember("click");
                            break;
                        }
                    }
                }
                else
                    Console.WriteLine("Meep");
                state++;
            }

            Application.Run();
        });
        th.SetApartmentState(ApartmentState.STA);
        th.Start();       
}

static void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    // var br = sender as WebBrowser;
    if (br.Url == e.Url)
    {
        Console.WriteLine("Natigated to {0}", e.Url);

    }
    HtmlElement search = br.Document.GetElementById("searchInput");
    Console.WriteLine(search.InnerHtml);
    Console.ReadLine();
    // Application.ExitThread();   // Stops the thread
    runBrowserThread();
    return;
}

Any idea on how to arrange this code to make the threading stuff play nice would be really appreciated.

Community
  • 1
  • 1
NathanTempelman
  • 1,301
  • 9
  • 34

1 Answers1

2

No, you are doing this fundamentally wrong. It bombs because you started a new thread, never do that. You should check the state in the DocumentCompleted event instead. So, roughly:

    var th = new Thread(() =>
    {
        state = 0;
        br.Navigate(new Uri("http://en.wikipedia.org/wiki/Main_Page"));
        Application.Run();
    }

And

void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    var br = sender as WebBrowser;
    if (state == 0) {
       // You are now on the main page, set the searchInput and submit click
       //...
       state = 1;
    }
    else if (state == 1) {
       // You are now on the searched page, do whatever you need to do
       //...
       // And if you are done:
       Application.ExitThread();
    }
}

Add additional states if you visit more pages.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536