I have to close a Form from a thread and I am using the Invoke method of the Form for calling the Close() method.
The problem is that when closing, the form is disposed and I get an InvalidOperationExecption wit the message "Invoke or BeginInvoke cannot be called on a control until the window handle has been created.".
I have got this exception only when debugging with a "Step Into" in the Close method but I don't want to risk with a possible error on normal running.
This is an example code to reproduce it:
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(CloseForm);
thread.Start();
}
private void CloseForm()
{
this.Invoke(new EventHandler(
delegate
{
Close(); // Entering with a "Step Into" here it crashes.
}
));
}
The form is disposed in the automatic generated code for the form (which I would like not to modify):
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
I would appreciate it if someone could give me a solution for this or another way to close a form from another thread.
Use this method :
// Inspired from: http://stackoverflow.com/a/12179408/1529139
public static void InvokeIfRequired(Control control, MethodInvoker action)
{
if (control.IsDisposed)
{
return;
}
if (control.InvokeRequired)
{
try
{
control.Invoke(action);
}
catch (ObjectDisposedException) { }
catch (InvalidOperationException e)
{
// Intercept only invokation errors (a bit tricky)
if (!e.Message.Contains("Invoke"))
{
throw e;
}
}
}
else
{
action();
}
}
Usage example:
Functions.InvokeIfRequired(anyControl, (MethodInvoker)delegate()
{
// UI stuffs
});
So far the best solution for this case has been to use the SynchronizationContext mechanism. I had the tip in Should I use Invoke or SynchronizationContext to update form controls from another thread?.
The example code would be like this:
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(MethodThread);
thread.Start(SynchronizationContext.Current);
}
private void MethodThread(Object syncronizationContext)
{
((SynchronizationContext)syncronizationContext).Send(CloseForm,null);
}
private void CloseForm(Object state)
{
Close();
}
The most obvious comment is - there's no apparent reason why you would need to close a form before it has even completed loading. There are other, better ways to handle whatever the reason is.
However since you asked...
The error gives you the answer - do not close until it has been constructed. Setup a Forms Timer - who's WM_TIMER message won't be processed until all other form creation messages are.
private System.Windows.Forms.Timer _timer;
protected override void OnLoad(EventArgs args)
{
_timer = new Timer { Interval = 1 };
_timer.Tick += (s, e) => new Thread(CloseForm).Start();
_timer.Start();
base.OnLoad(args);
}
While I feel that there must be a clean way to do this without platform interop, I can't think what it is. In the meantime, here's some code showing an approach that certainly works, assuming you don't mind the p/invoke...
public partial class Form1 : Form
{
private const uint WM_CLOSE = 0x0010;
private IntPtr _myHandle;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var t = new Thread(ThreadProc);
t.Start();
}
protected override void OnHandleCreated(EventArgs e)
{
_myHandle = this.Handle;
base.OnHandleCreated(e);
}
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
private void ThreadProc(object o)
{
Thread.Sleep(5000);
PostMessage(_myHandle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
}
I ran into a similar situation this morning where I was calling Close in an Invoke call and getting the InvalidOperationException when the Close method tried to return. The Invoke method is not able to return a value to the caller since it has been disposed. To sole this problem, I used BeginInvoke instead which allowed my thread to return before the form was closed.
Related
I am creating application that runs another app inside panel.
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
public Form3() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
Process p = Process.Start(#"path\app.exe");
Thread.Sleep(200); // Allow the process to open it's window
SetParent(p.MainWindowHandle, panel1.Handle);
}
But the problem is, that app.exe sometimes (I know when) creates new window as a new app. I want to add this new window into new panel.
private Process GetProcess() {
//do some magic stuff and find actually running app
return NewAppProcess;
}
private void button2_Click(object sender, EventArgs e) {
Process p = GetProcess();
SetParent(p.MainWindowHandle, panel2.Handle);
}
Thanks for everything that can push me to right way
Using ManagementEventWatcher, you can watch Win32_ProcessStartTrace to receive an event when a new process starts.
Example
In this example, I shows how you can watch starting of mspaint.exe and adding it as child of a Panel in your form. To so add a reference to System.Management dll to your project and then use the following code.
Note 1: The watcher is not super fast and you probably see the the window opens in desktop and then sits in the panel.
Note 2: It's an example and showing hot to do it with mspaint.exe. If you have any problem applying the solution on your real app.exe, you need to specifically ask about the solution for your app.exe.
Note 3: Make sure you run your as administrator.
using System.Management;
using System.Runtime.InteropServices;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
ManagementEventWatcher watcher;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
watcher = new ManagementEventWatcher(
"Select * From Win32_ProcessStartTrace Where ProcessName = 'mspaint.exe'");
watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
watcher.Start();
}
void watcher_EventArrived(object sender, EventArrivedEventArgs e)
{
var id = (UInt32)e.NewEvent["ProcessID"];
var process = System.Diagnostics.Process.GetProcessById((int)id);
this.Invoke(new MethodInvoker(() => {
SetParent(process.MainWindowHandle, panel1.Handle);
}));
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
watcher.Stop();
watcher.Dispose();
base.OnFormClosed(e);
}
}
In our .NET Framework based application we save a huge file using a BackgroundWorker to keep UI responsive. When we close it, we don't want to stop the work in background (default behavior) and truncate the file.
Does a more elegant way to wait for its completion exist compared to this one?
while (this.backgroundWorker1.IsBusy)
{
// Keep UI messages moving, so the form remains
// responsive during the asynchronous operation.
Application.DoEvents();
}
Thanks.
EDIT: Basically, what we are trying to achieve, is to see the application to disappear and continue to see a process alive (in the Task Manager) until the background work is finished.
You can use a WaitHandle to keep synchronization with the worker thread.
private ManualResetEvent _canExit = new ManualResetEvent(true);
private DoBackgroundWork()
{
_canExit.Reset();
backgroundWorker1.RunWorkerAsync(_canExit);
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// This foreground thread will keep the process alive but allow UI thread to end.
new Thread(()=>
{
_canExit.WaitOne();
_canExit.Dispose();
}).Start();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ManualResetEvent mre = (ManualResetEvent )e.Argument;
// do your work.
mre.Set();
}
If you have multiple background thread to wait, manage a WaitHanlde collection and use WaitHandle.WaitAll to keep the process from exiting.
There is a difference in closing a form (what the user sees as the application) and closing the actual application. Assuming you currently have the background worker instance as a part of the form, you need to separate it into a different class. You can use a static class for a simple way to do this. Note this has a ManualResetEvent that will be fired when the worker is finished and an Action to override that the worker will invoke rather than specifying the DoWork event in the form.
public static class Background
{
public static ManualResetEvent WorkerResetEvent = new ManualResetEvent(false);
private static BackgroundWorker worker;
public static BackgroundWorker Worker
{
get
{
if (worker == null)
{
worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
}
return worker;
}
}
private static void Worker_DoWork(object sender, DoWorkEventArgs e)
{
WorkerAction?.Invoke();
WorkerResetEvent.Set();
}
public static Action WorkerAction;
}
Now this isn't part of the form, it can persist after the form is closed. You can then keep the application running after the form has closed in a similar loop to the one you have. The simplest way to handle this is to have a 'parent' form that is not displayed but calls your existing form.
public class ParentForm : Form
{
private void ParentForm_Load(object sender, EventArgs e)
{
Visible = false;
ShowInTaskbar = false;
new Form1().ShowDialog(); // execution waits until form is closed
// now wait for worker to finish
while (Background.Worker.IsBusy)
{
Background.ResetEvent.WaitOne(5000); // Waits 5 seconds for the event
}
}
You'll need to call ParentForm instead of Form1 in the Program class:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ParentForm());
}
And to initialise the worker in your form. This also waits for the worker while updating the display but should quit out of the loop when the form is closed where the above loop will take over waiting.
public partial class Form1 : Form
{
private bool closing;
protected override void OnFormClosing(FormClosingEventArgs e)
{
closing = true;
base.OnFormClosing(e);
}
private void Save()
{
Background.WorkerAction = () =>
{
// your saving logic here
};
Background.Worker.RunWorkerAsync();
while (!closing && Background.Worker.IsBusy)
{
Background.WorkerResetEvent.WaitOne(500); // wait half a second at a time (up to you)
// any logic to update progress bar or other progress indicator
Refresh(); // Update the screen without costly DoEvents call
}
}
}
Clearly you missed this: https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.runworkercompleted(v=vs.110).aspx there is a runworkercompleted - which is triggered on completion, cancelled or exception
bgworker.RunWorkerCompleted += JobCompleted;
private void JobCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
MessageBox.Show("Job cancelled");
}
else
{
do_next_stuff();
}
}
I have a WinForm with a backgroundWorker:
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 SeoTools.Utils;
namespace SeoTools.UI
{
public partial class UIProgress : Form
{
public UIProgress(DoWorkEventHandler doWorkEventHandler, RunWorkerCompletedEventHandler runWorkerCompletedEventHandler)
{
InitializeComponent();
this.backgroundWorker.WorkerReportsProgress = true;
this.backgroundWorker.WorkerSupportsCancellation = true;
this.backgroundWorker.DoWork += doWorkEventHandler;
this.backgroundWorker.RunWorkerCompleted += runWorkerCompletedEventHandler;
}
public void Start()
{
var foo = SynchronizationContext.Current;
backgroundWorker.RunWorkerAsync();
}
private void btnStop_Click(object sender, EventArgs e)
{
btnStop.Enabled = false;
btnStop.Text = "Stopping...";
backgroundWorker.CancelAsync();
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
try
{
wdgProgressBar.Value = e.ProgressPercentage;
if (this.Visible == false)
{
this.ShowDialog();
this.Update();
}
}
catch (InvalidOperationException) {}
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Hide(); //Here I get a InvalidOperationException
this.Dispose();
}
}
}
First time I run this it works fine. But second time I get InvalidOperationException when calling this.Hide().
"Additional information: Cross-thread operation not valid: Control 'UIProgress' accessed from a thread other than the thread it was created on."
The weird thing is on first run foo in Start() is a WindowsFormsSyncronizationContext but on the second try it's a System.Threading.SyncronizationContext.
The application I'm writing is a ExcelDna plugin.
EDIT
Start() is called like this:
UIProgress uiProgress = new UIProgress(
delegate(object sender, DoWorkEventArgs args)
{
....
},
delegate(object sender, RunWorkerCompletedEventArgs args)
{
...
}
);
uiProgress.Start();
Your Start() method must be called from code that runs on the UI thread to allow the BackgroundWorker to operate correctly. It was not when you get this exception. Add protective code to your method so you can diagnose this mishap:
public void Start()
{
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) {
throw new InvalidOperationException("Bug! Code called from a worker thread");
}
backgroundWorker.RunWorkerAsync();
}
Now you can set a breakpoint on the throw statement and use the debugger's Call Stack window to find out why this happened.
You are calling UI operation on background thread. This is the reason for that exception. I would use entirely different method to make the progress form the best one is to use Task with IProgress. The other way it to use this:
private void backgroundWorker_ProgressChanged( object sender , ProgressChangedEventArgs e )
{
this.UpdateOnMainThread(
( ) =>
{
wdgProgressBar.Value = e.ProgressPercentage;
if ( this.Visible == false )
{
this.ShowDialog( );
this.Update( );
}
} );
}
private void UpdateOnMainThread( Action action )
{
if ( this.InvokeRequired )
{
this.BeginInvoke( ( MethodInvoker ) action.Invoke);
}
else
{
action.Invoke( );
}
}
private void backgroundWorker_RunWorkerCompleted( object sender , RunWorkerCompletedEventArgs e )
{
this.UpdateOnMainThread(
( ) =>
{
this.Hide( ); //Here I get a InvalidOperationException
this.Dispose( );
} );
}
Use the BeginInvoke() method on the form:
//http://msdn.microsoft.com/en-us/library/0b1bf3y3(v=vs.110).aspx
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.BeginInvoke(new InvokeDelegate(InvokeMethod));
}
public delegate void InvokeDelegate();
public void InvokeMethod()
{
this.Hide();
this.Dispose();
}
I think you can find some help here: BackgroundWorker hide form window upon completion .
However don't forget to detach BackgroundWorker events and stop BackgroundWorker it self like explained in here: Proper way to Dispose of a BackGroundWorker .
The problem can be in the
this.Dispose();
in the backgroundWorker_RunWorkerCompleted event. With that you are disposing form page. Is that what you want to do? Or you want to dispose BackgroundWorker? Disposing form page all resources are released so doing this.Hide(); a second time can be a mistake.
For more info, you can see this links: C# Form.Close vs Form.Dispose and Form.Dispose Method
You must check this link
How to update the GUI from another thread in C#?
Probably have all the possible answers
Hope this Help
You're running a call to the main thread from a thread that can't manipulate UI.
The simplest way is to use anonymous delegate invoke.
Change this:
if (this.Visible == false)
{
this.ShowDialog();
this.Update();
}
For this:
this.Invoke((MethodInvoker) delegate {
if (this.Visible == false)
{
this.ShowDialog();
this.Update();
}
});
It's not the most optimized way but does the job awesomely fast without much recode. :)
hey i am new to c# plz help.
i am writing a program that sorts data in a file and it is a time consuming process so i thought that i should run it in a separate thread and since it has alot of step so i made a new class for it. the problem is that i want to show the progress in the main GUI and i know for that i have to use Invoke function but the problem is that the form control variables are not accessible it this class. what should i do ??????
sample code:
public class Sorter
{
private string _path;
public Sorter(string path)
{
_path = path;
}
public void StartSort()
{
try
{
processFiles(_path, "h4x0r"); // Just kidding
}
catch (Exception e)
{
MessageBox.Show("Error: " + e.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void processFiles(string Dir, string[] key)
{
/* sorting program */
}
and it is used as
public partial class Form1 : Form
{
Sorter sort;
public Form1()
{
InitializeComponent();
}
private void browseBtn_Click(object sender, EventArgs e)
{
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
textBox1.Text = folderBrowserDialog1.SelectedPath;
}
private void startBtn_Click(object sender, EventArgs e)
{
if (startBtn.Text == "Start Sorting")
{
Thread worker = new Thread(new ThreadStart(delegate() {
sort = new Sorter(textBox1.Text);
sort.StartSort(); }));
worker.start();
}
else
MessageBox.Show("Cancel");//TODO: add cancelling code here
}
}
plz help..
Add an Event to your class that is doing the multi-threaded work, that triggers when the progress changes. Have your form subscribe to this event and update the progress bar.
Note ProgressEventArgs is a little class that inherits EventArgs and has an Integer for the progress.
// delegate to update progress
public delegate void ProgressChangedEventHandler(Object sender, ProgressEventArgs e);
// Event added to your worker class.
public event ProgressChangedEventHandler ProgressUpdateEvent
// Method to raise the event
public void UpdateProgress(object sender, ProgressEventArgs e)
{
ProgressChangedEventHandler handler;
lock (progressUpdateEventLock)
{
handler = progressUpdateEvent;
}
if (handler != null)
handler(sender, e);
}
I would recommend you read up on the BackgroundWorker class. It is exactly for the problem you are trying to solve and makes things a lot easier than doing manual threading yourself.
Brief Example
public Form1()
{
InitializeComponent();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
}
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void btnStart_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i < 101; ++i)
{
if (backgroundWorker.CancellationPending)
{
e.Cancel = true;
break;
}
else
{
//Sort Logic is in here.
Thread.Sleep(250);
backgroundWorker.ReportProgress(i);
}
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy && backgroundWorker.WorkerSupportsCancellation)
backgroundWorker.CancelAsync();
}
You could do something like this:
public delegate void StatusReporter(double progressPercentage);
public class MainClass
{
public void MainMethod()
{
Worker worker = new Worker(ReportProgress);
ThreadStart start = worker.DoWork;
Thread workThread = new Thread(start);
workThread.Start();
}
private void ReportProgress(double progressPercentage)
{
//Report here!!!
}
}
public class Worker
{
private readonly StatusReporter _reportProgress;
public Worker(StatusReporter reportProgress)
{
_reportProgress = reportProgress;
}
public void DoWork()
{
for (int i = 0; i < 100; i++ )
{
// WORK, WORK, WORK
_reportProgress(i);
}
}
}
There are a few option available to solve this sort of issue. In any case, you will have to fiddle with Invoke to get the UI to update.
You could...
...add an event that fires on your new class which your UI can listen to, and Invoke as applicable - you'd still need to pass the data to your worker class (by constructor, properties, method call, etc)
...keep the method as a method on your form, and pas that to start your new thread from (after all, a new thread doesn't have to be starting in a different class)
...change the access modifiers on your controls to be (say) internal such that any class within the same assembly can Invoke changes to the controls, or read from them.
...make your worker class a child of the form it needs to access - it can then see the privates of its parent, as long as it is passed a reference to the instance.
I have 2 buttons on page, where button1.visible = false.
Page code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
Class1.ShowButtonEvent += new Class1.ShowButton(Show);
}
}
public void Show()
{
Button1.Visible = true;
}
protected void Button2_Click(object sender, EventArgs e)
{
Class1.EventIT();
}
Class1:
public static class Class1
{
public delegate void ShowButton();
public static event ShowButton ShowButtonEvent;
public static void EventIT()
{
if (ShowButtonEvent != null)
ShowButtonEvent();
}
}
This is not working, how to fix it?
UPDATE:
No answers?
Ok, I will try to be more specific and explain what I am trying to do.
I start a time consuming job in a thread within static class.
I update the status (jquery progressbar) on UI using webmethods and javascript.
But, when thread is finished I need to preform some server side operations that it's not possible to do via javascript.
So the task is to invoke server side methods when thread finishes it's work.
You need to bind the event (Class1.ShowButtonEvent += new Class1.ShowButton(Show);
) on every postback.