【发布时间】:2011-11-25 10:39:27
【问题描述】:
在我的应用程序中,我启动了一个隐藏的dummyForm,它只是为了跟踪主 UI 线程而创建的。因此,如果即将创建一个新表单 InvokeRequired 在虚拟表单上使用,以确保我们在创建新表单时位于主 UI 线程上。
在实例化我的frmStart 表单后直接检查frmStart.InvokeRequired 并将其设置为false,因此无需在此处调用(dummyForm.InvokeRequired 也是如此)。
然后我得到了一个frmMyDialog,它将使用frmStart 作为父母/所有者,如下所示:
using(Create frmMyDialog on main UI thread)
{
frmMyDialog.Show(frmStart);
}
这会抛出一个跨线程异常,这里奇怪的是:
frmMyDialog.InvokeRequired = false
dummyForm.InvokeRequired = false
frmStart.InvokeRequired = true
即使我在创建frmStart 时检查dummyForm.InvokeRequired 是否为假?
frmMyDialog.InvokeRequired 的值应始终与dummyForm.InvokeRequired 相同?这里发生了什么?
我检查了frmStart 和dummyForm 在创建第一个实例后根本没有重新创建。
编辑1:
这是应用程序的启动方式:
public static void Main(string[] args)
{
_instance = new MyClientMain(parameters);
Application.Run(_instance);
}
MyClientMain 类的构造函数将在名为 MainControl 的静态类上运行安装程序。 MainControler 将在 setup 方法中实例化一个像这样的虚拟表单:
if (_dummyForm == null)
_dummyForm = new Form();
完成此操作后,登录表单将处理登录,并且此表单是多线程的。登录完成后,将再次调用 MainController 以实例化并打开包含 frmStart 的主 MDI 窗口。为确保我们在同一个线程上,请执行以下操作:
public static StartApplication()
{
if (_dummyForm.InvokeRequired)
_dummyForm.Invoke(new MethodInvoker(delegate { OpenMainOrbitWindow(); }));
//Instanciate mainform and frmStart then open mainForm with frmStart as a MDI child
}
这里没有多线程。
然后,当服务离线时,将触发一个事件,我需要弹出一个 frmMyDialog,但是当使用 .ShowDialog() 时,它的对话框将放置在表单后面,因此最能找到父/所有者并设置如下:
public static Form GetActiveForm()
{
Form activeForm = Form.ActiveForm;
if (activeForm != null)
return activeForm;
if (MainOrbitForm.TopMost)
return MainOrbitForm;
else
{
FormCollection openForms = Application.OpenForms;
for (int i = 0; i < openForms.Count && activeForm == null; ++i)
{
Form openForm = openForms[i];
if (openForm.IsMdiContainer)
return openForm.ActiveMdiChild;
}
}
if (_patientForm != null)
{
if (_patientForm.TopMost)
return _patientForm;
}
return null;
}
public static string ShowOrbitDialogReName()
{
frmMyDialog myDialog;
Form testForm;
//Makes sures that the frmOrbitDialog is created with the same thread as the dummyForm
//InvokeRequired is used for this
using (myDialog = MainController.CreateForm<frmOrbitDialog>())
{
//Settings...
testForm = GetActiveForm();
myDialog.ShowDialog(GetActiveForm(testForm));
}
}
问题是
myDialog.InvokeRequired = false
testForm.InvokeRequired = true;
MainController.DummyForm.InvokeRequired = false;
编辑2: 启动并创建虚拟表单:
dummyForm.InvokeRequired = false
Thread.CurrentThread.ManagedThreadId = 9
登录成功后我们创建主窗体
_mainForm.InvokeRequired = false
MainControl.DummyForm.InvokeRequired = false
Thread.CurrentThread.ManagedThreadId = 9
到目前为止,一切看起来都很好。然后收到一个回调(WCF),一个事件在同一个线程上创建一个 frmMyDialog(在 dummyForm 上使用 Invoke),然后使用 ShowDialog:
frmMyCustomDialog.ShowDialog(_mainForm)
这会引发 CrossThreadException,这就是它现在的样子:
_mainForm.InvokeRequired = true
frmMyCustomDialog.InvokeRequired = false
MainControl.DummyForm.InvokeRequired = false
Thread.CurrentThread.ManagedThreadId = 12
为什么 MainControl.DummyForm 不正确? ManageThreadId 不是 9 而是 12?
【问题讨论】:
-
显示更多真实代码,了解您如何创建涉及异常的表单。 InvokeRequired 未绑定到主 UI 线程,而是绑定到创建控件/表单的线程!
-
你是否多次调用Application.Run?
-
@Jodrell 请看Edit1
-
我脑子里有太多表格错误 - 我出去了:)
-
如果不考虑状态而只强制调用会发生什么?
标签: c# winforms multithreading