Selenium webdriver multiple threads - c#

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)
{
}
}
}
}

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.

Related

How to recreate windows form application when main window is closed

I have windows forms app. When it's closed the main window is disposed and then when user click on tray window is recreated - it works. However I have werid problem when I try to bring application back when using FileSystemWatcher. Idea is simple, when file is changed application is brought back. However in this case application appears but hangs and then dissapears. The shape of window comes back but is unresponsive, moving mouse on window shows like app is "thinking" or "hanging", the app doesn't have icon on taskbar.
My guess is that this is connected with threads/synchronization but I have no idea how to make it work again. I tried many different things connected with threading but failed. I cannot create this window again in UI thread because as I understand I can write _mainWindow.BeginInvoke but I can't do that before I create this form.
I have created the minimal working example that demonstrates the issue. It is available at https://gitlab.com/virtual92/getting-forms-up or here:
Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace GettingFormsUp
{
static class Program
{
private static bool hideFlag = true;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var initApplicationContext = new InitApplicationContext();
Console.WriteLine();
var fileSystemWatcher = new FileSystemWatcher(Path.GetFullPath("../.."), "watcher");
fileSystemWatcher.Changed += (sender, args) =>
{
Console.WriteLine("Watched");
initApplicationContext.Show();
};
fileSystemWatcher.EnableRaisingEvents = true;
Application.Run(initApplicationContext);
}
private class InitApplicationContext : ApplicationContext
{
private static MainWindow _mainWindow;
public InitApplicationContext()
{
NewForm();
}
private void NewForm()
{
Console.WriteLine("Creating new MainWindow");
_mainWindow = new MainWindow();
_mainWindow.Invoke((MethodInvoker) delegate
{
_mainWindow.Show();
});
}
public void Show()
{
if (_mainWindow == null || _mainWindow.IsDisposed)
{
NewForm();
}
else if (!_mainWindow.Visible)
{
_mainWindow.BeginInvoke((MethodInvoker) delegate
{
Console.WriteLine("showing");
_mainWindow.Show();
});
}
}
public void Delete()
{
if (_mainWindow != null && !_mainWindow.IsDisposed)
{
_mainWindow.Dispose();
}
}
}
}
}
MainWindow.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace GettingFormsUp
{
public class MainWindow : Form
{
public MainWindow()
{
CreateHandle();
InitializeComponent();
}
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";
}
}
}
How can I make it work?
The problem with your code is that when you create the window again, it's being created in the thread used to raise the FileSystemWatcher.Changed event, which is a background thread, not the thread that the Application.Run() method is using to pump window messages. So that background thread winds up owning the window.
Messages for the window are sent to the thread that owns it, but that thread doesn't have a message pumping loop, so the window never sees the messages. Those messages are critical, as they are what handle everything about the interaction with the window, both user input and everything involved in drawing the window (except for the bare minimum, which the Windows desktop manager handles). Those messages are even used to handle things like calls to Control.Invoke() and Control.BeginInvoke(). Without the message-pumping loop, BeginInvoke() delegates will never be handled, and Invoke() will never even return.
There are a variety of ways to address this. Simply not disposing the window in the first place could be an option (you can override OnFormClosing(), canceling the event and hiding the window instead). Then the call to _mainWindow.Invoke() will always go to the correct thread and work like you expect:
public partial class MainWindow : Form
{
// ...
protected override void OnFormClosing(FormClosingEventArgs e)
{
Visible = false;
e.Cancel = true;
base.OnFormClosing(e);
}
// ...
}
Alternatively, you can capture the main thread's SynchronizationContext object, which can be used to do the same thing as Control.Invoke()/BeginInvoke(). The key to that technique is that until you've created a Winforms component in a thread, that thread won't have a SynchronizationContext. In your code, the form is created when you create the InitApplicationContext object, so capturing the SynchronizationContext after that will work:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var initApplicationContext = new InitApplicationContext();
SynchronizationContext context = SynchronizationContext.Current;
Console.WriteLine();
string path = Path.GetFullPath("../..");
Console.WriteLine($"Watching {path}");
var fileSystemWatcher = new FileSystemWatcher(path, "watcher");
fileSystemWatcher.Changed += (sender, args) =>
{
context.Post(o =>
{
Console.WriteLine("Watched");
initApplicationContext.Show();
}, null);
};
fileSystemWatcher.EnableRaisingEvents = true;
Application.Run(initApplicationContext);
}
If you take this approach, then of course you don't need the call to Control.Invoke() when creating the window. It's superfluous the first time anyway, and because you'll be using SynchronizationContext from the FileSystemWatcher.Changed event handler in the subsequent instances, it's also unneeded then:
private void NewForm()
{
Console.WriteLine("Creating new MainWindow");
_mainWindow = new MainWindow();
_mainWindow.Show();
}

C# Error- Small console app

I'm still trying to get the hang of this. The second part of my Main method will not execute. I believe I've called it correctly. But, obviously I didn't. A little help would be greatly appreciated!
using System;
using static System.Console;
using System.Threading;
namespace mellon_Assignment2
{
class Getting2KnowUapp
{
static void Main()
{
WriteLine("The current time is: " + DateTime.Now);
Thread.Sleep(2000);
AboutMe Me = new AboutMe();
}
}
}
using System;
using static System.Console;
using System.Threading;
namespace mellon_Assignment2
{
class AboutMe
{
public void DisplayInfo()
{
WriteLine("My Name\tAssignment 2");
Thread.Sleep(1500);
WriteLine("ITDEV110\tIntro to Object-oriented Programming");
Thread.Sleep(1500);
WriteLine("Professor\tSeptember 18th");
Thread.Sleep(1500);
}
}
}
You need to call DisplaInfo method. You are only creating the object and doing nothing with it:
AboutMe Me = new AboutMe();
Me.DisplayInfo();
Echoing the other replies, you aren't invoking the method on your class.
If you want it to occur when you create a new instance, you could move it into the constructor.
To do that, change:
public void DisplayInfo()
to
public AboutMe()
public void DisplayInfo() is it's own method and has to be called directly after initialization of the class AboutMe.
If you want the DisplayInfo() method to fire immediately upon initialization of AboutMe then simply add a constructor for AboutMe like so.
class AboutMe {
public AboutMe() {
DisplayInfo();
}
public void DisplayInfo() {
...
}
}
Then you can call:
AboutMe myvariable = new AboutMe();

C# - Quitting Selenium IWebDriver from Windows Form application

I am sorry if this is a dumb question. But I am a C# noob. I am trying to make a program where the user can selected which browser is used to execute a Selenium script.
I can successfully select my desired browser, hit the START button, and launch the correct browser. The program then performs a simple test action by navigating to a page.
What I want also want to be able to do is hit a STOP button and have the browser close. I have tried messing with the get; set; feature. But I don't think I have it quite right. Please note that I had removed my attempt at a get; set; configuration for the IWebDriver because it simply was not working, and I didn't want to confuse anyone.
Thanks
Here is my Windows Form:`
namespace WindowsFormsApplication1
{
public partial class MainWindow : Form
{
public MainWindow()
{
InitializeComponent();
MyInitializeComponent();
}
private readonly Browser form;
public MainWindow(Browser form)
{
this.form = form;
}
//Initiate variables
public string selectedBrowser;
public string selBrowser
{
get { return comboBoxBrowser.Text; }
}
private void MyInitializeComponent()
{
//default values here
}
private void buttonStart_Click(object sender, EventArgs e)
{
//Launch Browser
Browser.ChooseDriver(selBrowser);
}
private void buttonStop_Click(object sender, EventArgs e)
{
//Perform the Quit operation on the Silenium driver
}
}
}
Here is my Program.cs:
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenQA.Selenium;
using OpenQA.Selenium.Edge;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.UI;
namespace WindowsFormsApplication1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainWindow());
}
}
public class Browser
{
private IWebDriver _driver_;
public IWebDriver driver
{
get { return _driver_; }
set { _driver_ = driver; }
}
public static void ChooseDriver(string selBrowser)
{
//Run this if IE was selected
if (selBrowser == "Internet Explorer")
{
RemoteWebDriver browserdriver = null;
string serverPath = "Microsoft Web Driver";
if (System.Environment.Is64BitOperatingSystem)
{
serverPath = Path.Combine(System.Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), serverPath);
}
else
{
serverPath = Path.Combine(System.Environment.ExpandEnvironmentVariables("%ProgramFiles%"), serverPath);
}
InternetExplorerOptions options = new InternetExplorerOptions();
options.PageLoadStrategy = InternetExplorerPageLoadStrategy.Eager;
browserdriver = new InternetExplorerDriver(serverPath, options);
Launch(browserdriver);
}
}
public static void Launch(RemoteWebDriver _driver)
{
//Set Page load timeout to 5 seconds
_driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromSeconds(5));
//Navigate to URL
_driver.Url = "http://www.outlook.com/";
}
}
}
In case you have to separate code of the Form and the test, you can use these 2 options:
Use process.Kill()
try
{
foreach (Process proc in Process.GetProcessesByName("chromedriver.exe"))
{
proc.Kill();
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
Launch cmd taskkill
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C taskkill /f /fi \"pid gt 0\" /im chromedriver.exe";
process.StartInfo = startInfo;
process.Start();
Thank you John for your help on this. I was able to get all of my code running successfully within the form class. it was still bugging me that I could not figure out how to solve my original goal. So I kept looking for more examples and finally found the part that I was missing.
I was not instantiating the Browser class from within the Form class.
private void buttonStart_Click(object sender, EventArgs e)
{
Browser _browser = new Browser();
_browser.GetBrowser(selBrowser);
}
I know it probably sounds like a simple mistake for someone to make, and easy to correct. But I've only been learning C# for a week or two, and the login behind doing that escaped me. Perhaps it still don't fully understand why or what i'm doing with this solution. But at least I can move forward.
And thanks to you as well Buaban. I will need that taskkill snippet to kill the console window that opens every time a selenium driver is initiated.

Windows form loads then quits

I'm creating a checkout system for a supermarket. It consists of a checkout, server and MIS program an operates WCF services between them. The problem I have is that the checkout program, which is a windows form, does a few neccessaries in it's application_load method and then just quits.
Here's the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CheckoutLibrary;
using Checkout.ServerLibraryService;
using Checkout.MarketService;
namespace Checkout
{
public partial class theForm : Form
{
private static int checkoutID = 3;
private Product[] allProducts;
public theForm()
{
InitializeComponent();
}
private void theForm_Load(object sender, EventArgs e)
{
// First cache all products
SupermarketServiceSoapClient marketService = new SupermarketServiceSoapClient();
allProducts = marketService.GetAllProducts();
// Load the service provided by the server
ServiceClient serverService = new ServiceClient();
// Load the event handlers for the bar code scanner
BarcodeScanner scanner = new BarcodeScanner();
scanner.ItemScanned += new BarcodeScanner.ItemScannedHandler(scanner_ItemScanned);
scanner.AllItemsScanned += new BarcodeScanner.AllItemsScannedHandler(scanner_AllItemsScanned);
scanner.Start(checkoutID);
}
void scanner_AllItemsScanned(EventArgs args)
{
throw new NotImplementedException();
}
void scanner_ItemScanned(ScanEventArgs args)
{
itemTextBox.Text = "Scanned " + GetItemName(args.Barcode);
}
private void scanItemButton_Click(object sender, EventArgs e)
{
scanner_ItemScanned(new ScanEventArgs(GetRandBarcode()));
}
// A barcode -> product name look up method
public string GetItemName(int barcode)
{
return allProducts[barcode].Description + " # " + allProducts[barcode].Price;
}
// Method to grab a random barcode for simulation
private int GetRandBarcode()
{
Random rand = new Random();
return rand.Next(0,500);
}
}
}
And program.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace Checkout
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new theForm());
}
}
}
Thanks for any insight.
In WinForms, if your form_load throws an exception, it quits without displaying anything. Annoying, but I'm guessing that's the problem.
You can try a try/catch, or you can hit CTRL+ALT+E and check the Thrown Column for Common Language Runtime Exceptions to see the error.
UPDATE:
Based on comments, here's a sample way to execute something on another thread.
ThreadStart ts = new ThreadStart(() => {
try {
scanner.Start(checkoutID);
} catch {
// Log error
}
});
Thread t = new Thread(ts);
t.Start();

Undesired termination of Thread created in Timer callback

This is what I want to do:
Have a timer with some interval
In the timer callback code, if some condition is met, another thread should be run
I’ve put my code in a class which is instantiated by the main form and the code is executed upon method call (‘StartSync()’, se sample code).
The problem is that the code runs for a couple of seconds but then terminates. I suppose I’m doing something stupid but I really can’t see what it is. Thankful for any help with regards to this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
class Syncer
{
static bool SYNC_IN_PROGRESS;
public void StartSync()
{
SYNC_IN_PROGRESS = false;
Timer timer = new Timer(timerCallback, null, 0, 1000);
}
public void timerCallback(Object stateInfo)
{
Debug.WriteLine("Sync?");
if (!SYNC_IN_PROGRESS)
{
SYNC_IN_PROGRESS = true;
Thread thSync = new Thread(new ThreadStart(sync));
thSync.Start();
}
}
void sync()
{
Debug.WriteLine("Syncing...");
SYNC_IN_PROGRESS = false;
}
}
}
At a guess, the Timer is only held in a method variable; it sounds to me like the Timer is getting garbage collected and finalized, hence terminated. I suspect you should hold onto that reference in a field to prevent collection.
As an aside - I doubt it is the cause here, but when dealing with threading you should be religiously aware of access to shared state from multiple threads; for example:
using Monitor (aka lock)
appropriate use of volatile
Interlocked when it fits
Your current access to the static bool will probably work OK, but...
Try this cleaner approach
static volatile bool SYNC_IN_PROGRESS;
static thread syncPoll;
public void StartSync()
{
SYNC_IN_PROGRESS = false;
syncPoll = new Thread(sync);
syncPoll.Start();
}
void sync()
{
while (true)
{
Debug.WriteLine("Sync?");
if (SYNC_IN_PROGRESS) Debug.WriteLine("Syncing...");
Thread.Sleep(1000);
}
}
It does the same you try to do with your current code :) but doesn't use a timer
So here is what I did and it seems to work just fine
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
StartSync();
}
static bool SYNC_IN_PROGRESS;
public void StartSync()
{
SYNC_IN_PROGRESS = false;
System.Threading.Timer timer = new System.Threading.Timer(timerCallback, SYNC_IN_PROGRESS, 0, 1000);
}
public void timerCallback(Object stateInfo)
{
Debug.WriteLine("Sync?");
if (!(bool)stateInfo)
{
SYNC_IN_PROGRESS = true;
Thread thSync = new Thread(new ThreadStart(sync));
thSync.Start();
}
}
void sync()
{
Debug.WriteLine("Syncing...");
SYNC_IN_PROGRESS = false;
}
}

Categories

Resources