Cross-thread operation not valid...Invoke and delegate seems useless - c#

I have a issue with thread, I've searched for a few days but still cannot solve it..
Due to some reason, I customize a progress form and use it in threads.
I tried to write all functions inside the progress form so that they are wrapped by Invoke and delegate. Unfortunately, this code is not working properly since this.InvokeRequired is returning false when I expected it to return true.
The problem is, when I execute the program, sometimes it throw an exception:
Cross-thread operation not valid: Control 'FormProgress' accessed from a thread other than the thread it was create on.
Here's the code of progress form.
I've wrapped all functions with Invoke and delegate.
public partial class FormProgress : Form
{
public FormProgress()
{
InitializeComponent();
}
public void SetStatusLabelText(string text)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker) delegate
{
label1.Text = text;
});
}
else
{
// exception thrown here
label1.Text = text;
}
}
public void SetDialogResult(DialogResult dialogResult)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
if (DialogResult == DialogResult.None)
this.DialogResult = dialogResult;
});
}
else
{
if (DialogResult == DialogResult.None)
this.DialogResult = dialogResult;
}
}
}
Here's the code of thread, the exception throws when I click button1
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
for (int i=0; i<100; i++)
ProgressTest();
}
private void ProgressTest()
{
FormProgress dialog = new FormProgress();
{
Thread threadTest = new Thread(delegate ()
{
dialog.SetStatusLabelText("initial....(1)");
Thread.Sleep(50);
dialog.SetStatusLabelText("initial....(2)");
Thread.Sleep(50);
dialog.SetStatusLabelText("initial....(3)");
Thread.Sleep(50);
dialog.SetDialogResult(DialogResult.OK);
});
threadTest.Name = "ThreadTest";
threadTest.Start();
if (dialog.ShowDialog() == DialogResult.Cancel)
{
if (threadTest.IsAlive)
threadTest.Abort();
}
threadTest.Join();
}
}
}

As per the docs:
If the control's handle does not yet exist, InvokeRequired searches up
the control's parent chain until it finds a control or form that does
have a window handle. If no appropriate handle can be found, the
InvokeRequired method returns false.
This means that InvokeRequired can return false if Invoke is not
required (the call occurs on the same thread), or if the control was
created on a different thread but the control's handle has not yet
been created.
In the case where the control's handle has not yet been created, you
should not simply call properties, methods, or events on the control.
This might cause the control's handle to be created on the background
thread, isolating the control on a thread without a message pump and
making the application unstable.
You can protect against this case by also checking the value of
IsHandleCreated when InvokeRequired returns false on a background
thread. If the control handle has not yet been created, you must wait
until it has been created before calling Invoke or BeginInvoke.
Typically, this happens only if a background thread is created in the
constructor of the primary form for the application (as in
Application.Run(new MainForm()), before the form has been shown or
Application.Run has been called.
The issue you have is that some of your InvokeRequired calls may be occurring before the form has been shown. This is because you are starting your new thread before calling dialog.ShowDialog(). Note, as is common with race conditions, the problem won't always occur - just sometimes.
As per above, you may want to consider checking IsHandleCreated before executing the logic in your else blocks (after checking InvokeRequired) to protect against this possibility. Alternatively, rethink your entire strategy around the progress form.

do changes with controls inside if (this.InvokeRequired) block
remove the else block staying after if (this.InvokeRequired)
public void SetStatusLabelText(string text)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker) delegate
{
label1.Text = text;
});
}
}
public void SetDialogResult(DialogResult dialogResult)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
if (DialogResult == DialogResult.None)
this.DialogResult = dialogResult;
});
}
}
let's consider method ProgressTest(), what happaning:
after threadTest.Start() has been called , the threadTest method starts execution of his work item in a new thread
after dialog.ShowDialog() the GUI thread become blocked , it makes this.InvokeRequired = false
at the same time threadTest keep working and when threadTest try to execute
else
{
label1.Text = text;
}
label1.Text setter is called from NONE GUI thread (it is called from "ThreadTest" thread), that's why you get exception
It should be noted that dialog.SetStatusLabelText("initial....") which supposed to be called 300 times , actually will be called less then 300

Related

InvokeRequired=true but BeginInvoke fails causing freeze

C# windows forms VS 2013 OS:Win7
I am having an interesting problem where invokeRequired is true but when I call beginInvoke() it never executes and the window never closes.
However when I remove beingInvoke() altogether the window closes ok.
public void CloseMyForm()
{
//if I remove this if block altogether including beingInvoke() the window closes ok
if ( !this.IsDisposed && this.InvokeRequired && this.IsHandleCreated )
{
log("callin begininvoke()"); //this is logged correctly
this.BeginInvoke((MethodInvoker)delegate() { CloseMyForm(); });
return;
}
log("outside of begin invoke"); //this is never logged
this.Close();
}
CloseMyForm is called by a separate thread which is created like this at the startup. Please note this is not the main window but a separate window open from the main form.
Thread connectThread = new Thread(new ThreadStart(CheckWhenToCloseMyForm));
public void CheckWhenToCloseMyForm()
{
while (true)
{
CallSomeFunc();
CallSomeFunc1();
if (allconditionsmet)
{
System.Threading.Thread.Sleep(1000);
CloseMyForm();
break;
}
}
}
The BeginInvoke is made available via the base Control class.
Executes a delegate asynchronously on the thread that the control's underlying handle was created on
If the InvokedRequired property is actually true, that means "the caller must call an invoke method when making method calls to the control because the caller is on a different thread than the one the control was created on".
It looks like you're incorrectly calling BeginInvoke, you should try calling Invoke instead.
Executes the specified delegate on the thread that owns the control's underlying window handle
public void CloseMyForm()
{
if (!this.IsDisposed && this.InvokeRequired && this.IsHandleCreated)
{
log("calling invoke()");
this.Invoke((MethodInvoker)delegate() { CloseMyForm(); });
}
else
{
log("outside of invoke"); // this is never logged
this.Close();
}
}
Check out this neat little extension method that could help simplify this. With this you could write your close method like this instead.
public void CloseMyForm()
{
this.ThreadSafeInvoke(() => this.Close());
}
Okay, now that you have provided this snippet I understand the issue.
Thread connectThread = new Thread(new ThreadStart(CheckWhenToCloseMyForm));
public void CheckWhenToCloseMyForm()
{
while (true)
{
CallSomeFunc();
CallSomeFunc1();
if (allconditionsmet)
{
System.Threading.Thread.Sleep(1000);
CloseMyForm()
}
}
}
In your while loop you need to break or return after you invoke CloseMyForm. That's it...very simple. You can use either BeginInvoke or Invoke.

Closing the Form opened in another thread

I'm getting some troubles with my Winforms C# app.
I wish to make form named Popup closing after some operations in main thread are done. The problem is an exception caused by cross-thread form closing.
private void loginButton_Click(object sender, EventArgs e)
{
LoginProcess.Start(); // Running Form.show() in new thread
ActiveAcc.IsValid = false;
ActiveAcc.Username = userBox.Text;
try
{
LoginCheck(userBox.Text, passBox.Text);
}
catch (IOException)
{
MessageBox.Show("..");
return;
}
catch (SocketException)
{
MessageBox.Show("..");
return;
}
if (ActiveAcc.IsValid)
{
MessageBox.Show("..");
Close();
}
else
{
Popup.Close(); // Error caused by closing form from different thread
MessageBox.Show("");
}
}
public Login() // 'Main' form constructor
{
InitializeComponent();
ActiveAcc = new Account();
Popup = new LoginWaiter();
LoginProcess = new Thread(Popup.Show); //Popup is an ordinary Form
}
I've been trying to use various tools such as LoginProcess.Abort() or Popup.Dispose() to make it work properly, but even if app is working on runtime environment its still unstable due to Exceptions which are thrown.
I would be grateful for any help, and I am sorry for ambiguities in issue describing.
Why don't you let the UI thread do UI stuff like opening and closing Forms, and spawn the other thread (or background worker, or async task) to do the other stuff?
IMO, having other threads attempt to interact with elements on the UI thread (e.g., have a background thread directly set the text of a label or some such) is asking for heartache.
If you simply must keep your code as is, here is a fairly simple thing you could do. In Popup, add a static bool that defaults to true. Also in Popup, add a timer task that once every X milliseconds checks the status of that boolean. If it finds that the value has been set to false, let Popup tell itself to close within that timer tick.
I'm not crazy about this design, but it could look something like:
public partial class Popup : Form
{
public static bool StayVisible { get; set; }
private System.Windows.Forms.Timer timer1;
public Popup()
{
StayVisible = true;
this.timer1.Interval = 1000;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
if (!StayVisible) this.Close();
}
}
Then, from another thread, when you want Popup to close, call
Popup.StayVisible = false;
Better yet, you would fire an event that Popup would receive so that it could close itself. Since you intend to use multiple threads, you'll have to deal with raising events cross-thread.

Updating a dialog from another form

I know there are a lot of similar questions out there and I have read through a lot of them. Unfortunately, I still couldn't solve my problem after reading through them - however I am relatively new to C#. According to docs the problem of not being thread safe results in an InvalidOperationException when using the debugger. This is not the case in my problem.
I've recreated the problem with a simple raw test class to concentrate on my problem.
The main form is supposed to show a kind of a progress dialog.
public partial class ImportStatusDialog : Form
{
public ImportStatusDialog()
{
InitializeComponent();
}
public void updateFileStatus(string path)
{
t_filename.Text = path;
}
public void updatePrintStatus()
{
t_printed.Text = "sent to printer";
}
public void updateImportStatus(string clientName)
{
t_client.Text = clientName;
}
public void updateArchiveStatus()
{
t_archived.Text = "archived";
}
}
When that code is called without any Invoke() from the main form:
private void button1_Click(object sender, EventArgs e)
{
ImportStatusDialog nDialog = new ImportStatusDialog();
nDialog.Show();
nDialog.updateFileStatus("test");
Thread.Sleep(1000);
nDialog.updateImportStatus("TestClient");
Thread.Sleep(1000);
nDialog.updatePrintStatus();
Thread.Sleep(1000);
nDialog.updateArchiveStatus();
Thread.Sleep(1000);
nDialog.Close();
}
And even when I call it like this:
private void button3_Click(object sender, EventArgs e)
{
ImportStatusDialog nDialog = new ImportStatusDialog();
nDialog.Show();
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
nDialog.updateFileStatus("Test");
});
}
else
{
nDialog.updateFileStatus("Test");
}
Thread.Sleep(1000);
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
nDialog.updatePrintStatus();
});
}
else
{
nDialog.updatePrintStatus();
}
Thread.Sleep(1000);
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
nDialog.updateImportStatus("cName");
});
}
else
{
nDialog.updateImportStatus("cName");
}
Thread.Sleep(1000);
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
nDialog.updateArchiveStatus();
});
}
else
{
nDialog.updateArchiveStatus();
}
Thread.Sleep(1000);
nDialog.Close();
}
the dialog which looks like this in the designer (in my example)
will be displayed like that:
When I use ShowDialog() instead of Show() the dialog displays correnctly, but as the API Doc points out
You can use this method to display a modal dialog box in your
application. When this method is called, the code following it is not
executed until after the dialog box is closed
which is not what I want, especially as it would mean that the dialog updating would only happen after it has been closed again.
What am I doing wrong here? This seems to be a trivial problem and yet the solution evades me. Please keep in mind that I am new to C# GUI programming.
Also, I would like to ask what would be the right place for using the Invoke()? Do Would you use it in the main form when calling the dialog methods or rather in the dialog update methods itself?
It doesn't look like you're using multiple threads here, so the invoke stuff is not required. Invoke is only needed for cross-thread calls - you are creating multiple forms, but all your code is running in the same thread.
If you're doing your work in the main UI thread (as implied by the code being in a button click event), then just call Application.DoEvents() periodically to allow the progress form to refresh.
A better solution would be to use a BackgroundWorker for your work, and have it report its progress periodically.
Then you can update the progress form in the BackgroundWorker's ProgressChanged event (which will be executed in the main thread, so you still don't have to invoke anything).

How to enable form button after process has exited?

I have an windows application developed using C#. In this application, I am creating one process. I want to enable and disable few buttons when Process_Exited() event occures.
In Process_Exited() method, I have written code to enable buttons but at runtime I get error as
"Cross-thread operation not valid:
Control
'tabPage_buttonStartExtraction'
accessed from a thread other than the
thread it was created on."
My code snippet is :
void rinxProcess_Exited(object sender, EventArgs e)
{
tabPage_buttonStartExtraction.Enabled = true;
tabPageExtraction_StopExtractionBtn.Enabled = false;
}
Can anyone suggest how to make this possible?
Move the enable/disable lines in a separate method and call that method from rinxProcess_Exited using Control.Invoke method.
You're attempting to change the UI from a different thread.
Try something like this;
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
You shouldn't be doing much work on the UI from another thread, as the invocations are quite expensive.
Source: http://msdn.microsoft.com/en-us/library/ms171728.aspx
You must make UI changes on the UI thread. See this question for more details.
Here's the solution applied to your example:
void rinxProcess_Exited(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke((Action)(() => ProcessExited()));
return;
}
ProcessExited();
}
private void ProcessExited()
{
tabPage_buttonStartExtraction.Enabled = true;
tabPageExtraction_StopExtractionBtn.Enabled = false;
}

Avoid calling Invoke when the control is disposed

I have the following code in my worker thread (ImageListView below is derived from Control):
if (mImageListView != null &&
mImageListView.IsHandleCreated &&
!mImageListView.IsDisposed)
{
if (mImageListView.InvokeRequired)
mImageListView.Invoke(
new RefreshDelegateInternal(mImageListView.RefreshInternal));
else
mImageListView.RefreshInternal();
}
However, I get an ObjectDisposedException sometimes with the Invoke method above. It appears that the control can be disposed between the time I check IsDisposed and I call Invoke. How can I avoid that?
What you have here is a race condition. You're better off just catching the ObjectDisposed exception and be done with it. In fact, I think in this case it is the only working solution.
try
{
if (mImageListView.InvokeRequired)
mImageListView.Invoke(new YourDelegate(thisMethod));
else
mImageListView.RefreshInternal();
}
catch (ObjectDisposedException ex)
{
// Do something clever
}
There are implicit race conditions in your code. The control can be disposed between your IsDisposed test and the InvokeRequired test. There's another one between InvokeRequired and Invoke(). You can't fix this without ensuring the control outlives the life of the thread. Given that your thread is generating data for a list view, it ought to stop running before the list view disappears.
Do so by setting e.Cancel in the FormClosing event and signaling the thread to stop with a ManualResetEvent. When the thread completes, call Form.Close() again. Using BackgroundWorker makes it easy to implement the thread completion logic, find sample code in this post.
The reality is that with Invoke and friends, you can't completely protect against invoke on a disposed component, or then getting InvalidOperationException because of the missing handle. I haven't really seen an answer yet, like the one farther below, in any of the threads that addresses the real fundamental problem, which cant be completely solved by preemptive testing or using lock semantics.
Here's the normal 'correct' idiom:
// the event handler. in this case preped for cross thread calls
void OnEventMyUpdate(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return; // ignore events if we arn't ready, and for
// invoke if cant listen to msg queue anyway
if (InvokeRequired)
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
else
this.MyUpdate(e.MyData);
}
// the update function
void MyUpdate(Object myData)
{
...
}
The fundemental problem:
In using the Invoke facility the windows message queue is used, which places a message in the queue to either wait or fire-and-forget the cross thread call exactly like Post or Send message. If there is a message ahead of the Invoke message that will invalidate the component and its window handle, or that got placed just after any checks you try to perform, then you are going to have a bad time.
x thread -> PostMessage(WM_CLOSE); // put 'WM_CLOSE' in queue
y thread -> this.IsHandleCreated // yes we have a valid handle
y thread -> this.Invoke(); // put 'Invoke' in queue
ui thread -> this.Destroy(); // Close processed, handle gone
y thread -> throw Invalid....() // 'Send' comes back, thrown on calling thread y
There is no real way to know that the control is about to remove itself fromthe queue, and nothing really reasonable you can do to "undo" the invoke. No matter how many checks you do or extra locks you make, you cant stop someone else form issuing something like a close, or deactivate. There are tons of senarios where this can happen.
A solution:
The first thing to realize is that the invoke is going to fail, no different than how a (IsHandleCreated) check would have ignored the event. If the goal is to protect the caller on the non-UI thread you will need to handle the exception, and treat it like any other call that didn't succeed (to keep app from crashing or do whatever. And unless going to rewrite/reroll Invoke facility, the catch is your only way to know.
// the event handler. in this case preped for cross thread calls
void OnEventMyWhatever(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return;
if (InvokeRequired)
{
try
{
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
}
catch (InvalidOperationException ex) // pump died before we were processed
{
if (this.IsHandleCreated) throw; // not the droids we are looking for
}
}
else
{
this.MyUpdate(e.MyData);
}
}
// the update function
void MyUpdate(Object myData)
{
...
}
The exception filtering can be tailored to suit whatever the needs are. Its good to be aware that worker threads often dont have all the cushy outer exception handling and logging the UI threads do, in most applicaitons, so you may wish to just gobble up any exception on the worker side. Or log and rethrow all of them. For many, uncaught exceptions on worker thread means the app is going to crash.
Try using
if(!myControl.Disposing)
; // invoke here
I had the exact same problem as you. Ever since I switched to checking .Disposing on the control, the ObjectDisposedException has gone away. Not saying this will fix it 100% of the time, just 99% ;) There is still a chance of a race condition between the check to Disposing and the call to invoke, but in the testing I've done I haven't ran into it (I use the ThreadPool and a worker thread).
Here's what I use before each call to invoke:
private bool IsControlValid(Control myControl)
{
if (myControl == null) return false;
if (myControl.IsDisposed) return false;
if (myControl.Disposing) return false;
if (!myControl.IsHandleCreated) return false;
if (AbortThread) return false; // the signal to the thread to stop processing
return true;
}
may be lock(mImageListView){...} ?
You could use mutexes.
Somewhere at the start of the thread :
Mutex m=new Mutex();
Then :
if (mImageListView != null &&
mImageListView.IsHandleCreated &&
!mImageListView.IsDisposed)
{
m.WaitOne();
if (mImageListView.InvokeRequired)
mImageListView.Invoke(
new RefreshDelegateInternal(mImageListView.RefreshInternal));
else
mImageListView.RefreshInternal();
m.ReleaseMutex();
}
And whereever it is you are disposing of mImageListView :
m.WaitOne();
mImageListView.Dispose();
m.ReleaseMutex();
This should ensure you cant dispose and invoke at the same time.
See also this question:
Avoiding the woes of Invoke/BeginInvoke in cross-thread WinForm event handling?
The utility class that resulted EventHandlerForControl can solve this problem for event method signatures. You could adapt this class or review the logic therein to solve the issue.
The real problem here is that nobugz is correct as he points out that the APIs given for cross-thread calls in winforms are inherently not thread safe. Even within the calls to InvokeRequired and Invoke/BeginInvoke themselves there are several race conditions that can cause unexpected behavior.
If a BackGroundWorker is a possibility, there's a very simple way to circumvent this:
public partial class MyForm : Form
{
private void InvokeViaBgw(Action action)
{
BGW.ReportProgress(0, action);
}
private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (this.IsDisposed) return; //You are on the UI thread now, so no race condition
var action = (Action)e.UserState;
action();
}
private private void BGW_DoWork(object sender, DoWorkEventArgs e)
{
//Sample usage:
this.InvokeViaBgw(() => MyTextBox.Text = "Foo");
}
}
Handle the Form closing event. Check to see if your off UI thread work is still happening, if so start to bring it down, cancel the closing event and then reschedule the close using BeginInvoke on the form control.
private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
if (service.IsRunning)
{
service.Exit();
e.Cancel = true;
this.BeginInvoke(new Action(() => { this.Close(); }));
}
}
The solution proposed by Isak Savo
try
{
myForm.Invoke(myForm.myDelegate, new Object[] { message });
}
catch (ObjectDisposedException)
{ //catch exception if the owner window is already closed
}
works in C# 4.0 but for some reasons it fails in C#3.0 (the exception is raised anyway)
So I used another solution based on a flag indicating if the form is closing and consequently preventing the use of invoke if the flag is set
public partial class Form1 : Form
{
bool _closing;
public bool closing { get { return _closing; } }
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_closing = true;
}
...
// part executing in another thread:
if (_owner.closing == false)
{ // the invoke is skipped if the form is closing
myForm.Invoke(myForm.myDelegate, new Object[] { message });
}
This has the advantage of completely avoiding the use of try/catch.
One way might be to call the method itself ones more instead of invoking the ImageListView-Method:
if (mImageListView != null &&
mImageListView.IsHandleCreated &&
!mImageListView.IsDisposed)
{
if (mImageListView.InvokeRequired)
mImageListView.Invoke(new YourDelegate(thisMethod));
else
mImageListView.RefreshInternal();
}
That way it would check one more time before finally calling RefreshInternal().
The suggestion to stop the thread generating the messages is not acceptable. Delegates can be multicast. Because one listener does not want to listen to the band, you don't shoot the band members.
Since the framework doesn't provide any easy way I know of to clear the message pump of those event messages, and since the form does not expose its private property that lets us know the form is closing:
Set a flag on the IsClosing Event of the window after you unsubscribe or stop listening to the events, and always check this flag before you do a this.Invoke().
i have same error. my error occurred in thread. finally i write this method :
public bool IsDisposed(Control ctrl)
{
if (ctrl.IsDisposed)
return true;
try
{
ctrl.Invoke(new Action(() => { }));
return false;
}
catch (ObjectDisposedException)
{
return true;
}
}
This works for me
if (this.IsHandleCreated){
Task.Delay(500).ContinueWith(_ =>{
this.Invoke(fm2);
});
} else {
this.Refresh();
}

Categories

Resources