即使使用InvokeRequired,跨线程操作也无效

vel*_*koz 6 c# multithreading .net-4.0 thread-safety winforms

我有一个带有自定义控件的表单.

我的表格中有一个方法:

private void SetEnabledOnControls(bool val)
{
  if (InvokeRequired)
  {
      Invoke((Action<bool>)SetEnabledOnControls, val);
  }
  else
  {
       //do the work - iterate over child controls, 
       //and they iterate over their children, etc...
  }
}
Run Code Online (Sandbox Code Playgroud)

else分支上的方法内部,我得到了提到的异常:
Cross-thread operation not valid: Control 'txtNumber' accessed from a thread other than the thread it was created on.

我的场景实际上有点复杂 - 我只是将其推断为一个例子.实际上发生的是我正在使用WorkflowFoundation - 我在WorkflowApplication中运行StateMachineActivity(CTP1)(它在自己的线程中运行),我订阅了它的事件,并从那里调用SetEnabledOnControls.此外,我正在使用书签来恢复我的工作流程(而且,旁边有MEF,没有涉及场景).

所有这些都与我对InvokeRequired的明显误解无关 - 如果InvokeRequired为false,我有可能有交叉线程异常吗?我没有"手动"创建任何控件 - 它都在设计者放置的Initialize()中.

任何人都可以对此有所了解吗?

谢谢!

编辑 使用GWLlosa建议,我使用了跟踪ThreadId System.Threading.Thread.CurrentThread.ManagedThreadId.现在来了奇怪的部分...... Initialize()中的线程id是10.在传递前2个状态之间,它带有Id 13 - InvokeRequired为true,并且它被正确调用.但是,在第二个状态之后,当它SetEnabledOnControls再次进入13时,但这次InvokeRequired是假的!怎么会!?当然,后来它无法改变儿童控制(这并不奇怪).可能是表格以某种方式改变了它所生活的线程?

编辑2 现在我打电话给:

 if (IsHandleCreated)
 {
     Invoke((Action<bool>)SetEnabledOnControls, val);
 }
Run Code Online (Sandbox Code Playgroud)

它必须IsHandleCreated是真的,但仍然失败了devSpeed所指出的.

编辑3 FACEPALM :)其中一个恢复状态的按钮首先是Form的CancelButton.当它从属性中删除时,codebihind仍然有DialogResult =取消它 - 所以我的表单确实关闭了,当然它缺少句柄所以InvokeRequired没有返回正确的信息,因此错误.

感谢大家!我今天学到了新东西:)

GWL*_*osa 2

如果在创建控件时(在 Initialize() 函数中)记录线程 ID 以及在尝试触摸控件之前记录线程 ID,可能会使调试更加容易。一般来说,当您以某种方式在线程上创建控件而不是您最初期望的线程时,我就看到过这种情况发生。