【问题标题】:Find owner thread of GUI control查找 GUI 控件的所有者线程
【发布时间】:2010-02-26 14:26:15
【问题描述】:

在 .NET 应用程序上工作时,我在“跨线程操作无效”异常中运行,只是它似乎发生在正确的线程中。 有没有办法找出创建特定控件的线程是哪个线程?

到目前为止我发现了什么:

“InvokeRequired”操作仅告诉当前线程是否是“所有者线程”... 在 Control.Invoke(...) 方法上使用 Reflector 的一段有趣时光让我找到了 user32.dll 中的 P/Invoke 方法,该方法从窗口句柄中获取线程 ID:

[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
public static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId);

【问题讨论】:

    标签: .net user-interface multithreading


    【解决方案1】:

    我也遇到过同样的问题。即使使用 UI 线程中的控件,我也会遇到该异常。 就我而言,在创建控件的Handle 之前,我在后台线程上使用了InvokeRequired(或Invoke)。这是一个托盘图标的上下文菜单,一些后台线程必须更改菜单项的值。如果用户从未打开上下文菜单,则永远不会创建句柄,也不会将控件绑定到 UI 线程,然后就会发生严重破坏。发生这种情况时,InvokeRequired 总是返回 false,Invoke 只是在当前线程(不是 UI 线程)上运行该方法,因此在后台线程上创建了 Handle,并且控件被永久绑定到那个线程,就好像后台线程是它的 UI 线程一样。并且在尝试从 UI 线程使用控件时,抛出了跨线程异常。另一方面,如果用户在运行任何后台线程之前打开上下文菜单,一切都会正常工作。

    解决方案是在启动时从 UI 线程调用 CreateControl() 方法,以免任何后台线程有机会“窃取”控制权并破坏线程所有权。

    【讨论】:

    • 这真的解决了您的问题吗?顺便说一句,我刚刚发现在“备注”部分下的 Control.InvokeRequired 方法的 MSDN 文章中存在相同的解释。我可以发誓几年前它不存在(当时这种行为让我发疯)。 msdn.microsoft.com/en-us/library/…
    • 正如人们所预料的那样,如果继承 UserControl,调用受保护的 CreateHandle 调用似乎具有相同的效果。我想知道为什么这个调用不是在构造时隐式进行的,有没有人想在调用之前执行额外的任务?这次提前电话会导致其他症状吗?
    • @JCollins:我只能猜测原因是懒惰地分配句柄。当创建了大量控件但只有部分控件出现时(或者它们都出现但随着时间的推移逐渐出现),这可能会加快控件实例化或减少句柄分配。
    【解决方案2】:

    您能否在处理程序中创建事件时捕获线程 ID,并将其与您获得异常时进行比较。这至少会告诉你你是否在同一个线程上。

    【讨论】:

    • “事件”是指“控制”?奇怪的是,创建控件的线程和发生异常的线程是同一个线程,即应用程序的主线程。我在想 {control -> owner thread} 映射搞砸了。
    【解决方案3】:

    Control.BeginInvoke() 对我来说总是很好用。试试看。

    【讨论】:

      【解决方案4】:

      尝试使用 Spy++ 应用程序,它带有 VS 并显示窗口列表(大多数控件是单独的子窗口)以及有关它们的一些信息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-08-27
        • 1970-01-01
        • 2017-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-19
        相关资源
        最近更新 更多