调试期间IEnumerable vs IList和奇怪的CrossThreadMessagingException

时间:2011-09-01 11:13:57

标签: c# .net debugging internals

初始代码是:

var processes = Process.GetProcesses().Where(p => p.MainWindowTitle.ToUpperInvariant().Contains("FOO"));

在调试期间,如果我尝试在即时窗口窗格中的Count()上调用processes或检查本地窗格中的“结果视图”,则会得到CrossThreadMessagingException。如果我不调试但只运行代码,一切都很好。如果我将集合转换为列表,然后将其分配给processes并在调试期间使用Count属性,那也没关系。

CrossThreadMessagingException究竟是什么?为什么IEnumerable方法会造成这种异常?


编辑:提供有关异常的更多信息。

消息:发生异常'Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException'

来源:Microsoft.VisualStudio.Debugger.Runtime

堆栈跟踪:

at Microsoft.VisualStudio.Debugger.Runtime.Main.ThrowCrossThreadMessageException(String formatString)

at Microsoft.Win32.NativeMethods.GetWindowTextLength(HandleRef hWnd)

在System.Diagnostics.Process.get_MainWindowTitle()

1 个答案:

答案 0 :(得分:3)

这可能完全是错的,但我收集它是延迟枚举与WhereArrayIterator的混合,并且调试器试图枚举它?

我感觉到,即时窗口试图枚举你的结果,它是在另一个线程上执行的(导致CrossThreadMessagingException)。

当您调用ToList时,它不会执行此操作,因为ToList会导致枚举立即运行并在列表中连接结果。在您尝试在即时窗口中使用Count方法之前完成此操作。

如果您在没有Count()调用的情况下使用ToList,则会强制WhereArrayIterator(这是您的Where方法调用的返回值)枚举,然后试图从另一个线程访问你的lamda委托。

在测试中,您实际上可以通过立即枚举WhereArrayIterator的其他实例,所以我认为这是您的特定用例,您尝试枚举Process类型,我认为使用Win32 API进行调用。

在内部,Process.MainWindowTitle属性使用延迟加载作为其值。它实际上并没有调用抓取信息,直到第一次访问该属性(并且,它没有锁定,所以如果有多个线程访问该代码区域,那么它是不是原子的,所以存在种族条件的继承风险 - 无论如何它都无关紧要,因为它是一个只读属性,它的值应该始终是相同的)。

[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[MonitoringDescription("ProcessMainWindowTitle")]
public string MainWindowTitle
{
  get
  {
    if (this.mainWindowTitle == null)
    {
      IntPtr mainWindowHandle = this.MainWindowHandle;
      if (mainWindowHandle == (IntPtr) 0)
      {
        this.mainWindowTitle = string.Empty;
      }
      else
      {
        StringBuilder lpString = new StringBuilder(Microsoft.Win32.NativeMethods.GetWindowTextLength(new HandleRef((object) this, mainWindowHandle)) * 2);
        Microsoft.Win32.NativeMethods.GetWindowText(new HandleRef((object) this, mainWindowHandle), lpString, lpString.Capacity);
        this.mainWindowTitle = ((object) lpString).ToString();
      }
    }
    return this.mainWindowTitle;
  }
}

首次访问时,属性会调用Win32来获取窗口文本。我相信这是它似乎正在倒下的地方。 只有在您的WhereArrayIterator实例中使用延迟枚举时才会出现问题。

说实话,这是一个盲目的猜测!