Deadlock when creating WPF window in a thread - c#

I am creating a new window in a thread in .Net Framework 4.8 WPF application.
Most of time it works. However sometimes it hangs even when the app starts.
The dispatcher in the main thread and the dispatcher in the new thread wait each other.
You can refer to the following window to check threads.
The following is the entire .cs code to reproduce the phenomenon. 'NewWindow' is a class derived from 'System.Windows.Window' and it does not have to have anything in it to reproduce the phenomenon(For clarification I also put code for 'NewWindow' at the end of this question).
using System;
using System.Threading;
using System.Windows;
using System.Windows.Interop;
namespace WindowCreateTest
{
public partial class MainWindow : Window
{
NewWindow newWindow = null;
Thread uiThread = null;
public MainWindow()
{
InitializeComponent();
}
public void ShowNewWindow(IntPtr handle)
{
uiThread = new Thread(() =>
{
Thread.CurrentThread.Name = "ShowNewWindow Thread";
if (newWindow == null)
newWindow = new NewWindow();
WindowInteropHelper helper = new WindowInteropHelper(newWindow);
helper.Owner = handle;
newWindow.Show();
System.Windows.Threading.Dispatcher.Run();
});
uiThread.SetApartmentState(ApartmentState.STA);
uiThread.Start();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var h = new WindowInteropHelper(this).Handle;
ShowNewWindow(h);
}
}
}
You might wonder why I'm creating a new window in a newly created thread not in a main thread. The reason is that, this code is used in MAF(Managed Add-ins and Extensibility Framework or System.AddIn) dlls which is activated in an external process.
WindowInteropHelper helper = new WindowInteropHelper(newWindow);
helper.Owner = handle;
The reason I put the above two lines is
to make 'NewWindow' minimized when I minimize 'MainWindow'
to make 'NewWindow' always be placed over 'MainWindow'.
And I guess this is the root cause of the deadlock.
using System.Windows;
namespace WindowCreateTest
{
public partial class NewWindow : Window
{
public NewWindow()
{
InitializeComponent();
}
}
}
Thank you for reading and any advice will be welcomed and appreciated.

Related

Selenium webdriver multiple threads

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.

How can I instantiate windows form objects for multiple clients in C #

I tried to instantiate an object of the WebBrowser class to make a query on a page and return a result but an error is generated:
An instance of the ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be created because the Current thread is not in a unprocessed container.
I have tried to implement this in different ways but I do not achieve the desired result.
Here is my code
// At beginig of class Form
public delegate void DataRecieved(ConexionTcp conexionTcp, string data);
public event DataRecieved OnDataRecieved;
private void Form1_Load(object sender, EventArgs e)
{
OnDataRecieved += MensajeRecibido;
}
private void MensajeRecibido(ConexionTcp conexionTcp, string datos)
{
WebBrowser myweb= new WebBrowser();
myweb.Navigate("http://localhost/Php/Final3");
myweb.Document.GetElementById("user").InnerText = "user";
myweb.Document.GetElementById("pass").InnerText = "pass";
myweb.Document.GetElementById("Encode").InvokeMember("Click");
if ("resultado" == myweb.Document.GetElementById("pass_sha_hash").InnerText)
{
textbox1.Text="Completado";
}
}
Can anyone find out what am i doing wrong?
Looks like you are trying to access a Windows Form Control from a thread.
Accessing Windows Form Controls from another thread is not safe. So you are getting this error.
Please refer this document for more details: https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls
You can use Background Threads to do your operation. Otherwise marking the thread's apartment state to STA might help.
In the below example, I have used STA thread, otherwise, I will get a similar error.
public partial class Form1 : Form
{
Thread t;
public delegate void DataRecieved();
public event DataRecieved OnDataRecieved;
public Form1()
{
InitializeComponent();
t = new Thread(new ThreadStart(this.TriggerEvent));
// Make sure that you are using STA
// Otherwise you will get an error when creating WebBrowser
// control in the Navigate method
t.SetApartmentState(ApartmentState.STA);
t.Start();
OnDataRecieved += Navigate;
}
public void Navigate()
{
WebBrowser b = new WebBrowser();
b.Navigate("www.google.com");
}
public void TriggerEvent()
{
Thread.Sleep(10000);
OnDataRecieved();
}
}
Below is the error:
System.Threading.ThreadStateException: 'ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.'
Hope this helps.

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# communication between splashscreen and mainform into different thread

I've found that example (Communicate between two windows forms in C#) that works fine but not when the two forms are in different thread.
I've got an "System.NullReferenceException" at line 20 in MainForm (this.splashy.LabelText = i;)
The point of this script is to modified the width of the progressBar in the splashscreen when MainForm is doing his job.
Thanks in advance!
Here are the two c# classes and program file:
//Program.cs
using System;
using System.Threading;
using System.Windows.Forms;
namespace GIS
{
static class Program
{
public static SplashScreen splashy = null;
/// <summary>
/// Point d'entrée principal de l'application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread splashThread = new Thread(new ThreadStart(
delegate
{
//SplashScreen splashy = new SplashScreen();
splashy = new SplashScreen();
Application.Run(splashy);
}
));
splashThread.SetApartmentState(ApartmentState.STA);
splashThread.Start();
//run form - time taking operation
MainForm mainForm = new MainForm(splashy);
mainForm.Load += new EventHandler(mainForm_Load);
Application.Run(mainForm);
}
static void mainForm_Load(object sender, EventArgs e)
{
//close splash
if (splashy == null)
{
return;
}
splashy.Invoke(new Action(splashy.Close));
splashy.Dispose();
splashy = null;
}
}
}
//SplashScreen.cs
using System;
using System.Windows.Forms;
namespace GIS
{
public partial class SplashScreen : Form
{
public SplashScreen()
{
InitializeComponent();
}
public int LabelText
{
get
{
return rectangleShape1.Width;
}
set
{
rectangleShape1.Width = value;
}
}
}
}
//MainForm.cs
using System.Threading;
using System.Windows.Forms;
namespace GIS
{
public partial class MainForm : Form
{
private SplashScreen splashy = null;
public MainForm(Form callingForm)
{
splashy = callingForm as SplashScreen;
InitializeComponent();
doWork();
}
private void doWork()
{
for(int i = 0; i < 100; i++)
{
this.splashy.LabelText = i;
Thread.Sleep(10);
}
}
}
}
You need to invoke the SplashScreen to change it's value from an other thread like
this.splashy.Invoke((MethodInvoker) delegate { this.splashy.LabelText = "Requested" + repeats + "Times"; });
Note that the constructor
splashy = callingForm as SplashScreen;
allows splashy to be null - this causes your current NullReferenceException.
This problem is locaed in this snipped:
Thread splashThread = new Thread(new ThreadStart(
delegate
{
splashy = new SplashScreen();
Application.Run(splashy);
}
));
splashThread.SetApartmentState(ApartmentState.STA);
splashThread.Start();
//run form - time taking operation
MainForm mainForm = new MainForm(splashy);
The new thread you are start is not fast enought to create the instance of SplashScreen before you pass it. The result - you are passing null.
Fix it by create the splashy before your thread starts:
splashy = new SplashScreen();
Thread splashThread = new Thread(new ThreadStart(
delegate
{
Application.Run(splashy);
}

How to perform asynchronous procedures on a background thread during application startup without blocking?

Simply put, I can not see what is blocking the UI, nor how to fix it. I've been staring at this for hours.
From the beginning,
App.xaml
<Application x:Class="Front_Office.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Application.Resources>
......
</Application.Resources>
</Application>
App.xaml.cs
using Front_Office.ViewModels;
using System.Windows;
namespace Front_Office
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected async override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainWindow = new MainWindow { DataContext = await MainWindowViewModel.Create() };
mainWindow.Show();
}
}
}
MainWindow.xaml.cs
using System.Windows;
namespace Front_Office
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
Factory pattern used to create MainWindowViewModel with asynchronous calls.
public static async Task<MainWindowViewModel> Create()
{
var myClass = new MainWindowViewModel();
await myClass.Initialize();
return myClass;
}
private async Task Initialize()
{
PatientList = await GetPatientList();
FilteredPatientList = PatientList;
}
private MainWindowViewModel() : base()
{
}
GetPatientList()
private Task<ObservableCollection<ViewPatient>> GetPatientList()
{
IsLoading = true;
var x = Task.Run(async () =>
{
Thread.Sleep(30000); // SIMULATING A VERY LONG CALL.
return new ObservableCollection<ViewPatient>(await MedicalClient.GetAllPatientsAsync());
});
IsLoading = false;
return x;
}
I am trying to run MedicalClient.GetAllPatientsAsync() and Thread.Sleep() on a background thread so as not to block the UI on application startup. No luck.
What am I doing wrong?
Any help is most welcome.
Edit #1:
By making the following changes, it seems to work as I need. Is this the correct way to do this? (It seems to break the factory pattern as described here on SO).
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
// Droped the async and the await
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainWindow = new MainWindow { DataContext = MainWindowViewModel.Create() };
mainWindow.Show();
}
}
// Dropped async and await. Added Task.Run()
public static MainWindowViewModel Create()
{
var myClass = new MainWindowViewModel(); // Create/Load ViewModel
Task.Run(()=> myClass.Initialize()); // Update ViewModel from Asynchronous methods.
return myClass;
}
In your case UI thread not blocked, it is just have nothing to show.
Here is your problem:
public partial class App : Application
{
protected async override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainWindow = new MainWindow
{ DataContext = await MainWindowViewModel.Create() };
//next line will be executed after "Create" method will complete
mainWindow.Show();
}
}
await keyword will stop executing current method, freed current thread(continue executing caller method or continue with next event...) and continue only when task is completed.
Because mainWindow not initialized yet, application have nothing to show.
I think you can move initialization of MainWindow.DataContext inside MainWindow (for example Load eventhandler), then window will be showed immediately and filled with data when Create method is complete
I work mostly in ASP land--I'm spinning up a WPF project real quick. I'm thinking the problem is that you are using the MainView's "Initialize" method which is actually being called synchronously.
Or if my jargon is wrong, at the very least Initialize() has to complete before the view will render. I was about to recommend using like the OnStartup class, where I saw you had the following line:
var mainWindow = new MainWindow { DataContext = await MainWindowViewModel.Create() };
Problem being is then upon application startup your code calls Create() which itself awaits Intitialize() so I'm assuming that is where the app is locking up on startup unless I'm mistaken. Can you throw in a few break points and confirm?
My suggestion would be like below--but it would still require refactoring since it appears your object depends upon the data actually being loaded to display.
protected async override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainData = await GetPatientList();
// Your MainWindowViewModel's Create() call would have to
//undo it's dependency on GetPatientList(), however. I don't really
//have a coherent example
var mainWindow = new MainWindow { DataContext = await MainWindowViewModel.Create() };
mainWindow.Show();
}

Categories

Resources