Since I added a splash screen my main form will sometimes (about 1 every 20 times) disappear like it's minimized (it will be invisible but it will be still on the task bar and if I click it it reappears). Here is my code:
static class Program
{
private static SplashScreen splashScreen = null;
private static ManualResetEvent splashScreenWaiter = null;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ShowSplashAsync();
BuilderForm2 builderForm2 = new BuilderForm2();
builderForm2.Shown += new EventHandler(builderForm2_Shown);
Application.Run(builderForm2);
}
private static void HideSplash()
{
if (splashScreenWaiter != null)
{
splashScreenWaiter.WaitOne();
splashScreen.Invoke(new Action(splashScreen.Close));
splashScreenWaiter = null;
splashScreen = null;
}
}
private static void builderForm2_Shown(object sender, EventArgs e)
{
HideSplash();
}
private static void ShowSplashAsync()
{
splashScreenWaiter = new ManualResetEvent(false);
Thread splashThread = new Thread(ShowSplash);
splashThread.IsBackground = true;
splashThread.SetApartmentState(ApartmentState.STA);
splashThread.Start(splashScreenWaiter);
}
private static void ShowSplash(object resetEvent)
{
splashScreen = new SplashScreen((ManualResetEvent)resetEvent);
Application.Run(splashScreen);
}
}
And this is SplashScreen code:
public partial class SplashScreen : Form
{
private ManualResetEvent ResetEvent;
bool handleCreated = false;
bool formShown = false;
public SplashScreen(ManualResetEvent resetEvent)
{
ResetEvent = resetEvent;
HandleCreated += new EventHandler(SplashScreen_HandleCreated);
InitializeComponent();
}
private void SetResetEventIfReady()
{
if(handleCreated && formShown) ResetEvent.Set();
}
private void SplashScreen_Shown(object sender, EventArgs e)
{
formShown = true;
SetResetEventIfReady();
}
void SplashScreen_HandleCreated(object sender, EventArgs e)
{
handleCreated = true;
SetResetEventIfReady();
}
}
Nothing jumps out. There is however a very serious race condition in your code. It is related to the SystemEvents class. That class provides important notifications to controls so they can respond to the user changing the Windows theme. That class needs a hidden notification window to receive messages about the changes the user made.
This goes very wrong if your program's first window is created on a worker thread instead of the UI thread. That makes the SystemEvents class create that notification window on the wrong thread (not your worker thread btw). And the events it raises will be called from that thread. Getting the event on that wrong thread creates havoc, controls are not thread-safe. The most typical outcome is that you'll have odd painting problems or the form deadlocks when you lock the workstation. I can imagine what you see going wrong could be explained by this as well.
The .NET framework already has excellent and time-tested support for splash screens. I recommend you use it instead of spinning your own. Check this answer for the code.
If you want to keep your own then you can work around the race problem by pasting this line of code into your Main method, before the ShowSplashAsync call:
Microsoft.Win32.SystemEvents.UserPreferenceChanged += delegate { };
Related
Sorry for my bad English. Hope someone suggests me a better version of my question.
I've searched but seemed like I couldn't find the answer for my problem.
Currently, I'm writing a C# WPF app. This app will perform a heavy task in a long time. So I've decided to create another class with that heavy method and pass that method to another thread. I have to create a class to do that because the heavy method takes parameters.
I want the ability to suspend and resume that thread. I've known that I should use a ManualResetEvent object or Thread.Sleep method.
After many hours of trying and testing, getting confused why I always end up suspend the UI thread but the heavy thread is still running. What I've tried were:
Create a ManualResetEvent object called mre inside the HeavyClass. When user click the Pause button, the UI class will call the method heavyClass.mre.WaitOne().
class HeavyClass
{
// properties
ManualResetEvent mre = new ManualResetEvent(false);
public void HeavyRun()
{
//Do something takes really long time
//And doesn't have any loops
}
}
class MainWindow : Window
{
// properties
private HeavyClass heavyClass = new HeavyClass();
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun);
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
heavyClass.mre.WaitOne();
}
}
Create a method called SleepThread inside the HeavyClass. When user click the Pause button, the UI class will call the method heavyClass.SleepThread().
class HeavyClass
{
//properties
ManualResetEvent mre = new ManualResetEvent(false);
public void SleepThread()
{
Thread.Sleep(Timeout.Infinite);
//mre.WaitOne();
//They are the same behavior
}
public void HeavyRun()
{
//Do something takes really long time
//And doesn't have any loops
}
}
class MainWindow : Window
{
// properties
private HeavyClass heavyClass = new HeavyClass();
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun);
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
heavyClass.SleepThread();
}
}
Create an EventHandler<MainWindow> PauseThread inside the UI class, then write its handle inside the HeavyClass. When user click the Pause button, the UI class will trigger the event PauseThread(this, this).
class MainWindow : Window
{
// properties
private HeavyClass heavyClass = new HeavyClass();
public event EventHandler<MainWindow> PauseThread;
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun);
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
PauseThread(this, this);
}
}
class HeavyClass
{
// properties
ManualResetEvent mre = new ManualResetEvent(false);
public void HeavyRun()
{
MainWindow.PauseThread += (s, E) =>
{
Thread.Sleep(Timeout.Infinite);
//mre.WaitOne();
//They are the same behavior
};
//Do something takes really long time
//And doesn't have any loops
}
}
As I said above, I always paused the UI thread and the heavy task is still running.
And finally in the end, I've known the essence of my problem. That is: which thread calls Thread.Sleep() or WaitOne() will be blocked. Yeah, "which thread", not "which class".
Everything makes sense for me now. But that doesn't help me to achieve my goal. And that leads me to think that I am doing the seemingly impossible thing. It's clearly that I want to pause a thread by another thread. But that another thread is the one who calls any kinds of "suspend thread", so it is the one who is suspended. I don't have any idea about how to make the heavy method to be suspended by itself. It is running, how the hell it could know when the user click the Pause button?
I am at a total loss. Someone please help me to make my app works as expected.
By the way, this impossible thing makes me think that I am doing things wrong way, is it?
UPDATE: If you like to see my heavy task, actually it is very simple
class HeavyClass
{
public string filePath = "D:\\Desktop\\bigfile.iso";//This file is about 10GB
public string HeavyRun()
{
string MD5Hash;
MD5 md5 = MD5.Create();
Stream stream = File.OpenRead(filePath);
MD5Hash = Encoding.Default.GetString(md5.ComputeHash(stream));
return MD5Hash;
}
}
To make a thread suspendable, the work in the thread must be separable. In your case md5.ComputeHash(stream) will do all the work, and there is not way to make sure that thread will suspend at a right(saft) point inside md5.ComputeHash(stream). So you have to rewrite HeavyClass like below. Please notice that those codes are not the best approach of handling a thread, and I just try to keep it as same as the original.
class HeavyClass
{
MD5 _md5 = MD5.Create();
MethodInfo _hashCoreMI = _md5.GetType().GetMethod("HashCore", BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo _HashFinalMI = _md5.GetType().GetMethod("HashFinal", BindingFlags.NonPublic | BindingFlags.Instance);
WaitHandle _signal;
public void HeavyClass(WaitHandle signal)
{
_signal = signal;
}
public string HeavyRun(string filename)
{
byte[] buffer = new byte[4096];
int bytesRead = 0;
_signal.Set();
using(FileStream fs = File.OpenRead(filename))
{
while(true)
{
bytesRead = fs.Read(buffer, 0, 4096);
if (bytesRead > 0)
{
_hashCoreMI.Invoke(_md5, new object[] { buffer, 0, bytesRead });
}
else
{
break;
}
// if WaitHandle is signalled, thread will be block,
// otherwise thread will keep running.
_signal.WaitOne();
}
}
byte[] hash = _hashFinalMI.Invoke(_md5, null);
_md5.Initialize();
return Encoding.ASCII.GetString(hash);;
}
}
class MainWindow : Window
{
private HeavyClass _heavyClass;
private ManualResetEvent _mre;
public MainWindow()
{
InitializeComponent();
_mre = new ManualResetEvent(true);
_heavyClass = new HeavyClass(_mer);
}
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun("D:\\Desktop\\bigfile.iso"));
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
_mre.Reset();
}
private void buttonResume_Click(object sender, RoutedEventArgs e)
{
_mre.Set();
}
}
I've been playing around with multithreading and reading up on some of the questions here, but I haven't found an answer that directly addresses my concerns here.
I have an application that runs on a single thread, except for a progress bar in a separate window. Based on my research, I need to create a new thread for that form which will redraw the form's controls as it's properties change. I've reduced the problem to a simple example below:
Here's the 'main' program:
class Program
{
static MyForm form;
static void Main(string[] args)
{
form = new MyForm();
form.Show();
doWork();
form.Close();
}
//arbitrary example of processing that takes some period of time
static void doWork()
{
while (form.Value < 100000)
{
form.ChangeVal();
Thread.Sleep(1);
}
return;
}
}
...And here's the Form. I'm not including the auto-generated stuff from VS.
public partial class MyForm : Form
{
private int val;
public int Value
{
get { return val; }
set { val = value; }
}
public Thread GUIupdater;
public MyForm()
{
InitializeComponent();
this.Refresh();
}
private void MyForm_Load(object sender, EventArgs e)
{
GUIupdater = new Thread(new ThreadStart(GUIupdaterThread));
GUIupdater.Start();
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(killThreadOnClose);
}
public void ChangeVal()
{
val++;
}
private void changeLabel(string s)
{
label.Text = s;
label.Refresh();
}
private delegate void labelChanger(string s);
private void GUIupdaterThread()
{
while (true)
{
Invoke(new labelChanger(changeLabel), new object[]{val.ToString()} );
Thread.Sleep(100); //100 ms
}
}
private void killThreadOnClose(object sender, FormClosingEventArgs e)
{
GUIupdater.Abort();
}
}
So, my intention here is to have the calculations running constantly, with the window's graphics updating reasonably quickly. When I run the program, however, the invoke function is only called once, and the label never actually updates!
Any and all feedback is appreciated. If you want to view the code in an IDE you can download my project from Here
Edits:
When I add Console.WriteLine Statements, I discovered that the GUIupdaterThread (the thing that's meant to update the GUI) loop always 'breaks' on the Invoke statement, never reaching 'Thread.Sleep'. I changed it to 'BeginInvoke', which causes the loop to function properly, but this hasn't changed the fact that the GUI doesn't update.
CLARIFICATIONS:
About my 'actual' project:
The main thread here in 'Program' simulates my software, which is a plugin implementing an interface. My decision to alter val / value in that thread, not in the thread created by the window, was deliberate.
I'm constrained to using .NET 4.0 . any more recent features can't help me
Since in your application you have GUI thread (main thread) - all UI controls will be accessible from this thread only.
There are several approaches how to update controls from other threads.
I would like to recommend you to use one of modern and native approaches based on Progress < T > class (it's native for .Net platform).
I would suggest overriding the form's OnPaint method. Then inside ChangeVal, after you have updated whatever variables/data you need to update, call this.Invalidate which should trigger the form to repaint itself.
Or if you're just updating a single label, call label.Refresh in your ChangeVal method. The form should update correctly. Here's an example that worked for me:
This form has a single label on it.
public partial class ProgressForm : Form
{
private int currentValue = 0;
public ProgressForm()
{
InitializeComponent();
}
public void ChangeValue(int newValue)
{
currentValue = newValue;
lblValue.Text = string.Format("Current value: {0}", currentValue);
lblValue.Refresh(); //Call Refresh to make the label update itself
}
}
static class Program
{
private static ProgressForm progressForm = null;
[STAThread]
static void Main()
{
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
progressForm = new ProgressForm();
progressForm.Show();
doWork();
progressForm.Close();
}
//arbitrary example of processing that takes some period of time
static void doWork()
{
int i = 0;
while (i < 100000)
{
progressForm.ChangeValue(i);
Thread.Sleep(1);
i++;
}
return;
}
}
You may use the following instead as you are trying to access UI control other than main thread (from which it is created).
while ( true )
{
Invoke ( ( Action ) (() =>
{
label.Text = val.ToString();
label.Refresh()
Application.DoEvents();
}));
Thread.Sleep( 100 );
}
I recommend you to use "backgroundworker".
First add CheckForIllegalCrossThreadCalls = false; to initialization part otherwise InvalidOperationException occurs.
private void btnDoIt_Click(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Foo();
}
int total = 0;
private void Foo()
{
for (int i = 0; i <= 100000; i++)
{
total += i;
this.Text = i.ToString();
}
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Run next process
}
I have form with button and text box. Button is starting thread which is updating value of text box.
public Form1()
{
InitializeComponent();
myDelegate = new UpdateUi(updateUi);
}
private void button1_Click(object sender, EventArgs e)
{
myThread = new Thread(new ThreadStart(ThreadFunction));
myThread.Start();
}
private void ThreadFunction()
{
MyThreadClass myThreadClassObject = new MyThreadClass(this);
myThreadClassObject.Run();
}
private void updateUi(int i)
{
textBox1.Text = i.ToString();
Thread.Sleep(1000);
}
public Thread myThread;
public delegate void UpdateUi(int i);
public UpdateUi myDelegate;
and ThreadClass:
public class MyThreadClass
{
Form1 myFormControl1;
public MyThreadClass(Form1 myForm)
{
myFormControl1 = myForm;
}
public void Run()
{
// Execute the specified delegate on the thread that owns
// 'myFormControl1' control's underlying window handle.
for(int i=0;i<100;i++)
{
if(myFormControl1.InvokeRequired)
{
myFormControl1.Invoke(myFormControl1.myDelegate,i);
}
}
}
}
As You can see there is nothing special in my code but sometimes the code freeze.
eg it goes 1->2->3->freeze->16->17 and so on.
I took code from HERE with little modifications
The issue is you are delaying the UI thread not the the process itself so what happens is you issue all the update commands but since it all runs on the same thread it gets clogged because the Thread.Sleep stops the UI thread so it runs a bunch of textBox1.Text = i.ToString(); then it stops for all the time of all the Thread.Sleep(1000); probably the number of 1->2->3... you see is equal to the number of cores in your machine.
When you stop the run method what happens is you issue one update command that runs immediately and wait for one second until you issue the next command witch I think its what you are trying to accomplish.
Working on a C# project which I would like to implement a "waiting" (throbber) indicator in a separate form. After much research and trial and error it appears as the suggested method of doing this is to load a form using a separate thread from the one from the current form/thread.
The reason I went with this method was because initially using the Show() method on the throbber form produced a transparent form. I cannot use ShowDialog because I need to run some code after the throbber is displayed, after which that completes I would like to close the throbber form.
Anyway .. after trying many different methods to load the throbber form in a separate thread I still get an error about trying to access it from a thread which is different from the one it was created in. Here is a skelton version of the project code that should shed some light on my issue:
the example I was working off of for multithreading was this popular link for creating your own spashscreen in a separate thread ... http://www.codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C
public class Main
{
public void CheckData()
{
try
{
ProgressBar pb = new ProgressBar();
pb.ShowProgressBar();
//do data checking here
pb.CloseForm()
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static Thread ms_oThread = null;
public bool shouldStop = false;
static ProgressBar ms_ProgBar = null;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
public void ShowForm()
{
ms_ProgBar = new ProgressBar();
Application.Run(ms_ProgBar);
}
public void CloseForm()
{
ms_ProgBar.Close();
}
public void ShowProgressBar()
{
// Make sure it is only launched once.
if (ms_ProgBar != null)
return;
ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
while (ms_ProgBar == null || ms_ProgBar.IsHandleCreated == false)
{
System.Threading.Thread.Sleep(1000);
}
}
}
You are creating your ProgressBar twice. Once in your main function, and once in your new thread. You are also calling your CloseWindow method from your main function (and on the window that is never shown), rather than on your new thread window.
You only want to create ProgressBar and show it using your new thread. Make your static ProgressBar field public so you can call close on it directly from Main, but make sure to use Invoke to do it since it's not on that Window's GUI thread.
Also, ShowProgressBar should be static.
Here's a rewrite attempt:
public class Main
{
public void CheckData()
{
try
{
ProgressBar.ShowProgressBar();
//do data checking here
ProgressBar.CloseForm();
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static ProgressBar _progressBarInstance;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
static void ShowForm()
{
_progressBarInstance = new ProgressBar();
Application.Run(ms_ProgressBar);
}
public static void CloseForm()
{
_progressBarInstance.Invoke(new Action(_progressBarInstance.Close));
_progressBarInstance= null;
}
public static void ShowProgressBar()
{
// Make sure it is only launched once.
if (_progressBarInstance != null)
return;
var ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
}
}
OK guys, I have this Class that shows a "Loading..." Splash Screen. It works great when I call it on Initialize() but not on Form_Load. Instead of showing at the beginning of Form_Load, it shows after all tables are filled and then just hangs there (no lock).
class innerLoad
{
//Delegate for cross thread call to close
private delegate void CloseDelegate();
//The type of form to be displayed as the splash screen.
private static frmLoading splashForm;
static public void ShowSplashScreen()
{
// Make sure it is only launched once.
if (splashForm != null)
return;
Thread thread = new Thread(new ThreadStart(innerLoad.ShowForm));
thread.IsBackground = true;
//Thread.Sleep(100);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
//volatile static public bool isOpen = false;
static private void ShowForm()
{
splashForm = new frmLoading();
splashForm.ShowDialog();
splashForm.Dispose();
}
static public void CloseForm()
{
try
{
if (splashForm == null)
return;
splashForm.Invoke(new CloseDelegate(innerLoad.CloseFormInternal));
}
catch
{
}
}
static private void CloseFormInternal()
{
splashForm.Close();
splashForm = null;
}
}
And here is the Form_Load Code:
private void frmPayGen_Load(object sender, EventArgs e)
{
//th1 = new Thread(LoadingForm);
//th1.Start();
//Thread.Sleep(500);
innerLoad.ShowSplashScreen();
fill();
innerLoad.CloseForm();
//Thread.Sleep(500);
}
I appreciate your help and I love this site... helps me a lot :D
If you set a breakpoint at the start of your Form Load event, and use F11 to step through, you eventually see this exception:
Exceptions in a Form Load event are basically ignored. If an exception is thrown, nothing after the line where the exception was thrown runs, but the Windows Form doesn't crash either. Taking away this line of code should make things work as you wish.