我必须从一个线程关闭一个Form,我使用Form的Invoke方法来调用Close()方法。
问题是,当关闭时,表单被释放,我得到一个InvalidOperationExecption,消息“在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。”。
只有在Close方法中使用“Step Into”进行调试时才会出现此异常,但我不想冒险在正常运行时出现错误。
这是重现它的示例代码:
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.
}
));
}
表单在表单的自动生成代码中处理(我不想修改):
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
如果有人可以通过这种或其他方式从另一个帖子中关闭表单,我会很感激。
答案 0 :(得分:5)
使用此方法:
// 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();
}
}
用法示例:
Functions.InvokeIfRequired(anyControl, (MethodInvoker)delegate()
{
// UI stuffs
});
答案 1 :(得分:1)
到目前为止,针对此案例的最佳解决方案是使用SynchronizationContext机制。我在Should I use Invoke or SynchronizationContext to update form controls from another thread?获得了提示。
示例代码如下:
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();
}
答案 2 :(得分:0)
最明显的评论是 - 没有明显的理由说明为什么在完成加载之前需要关闭表单。还有其他更好的方法来处理任何原因。
然而,你问过......
错误为您提供答案 - 在构建之前不要关闭。设置表单计时器 - 在所有其他表单创建消息出现之前,不会处理谁的WM_TIMER消息。
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);
}
答案 3 :(得分:0)
虽然我觉得必须有一个干净的方法来做这个没有平台互操作,我想不出它是什么。与此同时,这里有一些代码显示了一种肯定有效的方法,假设你不介意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);
}
}
答案 4 :(得分:0)
今天早上我遇到了类似的情况,我在Invoke调用中调用Close,并在Close方法尝试返回时获取InvalidOperationException。 Invoke方法无法将值返回给调用者,因为它已被释放。为了解决这个问题,我使用了BeginInvoke,它允许我的线程在表单关闭之前返回。