1

I have a winforms project that whenever a button is clicked, it calls another class which uses selenium. I am trying to make it so I could run 1 - however many windows I want running. So, I made it so that whenever the button is clicked it makes a new thread and calls a method that calls the other class. The issue is whenever I do multiple windows, it appears that only one window is doing the action. So if I want two windows to try and select a country, only one will select a country. Both windows however, open the url. I am really confused how or why this is happening, I initialize a new webdriver in the constructor so I don't understand why it doesn't seem to do anything else.

using System;
using System.Threading;
using System.Windows.Forms;

namespace MRE
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(new ThreadStart(generate));
            t.IsBackground = true;
            t.Start();
           // if (!InvokeRequired)
           // {
           //     Invoke((Action)generate);
          //  }
        }

        private void generate()
        {
            
            Class1 generator = new Class1();
            generator.start();
        }
    }
}
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System;
using System.Threading;

namespace MRE
{
    class Class1
    {
        public static IWebDriver driver;
        public static WebDriverWait wait;


        public Class1()
        {

            driver = new ChromeDriver();
            wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20));


        }

        public void start()
        {
            try
            {
                driver.Navigate().GoToUrl("your url");

                //Input DOB


                SelectElement oSelect = new SelectElement(driver.FindElement(By.Id("capture-country")));
                Thread.Sleep(2000);
                oSelect.SelectByValue("GBR");
                //click
                wait.Until(ExpectedConditions.ElementToBeClickable(By.Id("dob-field-inactive"))).Click();


                //enter Month
                wait.Until(ExpectedConditions.ElementIsVisible(By.Name("dob-month"))).SendKeys("01");
                Thread.Sleep(1000);
                //enter Day
                wait.Until(ExpectedConditions.ElementIsVisible(By.Name("dob-day"))).SendKeys("01");
                Thread.Sleep(1000);
                //enter Year
                wait.Until(ExpectedConditions.ElementIsVisible(By.Name("dob-year"))).SendKeys("2000");


            }

            catch (WebDriverException e)
            {
            }
        }
    }
}
  • You cannot update the GUI thread from another thread. Are you trying to make more than one GUI thread (which is much more difficult than just starting a new thread)? `Application.DoEvents();` is a big code smell the design is foobar. Note that both `[STAThread]` (COM promise) and `Application.Run` are used to properly create the initial WinForms GUI thread (message pump and all). – Zer0 Aug 09 '21 at 01:57
  • Well the main reason I added the thread was to make it so when Selenium opens chrome they can still use the GUI. The issue though is when you click the button twice, it opens two chrome windows and takes you to the URL on both, but only one chrome driver will select the location and the other just runs into an error. My issue isn't with the GUI. – Tanner Ensign Aug 09 '21 at 02:19
  • Well you need to fix `generateGoogle` first. It's accessing GUI components. You can make those thread-safe by marshalling over to the GUI thread (Invoke, BeginInvoke, InvokeRequired). Also you mentioned an error. What's the error? Aside, I also don't like the `threader` method for various reasons. Let me re-read your problem see if I can find a better solution. – Zer0 Aug 09 '21 at 02:21
  • A thread-safe way to access the GUI is by checking `InvokeRequired`, and if that's true, then using `Invoke` or `BeginInvoke` to get over to the GUI thread. `Invoke` will block until it's complete, so you can use that from your background google threads to access GUI elements. Does that solve your problem? – Zer0 Aug 09 '21 at 02:24
  • The error it throws is the element isn't interactable. I catch it though so the driver doesn't crashed, but the thing is I know it is interactable because the other chrome window is able to interact with the selection item. I also am trying to understanding threading more. It sounds like the current threading method I am using is mostly a thread towards the GUI? Thank you for your help. – Tanner Ensign Aug 09 '21 at 02:26
  • So anything owned by the GUI (any class that derives from `Control` included) should only ever be accessed by the GUI thread. Say you want to read/write from another thread. Here's example code: `private void SomeMethod() { if (InvokeRequired) { Invoke((Action)SomeMethod); return; } //Access all GUI stuff here safely now }`. Basically that reads "Is this method on the GUI thread? Good, run normally. If not, Invoke on GUI thread". This method can be safely called by background threads. Wrap anything that accesses GUI elements like that (marshal to UI thread). – Zer0 Aug 09 '21 at 02:30
  • I have updated the code above in threader. The issue is now, the gui freezes once I click the generate button and I can only have one chrome option open. – Tanner Ensign Aug 09 '21 at 02:36
  • Does this answer your question? [How do I update the GUI from another thread?](https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread) also see [here](https://stackoverflow.com/questions/30808606/can-selenium-use-multi-threading-in-one-browser). – Zer0 Aug 09 '21 at 02:39
  • Kinda. It did fix the threading issue that you had mentioned earlier, but I still have the issue with selenium. The selenium issue, which is my main issue, is that having two selenium threads open only one can do the actions. I will add a code example above. – Tanner Ensign Aug 09 '21 at 02:45
  • If you could add the smallest amount of complete running code that would allow us reproduce the issue that would be great. Then we can debug on our end. – Zer0 Aug 09 '21 at 02:47
  • I added some code above. I added the constructor, my method which opens chrome, and my method where the error is caught. – Tanner Ensign Aug 09 '21 at 02:50
  • I don't see anything in your code that makes more than one instance. Can you take the time to create a simple fully reproducible example? Code we can copy and run ourselves? Also if you get an error, please post the full exception with call stack. It helps. – Zer0 Aug 09 '21 at 02:59
  • I added some code that should show what the issue is for me. – Tanner Ensign Aug 09 '21 at 03:11
  • In order to see what I am talking about. Click the gen start button twice or more in a row. The second chrome window will not execute anything other than navigate the page selected – Tanner Ensign Aug 09 '21 at 03:12
  • Thanks, but that isn't code that someone else can run. Please try creating an [MRE](https://stackoverflow.com/help/minimal-reproducible-example). That code does have GUI threading issues again, as I see controls used from multiple threads. – Zer0 Aug 09 '21 at 03:14
  • My apologies I have never done an MRE. I just put an example together above that has the same issue I am having in my actual code. You will need to import the selenium nuget package along with chromedriver. I also commented out the example I was doing before with the `InvokeRequired` so you could see the issue I was having with that. I want to be able to click the button as many times and have chrome open a new window each time and execute those actions. Currently only one window executes the actions. – Tanner Ensign Aug 09 '21 at 03:37
  • Retracted close vote. Thanks for the MRE. I'll give it a shot myself, but I'm sure others can also repro and you'll get good answers too now. Appreciate it. – Zer0 Aug 09 '21 at 03:40
  • Thank you for your help! Let me know if you find anything. – Tanner Ensign Aug 09 '21 at 04:38
  • After doing more research, it seems that what I am trying to do is multithread selenium in parallel . Most videos I have watched are in java and they use `ThreadLocal`. – Tanner Ensign Aug 09 '21 at 05:32
  • How does the `SendKeys` calls work? It sounds like the keys are being sent to the wrong form. – Enigmativity Aug 09 '21 at 05:49
  • Yes, so what it seems like is, when running two instances of Class1 the driver instance gets mixed up and seems to send keys to the second chrome window opened. I am messing with `ThreadLocal drive = new ThreadLocal();` currently, but cannot seem to figure out how to work it. – Tanner Ensign Aug 09 '21 at 05:52
  • @TannerEnsign - `SendKeys` typically push the keys to the OS so that it thinks that the actual keys on the keyboard are being pressed. That means only the screen/form in focus can receive the keys. `SendKeys` is usually a design of last resort. – Enigmativity Aug 09 '21 at 23:39
  • So I guess the other option would be having to use javascriptexectuor. – Tanner Ensign Aug 10 '21 at 01:46

1 Answers1

0

I actually found one way to solve this problem. Instead of initializing the Webdriver as a class variable, I made it so it was a local variable to the start() method. It is not shown in my MRE, but in my actual class I have different methods that use the driver, so I made it so the parameters for any methods I called included IWebDriver as a parameter. Therefore there wasn't one instance of the webdriver running in multiple windows. If there is another way around this please let me know.