问题
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.
回答1:
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
});
回答2:
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();
}
回答3:
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);
}
回答4:
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);
}
}
回答5:
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.
来源:https://stackoverflow.com/questions/10084691/close-a-form-from-an-external-thread-using-the-invoke-method