0

I have written some code to allow me to create an account automatically on a website using C#. Unfortunately the code does not seem to be working as intended as I'm not getting a confirmation e-mail from the site.

Here's my code (i only copied the important parts to make it as objective as possible)

webBrowser.Navigate("https://account.protonvpn.com/signup");
webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser_DocumentCompleted);
MessageBox.Show("end");

private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    if (webBrowser.ReadyState != WebBrowserReadyState.Complete)
        return;

    if (webBrowser.Url.ToString() == "https://account.protonvpn.com/signup")
    {
        buttons = webBrowser.Document.GetElementsByTagName("button");
        foreach (HtmlElement button in buttons)
        {
            if (button.InnerText == "Get Free")
            {
                button.InvokeMember("click");
                MessageBox.Show("clicked");
            }
            else
            {
                MessageBox.Show("not clicked");
            }
        }
        return;
    }
    if (webBrowser.Url.ToString() == "https://account.protonvpn.com/signup/account")
    {
        webBrowser.Document.GetElementById("username").SetAttribute("value", "username");
        webBrowser.Document.GetElementById("password").SetAttribute("value", "password");
        webBrowser.Document.GetElementById("passwordConfirmation").SetAttribute("value", "password");
        webBrowser.Document.GetElementById("email").SetAttribute("value", "email");
        buttons = webBrowser.Document.GetElementsByTagName("button");
        foreach (HtmlElement button in buttons)
        {
            if (button.InnerText == "Create account")
            {
                button.InvokeMember("click");
                MessageBox.Show("account created");
            }
            else
            {
                MessageBox.Show("error");
            }
        }
        return;
    }

    if (webBrowser.Url.ToString() == "https://account.protonvpn.com/signup/verification")
    {
        buttons = webBrowser.Document.GetElementsByTagName("button");
        foreach (HtmlElement button in buttons)
        {
            if (button.InnerText == "Send")
            {
                button.InvokeMember("click");
                MessageBox.Show("account created!");
            }
            else
            {
                MessageBox.Show("error!");
            }
        }
        return;
    }
}

I am also encountering some "script errors" when running my code, not sure how to fix them and not sure if this affects anything when running the code.

Screenshots of script errors

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
w4sted
  • 1
  • 3
  • That code is missing some important parts and is messing up some others. First, set to `true` the `ScriptErrorSuppress` property and apply this: [How can I get the WebBrowser control to show modern contents?](https://stackoverflow.com/a/38514446/7444103). Don't subscribe to a `DocumentCompleted` event inside the handler of another: you don't want that. Check the URL instead. See the notes here: [How to get an HtmlElement value inside Frames/IFrames?](https://stackoverflow.com/a/53218064/7444103). The first line is: `if (webBrowser1.ReadyState != WebBrowserReadyState.Complete) return;`. etc. – Jimi Sep 06 '20 at 00:01
  • @Jimi I am subscribing to a ```DocumentCompleted``` event inside the handler of another because that is how i thought it would be the best way to figure out if the next page has loaded, how do I do it differently (how do i check the URL?)?. I didn't properly understand the IFrames explanation in order to apply it to my code, can you please clarify? – w4sted Sep 06 '20 at 00:28
  • The current document URL you can read it from `WebBrowser.Url`. Don't focus on the Frame/IFrame alone (you could have IFrames there, you may not, you need to handle this anyway), focus on this statement: *Remembering that a Web Page may be composed by multiple Documents contained in Frames/IFrames, we won't be surprised if the event is raised multiple times with a `ReadyState = WebBrowserReadyState.Complete`*. The `DocumentCompleted` event can - and will - be raised multiple times in different cases. You need to be prepared for this. Remove that second handler. – Jimi Sep 06 '20 at 00:39
  • @Jimi When i click the first button I get redirected to a different page. Do I remove the second handler and check if I'm on the right page with ```WebBrowser.Url``` ? Isn't there the possibility that the program will try to fill in the textboxes before the page has finished loading? – w4sted Sep 06 '20 at 00:44
  • @Jimi I think the problem is somewhere else as I am not getting confirmation messages that I'm actually clicking the buttons so I think they are never getting clicked at all and I can't advance to the next page – w4sted Sep 06 '20 at 01:14
  • No, `if ([WebBrowser].ReadyState != WebBrowserReadyState.Complete) return;` takes care of that. Of course, when searching for a HtmlElement, you check whether it's null all the time. -- *I am not getting confirmation messages that I'm actually clicking the buttons so I think they are never getting clicked at all*: with the code you have now, no surprise there. Read again all previous comments, apply all and test. You'll get the hang of it. – Jimi Sep 06 '20 at 01:16
  • Yes, you need different methods that you can call, from the event handler, based on the current URL, since each URL needs its own *special treatment*. You can setup a `List` for this. -- Also, in relation to the event handlers: when you add a new handler to the invocation list, all the previous delegates are also called, not just the last you added. Since the `DownloadCompleted` event is raised multiple times, how many handlers are you actually adding, and how many are then called each time? – Jimi Sep 06 '20 at 01:28
  • @Jimi I'm sorry, I didn't get the question. Can you understand what I'm trying to do by the code? – w4sted Sep 06 '20 at 01:31
  • I don't understand the question. Of course I saw your code (which you should really post here, not on PasteBin - you should have taken [the Tour](https://stackoverflow.com/tour) and visited the [Help Center](https://stackoverflow.com/help)), that's why I'm posting these comments, related to your code. – Jimi Sep 06 '20 at 01:33
  • I meant the updated code. I do know that i should post the code here but i figured that it was too much and would make the page very big. I will update the original post with the updated code, try and take a look at it whenever you can – w4sted Sep 06 '20 at 01:40
  • @Jimi I believe that for some reason my code is failing to find an element by "button" because i tried adding a debug message in the beggining of the foreach loop and the message never showed. The code inside the if statement is running but the for loop isn't – w4sted Sep 06 '20 at 02:03
  • Your code is much better now. You should `break` the loop if you find the HtmlElement you want, or write something like: `var buttonElement = browser.Document.GetElementsByTagName("BUTTON").OfType().FirstOrDefault(elm => elm.InnerText.Equals("Get Free")); buttonElement?.InvokeMember("click");`. BUT, I'm afraid the Web Site you're trying to automate is refusing to let the WebBrowser Control - even updated to IE11 - to operate. It detects the Browser capabilities, it doesn't care about the version you provide in the `Navigate()` overload. You'll have to try with another WebBrowser. – Jimi Sep 06 '20 at 02:03
  • The code is better thanks to you, I wasn't understanding it fully in the beginning but I am now. Well that's not great, how do I try with another WebBrowser? – w4sted Sep 06 '20 at 02:11
  • There are many open source WebBrowsers compatible with .Net desktop applications. CEFSharp, GeckoFx (FireFox), WebView (1 and 2, the new Microsoft Control based on Edge (1) and the new Chrome (2)) etc. Note that, with most WebBrowsers, you have to invoke javascript snippets you write on the fly to automate the Document. There's no `Document.GetElementById` directly on the a Document object, you have to use the corresponding javascript functions and invoke the script. – Jimi Sep 06 '20 at 02:18
  • That seems a bit too much for my knowledge if I have to be honest. I remember I read something about "Selenium", would that work? Also note that I am planning to use this program on an older, outdated computer that is currently running Windows 7. The computer does have .NET Framework installed but how can I make sure that the program that I build is compatible and how would i export it? – w4sted Sep 06 '20 at 02:22
  • Selenium is a WebBrowser automation tool. I cannot say whether it fits your needs. Try CEFSharp and WebView. WebView1 is actually just a viewer, it's incomplete and probably will always be like that. WebView2 is (or, will be) the new WebBrowser Control. But it's tied to .Net 5 and will be fully operational only after .Net 5 is ready. It can be used nonetheless (in a form of *preview*). `GeckoFx` works relatively well, but it has a lot of *quirks*. I suggest CEFSharp. It has its learning curve. – Jimi Sep 06 '20 at 10:06
  • Thanks for all the help, I'll see what I can do. If i do use CEFSharp how do I export the program to another computer? I cant just send the .exe can I? – w4sted Sep 06 '20 at 15:40
  • No. But you can add a Setup Project to the Solution, build an installer and send that *package*. Note that CEFSharp - and all the others, actually, except WebView - *weight* a lot. – Jimi Sep 06 '20 at 15:49
  • What do you mean by weigh a lot? RAM or disk? – w4sted Sep 06 '20 at 16:02
  • *A lot* in both departments (compared to the Controls that make use the Windows-native classes). I was referring specifically to the size and number of files you have to carry over, in this case. – Jimi Sep 06 '20 at 16:04
  • I will be taking a look at it and see what I can do, thanks for all the help during this project! – w4sted Sep 06 '20 at 16:41

0 Answers0