8

我有一个带有非常复杂的 XAML 的 WPF 应用程序,我需要一种方法来知道我的应用程序挂起的点,当我尝试暂停执行时,应用程序似乎没有挂起,指针将在这一行:

System.Windows.Application myApp;
.
.
.
.
myApp.Run(); // <== this line

当我更改任务栏的布局或 Windows 资源管理器崩溃(隐藏任务栏)时,会发生这种情况,如果我重复进行这些更改,应用程序将永远无法恢复,但是当一个小的更改完成时,应用程序几分钟后会恢复,我需要知道这个问题的原因,我怀疑我的应用程序的复杂 XAML,但我需要一种方法来了解页面或组件,或者这个挂起的任何来源。

* 编辑 *

我需要一种工具或方法来了解消耗调度程序时间的 XAML 是什么!

* 编辑 *

我得到了挂起的确切原因,这是因为在另一个线程中创建了 ReportViewer 的实例,当我删除实例的创建时,它工作得很好,奇怪的是,这个错误在我的应用程序中存在很长时间以前,但最近出现了挂起,我的意思是:当您在我的应用程序的任何位置插入以下代码之一时,我的应用程序将挂起:

        new Action(() =>
        {
            ReportViewer rv = new ReportViewer();
        }).BeginInvoke(null, null);

或者

        new Action(() =>
        {
            ReportViewer rv = new ReportViewer();
            rv.Dispose();
        }).BeginInvoke(null, null);

或者

        new Action(() =>
        {
            ReportViewer rv = new ReportViewer();
            rv.LocalReport.ReleaseSandboxAppDomain();
            rv.Dispose();
        }).BeginInvoke(null, null);

我的问题:

1-更改窗口布局(调整任务栏大小或移动它)与未添加到任何可视化树的报表查看器之间有什么关系,为什么这会导致我的应用程序挂起?

2-如何确定挂起的位置?

3-有时应用程序会在几分钟内恢复(3-5),但有时会挂起几个小时,应用程序不会恢复,为什么?

4-在这种情况下,我如何确定导致我的应用程序挂起的组件或配置?

顺便说一句,如果解决了这对其他人来说非常有用,我们花了很多时间来检测它,但是结合 ReportViewer 没有得到导致挂起的确切原因!

4

4 回答 4

9

我会尽量完整地回答你的问题:

ReportViewer 控件是一个 Windows 窗体控件,并且 Windows 窗体存在一个问题,在适当的情况下,该问题可能会导致 UI 线程锁定,从而导致应用程序挂起。

当 Windows 发布 WM_SETTINGCHANGED 消息时,System.Win32.SystemEvents.UserPreferenceChanged将触发该事件。一些 Windows 窗体控件,包括ReportViewer在系统设置更改时侦听此事件以更新自身。

UserPreferenceChanged与其他常规事件相比,该事件将使用订阅线程的 SynchronzationContext 调用每个处理程序。因此,如果 ReportViewer 是在主 UI 线程(WPF 中的调度程序线程)以外的线程上创建的,则事件触发代码将尝试执行调用并等待调用完成,这将永远不会在该其他线程上发生,从而冻结 UI .

要确定挂起的位置,您可以轻松禁用选项 Enable Just my code in Visual Studio -> Tools -> Options -> Debugging,当挂起发生时,只需将 VS 调试器附加到挂起的应用程序,这将向您显示应用程序挂在 WaitOne 调用的 UserPreferenceChanged 事件处理中。

您可以在以下文章中阅读有关此挂起的更多信息:

http://www.ikriv.com/dev/dotnet/MysteriousHang.html

从另一个线程调用 show 后 Windows 窗体窗体挂起

于 2013-01-12T12:47:36.313 回答
2

简单 - 不要在另一个线程中创建 ReportViewer。绑定到一个 UI 层次结构的所有元素必须来自 UI 线程。

在创建动作中,调用回 UI 线程以在 UI 线程中进行实际创建。

于 2013-01-05T12:59:14.800 回答
0

在 VS 调试器中暂停后,打开 Debug/Windows 菜单上的 Threads 窗口。选择它们并冻结它们(从上下文菜单中)。然后双击每个线程以查看其卡住的位置。此外,右键单击调用堆栈并显示外部代码。

于 2013-01-03T03:38:24.643 回答
0

在这种情况下,我喜欢使用几个技巧:

我已经在如何调试 .NET 运行时中的内部错误?

但这也应该涵盖您的问题,它会记录每个操作,直到 CLR 崩溃

IntelliTraceEvents 和呼叫信息是一个惊人的技巧,可以准确地找到在粉碎发生之前发生的事情

工具->调试->常规->启用 .Net Framework 调试

+

工具->IntelliTace-> IntelliTraceEvents 和调用信息

+

Tools->IntelliTace-> Set StorIntelliTace Recordings in this directory

并选择一个目录

应该允许您进入 .net 代码并跟踪每个函数调用。我在一个小型示例项目上进行了尝试,它可以工作

在每个调试会话之后,它应该创建调试会话的记录。如果我没记错的话,即使 CLR 死了,它也是设置的目录

这应该允许您在 CLR 崩溃之前进行确切的调用。

第二招

可能在生产中使用的是这个很棒的框架,叫做PostSharp

注意本教程:

http://www.sharpcrafters.com/solutions/logging

在您按照描述使用 postsharp 之后。

您可以为系统中的每个函数在终止时输入它,如果抛出异常,则记录所有变量。

并使用几十行代码完成所有这些工作。

于 2013-01-10T12:17:55.020 回答