When I start my application I need to check if one of my service is running. If service is not running then I have to allow the user to start the service and then execution continues. If User chooses not to start the service then I have to Exit the Application. For every 10 sec I have to pop up the message to start the service,during this time program execution should not continue.Below is the code I have written. The service which I am looking to be running has a event handler which notifies when service is available(event handler is WindowsServiceAvailableEventHandler).
I have two questions here
How to resume or interrupt the Thread which is sleeping(Thread.Sleep(10000);) as soon as ServiceAvailable Event is raised?
When user want to start the service then he will click on Yes button and Dailog Box Closes and user will not know if something is happening..so I am looking for something like progress bar which will tell user that it is waiting for the user to start the service...as soon as service is started progress bar should close.How to achieve this?
My code:
this.windowsService.ServiceAvailable += this.WindowsServiceAvailableEventHandler
While (!CheckifServiceStarted())
{
DialogResult dlgResult = MessageBox.Show("Service is not Started.Please start Service to continue", "Start Service", MessageBoxButtons.YesNo);
if (dlgResult == DialogResult.Yes)
{
Thread.Sleep(10000);
}
else
{
System.Environment.Exit(0);
}
}
private void DataServicesAvailableEventHandler(object sender, EventArgs e)
{
//How to Resume or Interrupt the Thread here?
}
You could replace your Thread.Sleep with ManualResetEvent.WaitOne to signal the change in the handler like so:
private ManualResetEvent serviceAvailableEvent = new ManualResetEvent(false);
this.windowsService.ServiceAvailable += this.WindowsServiceAvailableEventHandler
While (!CheckifServiceStarted())
{
DialogResult dlgResult = MessageBox.Show("Service is not Started.Please start Service to continue", "Start Service", MessageBoxButtons.YesNo);
if (dlgResult == DialogResult.Yes)
serviceAvailableEvent.WaitOne(10000);
else
System.Environment.Exit(0);
}
private void DataServicesAvailableEventHandler(object sender, EventArgs e)
{
serviceAvailableEvent.Set();
}
As for the progress bar, check this question out for example.
Related
Is it possible to stop an ongoing process with a button click in Windows form application?
For example, let's say there are 2 buttons, "START" and "STOP"
When you press "START", it will start an infinite loop, printing numbers from 1 to infinity.
When I press "STOP", the process should stop at that moment.
But the problem is, I cannot press the "STOP" button as it does not allow me, since there's an ongoing process.
Is there a way to overcome this?
I know there's something called "MethodInvoker", but I have no idea how that works or whether it is relevant to this.
private bool keepRunning = true;
public Form1()
{
InitializeComponent();
}
private void StartBtn_Click(object sender, EventArgs e)
{
var number = 1;
while (keepRunning)
{
Thread.Sleep(1000);
MesgeLabel.Text = "" + number++;
}
}
private void StopBtn_Click(object sender, EventArgs e)
{
//Cannot even click this button
keepRunning = false;
//or
Application.Exit();
}
EDIT 1:
If you need to interact with UI controls, doing it from a background task would throw invalid operation -> illegal cross thread exception. To overcome this,
check Control.InvokeRequired
if(myLabel.InvokeRequired)
myLabel.Invoke(new Action(() => myLabel.Text = newText));
else
myLabel.Text = newText;
You can start a Task by providing a CancellationToken and cancel the operation when the stop button is clicked.
The task will execute the infinite loop on another thread and your main thread (the UI thread) should not be affected and should be accessible.
Try this:
/*
Please add these on top of your form class
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
*/
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
CancellationTokenSource cancellationTokenSource;
CancellationToken cancellationToken;
private void CountToInfinity()
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
Debug.WriteLine(new Random().Next());
}
}
private async void button1_Click(object sender, EventArgs e)
{
if (cancellationTokenSource == null)
{
cancellationTokenSource = new CancellationTokenSource();
cancellationToken = cancellationTokenSource.Token;
Task.Run((Action)CountToInfinity, cancellationToken);
}
}
private void button2_Click(object sender, EventArgs e)
{
if (cancellationTokenSource != null)
{
cancellationTokenSource.Cancel();
cancellationTokenSource.Dispose();
cancellationTokenSource = null;
}
}
}
If you have spawned a new process then you can call kill method.
Process myProcess = Process.Start("Notepad.exe")//starts new process
myProcess.Kill();// kills the process. save reference to myProcess and call kill on STOP button click
If you have started new thread then call abort method to stop the thread.
Thread thread = new Thread(new ThreadStart(method));
thread.Start();
thread.Abort(); // terminates the thread. call abort on STOP button click
When you press the "start" button, the code that runs and prints the numbers will run on the ui thread. (from your explanation, i assume that all you have is the message handler for the button press event and nothing else. e.g.: Not setting up a seperate thread.).
Running an infinite loop on the ui thread means, that you do not get any more time for processing other messages. (the thread that is responsible for processing the ui messages is stuck in your infinite loop.)
So, in order to be able to press the "stop" button, you need to run the code with the infinite loop in a different thread or in a different process altogether. This is what Arjun is trying to tell you. (if you want the code in the infinite loop to access resources from your form app, you need a thread. [the thread is inside the forms app process.])
please note: if you create a thread and run your number printing code inside that thread, this will not be the ui thread. Thus, you will not be able to interact with the forms controls as if you'd be on the ui thread. (i.e.: trying to set the windows.text in order to display your numbers will most likely throw an exception.)
I am writing this program that I want to run forever via a while loop and whenever the user presses a certain key on their keyboard it exits the program. I've looked everywhere but I have only seen KeyEvents, but the WindowsForm isn't active while the program is running. Anyone have a solution for me?
Edit: The program takes over the cursor so activating an event on the UI is basically impossible
Edit Two:
public void MainMethod()
{
while (true)
{
if (checkBox1.Checked == true) state = State.PERFORM_ACTION_ONE;
if (checkBox2.Checked == true) state = State.PERFORM_ACTION_TWO;
// More stuff checking which state to assign
switch (state)
{
case State.PERFORM_ACTION_ONE:
DoSomething();
break;
// More cases
// I want it to be able to break anywhere in the while loop
}
}
}
You need to set a HotKey like here Set global hotkeys using C# then using that HotKey to exit the application.
You need to run your infinite loop in a separate thread from the UI thread. And have a infinite loop to check on the variable that can be set from UI thread:
while (keepRunning){
// do stuff
}
and then set event on a button press to change keepRunning to false.
Here is the quick sample:
public partial class Form1 : Form
{
public static bool KeepRunning;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
KeepRunning = true;
Task.Factory.StartNew(() =>
{
while (KeepRunning)
{
Trace.WriteLine("Keep running");
}
});
}
private void button2_Click(object sender, EventArgs e)
{
KeepRunning = false;
Trace.WriteLine("Finished Execution");
}
}
if you start the loop in another thread you could cancel that thread that it is running when you hit the "hot key" or whatever you want to stop it. Check out BackgroundWorker.
Put the loop into a separate Task.
WinForms will continue to run concurrently on the UI thread, so it can continue to receive the user input. When user requests you to stop, you can use the task cancellation mechanism1 to exit from the loop and the task itself.
1 See the "Canceling a Task" section here.
I am having serious issues with a progress bar. I am building a custom backup utility, it allows users force their update now on a button click. When they click the button it calls a console application with the console window hidden runs the full backup process then completes. during the whole process there is no status or progress bar, due to the fact that after this install is done it will be transparent to the user....users cause issues we all know that. During the GUI interaction there needs to be something that tells the admin that it's doing something. here is my code:
private void forback_Click(object sender, EventArgs e)
{
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += new DoWorkEventHandler(MethodToGetInfo);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
progressBar1.Style = ProgressBarStyle.Marquee;
bg.RunWorkerAsync();
}
void MethodToGetInfo(Object sender, DoWorkEventArgs args)
{
Process info = new Process();
info.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
info.StartInfo.FileName = "C:\\Rameses\\Program\\Day_Cloud_Backup.exe";
info.StartInfo.UseShellExecute = false;
info.StartInfo.CreateNoWindow = true;
info.StartInfo.RedirectStandardOutput = true;
info.Start();
}
void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs args)
{
MessageBox.Show("it's f***ing done");
}
what the heck am I doing wrong. I click the button, the app runs... but the message box shows right away and the progress bar doesn't stop.
Any Ideas?
Two things here.
For the Progress bar to stop, you need to tell it to stop in the RunWorkerCompleted callback (in your case, change the style).
For the thread to not return immediately, you need to block in the thread to wait until the Process you kicked off is completed. One way to do this is to use the WaitForExit Process method:
...
info.Start();
info.WaitForExit();
http://msdn.microsoft.com/en-us/library/fb4aw7b8.aspx
This will make the thread wait until your process is done before calling the WorkerCompleted callback.
I have implemented my custom ThreadManager which has been working flawlessly during my tests. When user wants to close the application, the closing is suspended until all threads exit or they select to end the application without waiting (after 30 seconds have passed).
What I need to clarify is if using Application.DoEvents() could be dangerous in FormClosing event. Shall I use it or find another way of waiting for threads to exit?
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
// save settings before exit
Properties.Settings.Default.Save();
// Update program about intention
Program.ApplicationClosing = true;
try
{
// Inform user with friendly message
ShowModalWaitForm("Application is closing.");
// Keep the timestamp in order to keep track of how much time has passed since form closing started
DateTime startTime = DateTime.Now;
// Wait for all threads to die before continuing or ask user to close by force after 30 seconds have passed
// In case user prefers to wait the timer is refreshed
int threadsAlive;
do
{
if (_threadManager.TryCountAliveThreads(out threadsAlive) && threadsAlive > 0)
{
Application.DoEvents();
Thread.Sleep(50);
}
TimeSpan timePassed = DateTime.Now - startTime;
if (timePassed.Seconds > 30)
{
if (ShouldNotWaitThreadsToExit())
{
break; // Continue with form closing
}
else
{
startTime = DateTime.Now; // Wait more for threads to exit
}
}
} while (threadsAlive > 0);
}
catch (Exception ex)
{
_logger.ErrorException("MainForm_FormClosing", ex);
}
finally
{
HideWaitForm();
}
}
private bool ShouldNotWaitThreadsToExit()
{
return MessageBox.Show(#"Press ""OK"" to close or ""Cancel"" to wait.", "Application not responding ", MessageBoxButtons.OKCancel) == DialogResult;
}
I'd recommend putting your wait condition in another thread. Display a modal dialog from OnFormClosing method. Inside this dialog start worker thread e.g using BackGroundWorker class and dismiss this dialog when waiting finished.
Bonus topic possible drawbacks of calling Application.DoEvents Method
I am trying to create a login form. The problem that I am having is that the login process is taking too long and is locking up my GUI. I have read up on background worker, but I am still uncertain on how to have my program wait for the login process but not freeze my GUI. Here is my code to help explain it more.
Login.cs
public partial class Login : Form
{
public delegate bool Authenicate(string account, string password,string type);
public Authenicate authenicate;
public Login()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, EventArgs e)
{
if (txtAccount.Text == string.Empty)
{
MessageBox.Show("Must include account number", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (txtPassword.Text == string.Empty)
{
MessageBox.Show("Must include password", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!authenicate(txtAccount.Text, txtPassword.Text,cmbType.Items[cmbType.SelectedIndex].ToString()))
{
return;
}
this.DialogResult = DialogResult.OK;
}
private void Login_Load(object sender, EventArgs e)
{
cmbType.SelectedIndex = 0;
}
MainForm.cs
public partial class MainForm: Form
{
Ex.Service myService=new Ex.Service();
public MainForm()
{
InitializeComponent();
}
public bool Authenicate(string account, string password,string type)
{
try
{
//Login takes too long and locks up GUI
//Maybe try background worker, but how to wait until
//login is complete?
myService.Login(account,password,type);
return myService.IsLogin();
}
catch(Exception exception)
{
MessageBox.Show(exception.message);
}
return false;
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
myService.Logout(); //Logout from service
myService = null;
}
}
Thank you for your time.
The general steps are:
Add the Background Worker to your Login dialog
Create an event handler for the Background Worker's DoWork event that calls you authenticate delegate.
In btnLogin_Click disable the Login dialog so the user cannot click login again while the back ground worker is running.
In btlLogin_Click call the BackGround worker's RunWorkAsync method to start the worker running.
Create an event handler for the Background Worker's RunWorkerCompleted event. In that event enable the LoginForm and either close the dialog if the login result was successful or display en error message.
I would say create an event in the login form and subscribe in the main form. Within the login form you can use the thread to perform login task if it is taking too long. Once login is succesful or unsuccessful you can notify using this event to main form and send the additional information in the event arguments to the main form.
Upon recieving this event, main form can then proceed based on the conditions set by you.
Disable the pertinent UI elements (buttons, textfields, etc.) and then spin up a background worker thread. When it completes, update the UI as appropriate.
Communicating with the UI could take the form of LoginSucceeded and LoginFailed events, or similiar.