我在Windows窗体中的跨线程调用有什么问题?

我遇到Windows窗体应用程序的问题。

必须从另一个线程显示一个表单。 所以在表单类中,我有以下代码:

private delegate void DisplayDialogCallback(); public void DisplayDialog() { if (this.InvokeRequired) { this.Invoke(new DisplayDialogCallback(DisplayDialog)); } else { this.ShowDialog(); } } 

现在,每次运行它时,都会在this.ShowDialog();行上抛出InvalidOperationException this.ShowDialog();

“跨线程操作无效:控制’SampleForm’从其创建的线程以外的线程访问。”

这段代码出了什么问题? 这不是一种有效的跨线程调用方式吗? ShowDialog()有什么特别之处吗?

试试这个:

 private delegate void DisplayDialogCallback(); public void DisplayDialog() { if (this.InvokeRequired) { this.Invoke(new DisplayDialogCallback(DisplayDialog)); } else { if (this.Handle != (IntPtr)0) // you can also use: this.IsHandleCreated { this.ShowDialog(); if (this.CanFocus) { this.Focus(); } } else { // Handle the error } } } 

请注意InvokeRequired返回

如果控件的句柄是在与调用线程不同的线程上创建的,则为true(表示必须通过调用方法调用控件); 否则,是的。

因此,如果尚未创建控件,则返回值将为false

您可能在表单显示之前执行此代码。
因此, InvokeRequired返回false

我相信这里发生的事情是这个代码是在Form显示之前运行的。

在.Net中创建Form ,它不会立即获得对特定线程的亲和力。 只有在执行某些操作时,如显示它或抓住手柄才能获得亲和力。 在此之前, InvokeRequired很难正常运行。

在这种特殊情况下,没有建立关联,也没有父控件,因此InvokeRequired返回false,因为它无法确定原始线程。

解决此问题的方法是在UI线程上创建控件时建立与控件的关联。 执行此操作的最佳方法是向控件询问其handle属性。

 var notUsed = control.Handle; 

您可能在显示表单之前获取此代码,因此尚未创建窗口句柄。

您可以在代码之前添加此代码,所有代码都应该是好的:

 if (! this.IsHandleCreated) this.CreateHandle(); 

编辑:您的代码还有另一个问题。 显示表单后,您无法再次调用ShowDialog()。 您将收到无效的操作exception。 您可能希望像其他人提议的那样修改此方法。

您最好直接从调用类调用ShowDialog(),并为BringToFront()或类似的东西提供另一种方法…

您总是可以尝试针对不同的控件进行测试。

例如,您可以访问Application.Forms集合

 public Control GetControlToInvokeAgainst() { if(Application.Forms.Count > 0) { return Application.Forms[0]; } return null; } 

然后在您的DisplayDialog()方法中,在尝试执行invokerequired调用之前调用GetControlToInvokeAgainst()并测试null。

很可能尚未创建控件的句柄,在这种情况下, Control.InvokeRequired返回false

检查Control.IsHandleCreated属性以查看是否是这种情况。

我也认为SLaks是正确的。 来自msdn( http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx ):

如果找不到合适的句柄,则InvokeRequired方法返回false。

如果它可能在你的情况下,我会尝试在单个方法中组合创建和显示控件,即:

 public DisplayDialog static Show() { var result = new DisplayDialog; //possibly cache instance of the dialog if needed, but this could be tricky result.ShowDialog(); return result; } 

你可以从另一个线程调用Show