编辑 9 月 26 日
有关完整背景,请参见下文。tl; dr:数据网格控件导致奇怪的异常,我正在寻求帮助隔离原因并找到解决方案。
我进一步缩小了这个范围。我已经能够在较小的测试应用程序中重现该行为,并且可以更可靠地触发不稳定的行为。
我绝对可以排除线程和(我认为)内存问题。新应用程序不使用任务或其他线程/异步功能,我只需向 DataGrid 中显示的对象类添加返回常量的属性即可触发未处理的异常。这向我表明问题出在非托管资源耗尽或我还没有想到的问题上。
修改后的程序结构是这样的。我创建了一个名为的用户控件EntityCollectionGridView
,它有一个标签和一个数据网格。在控件的 Loaded 事件处理程序中,我将 a 分配给List<TestClass>
具有 1000 或 10000 行的数据网格,让网格生成列。此用户控件在页面事件中的 MainPage.xaml 中实例化 2-4 次OnNavigatedTo
(或者Loaded
,这似乎无关紧要)。如果发生异常,则会在 MainPage 显示后立即发生。
有趣的是,行为似乎并没有随着显示的行数而变化(它会在 10000 行时可靠地工作,或者在每个网格中只有 1000 行时可靠地失败),而是随着所有网格中的列总数而变化在给定时间加载。显示 20 个属性,4 个网格可以正常工作。使用 35 个属性和 4 个网格,会引发异常。但是如果我消除两个网格,具有 35 个属性的同一个类将正常工作。
请注意,我添加以TestClass
从 20 列跳转到 35 列的所有属性均采用以下形式:
public string StringXYZ { get { return "asdfasdfasdfasdfasf"; } }
因此,支持数据中没有额外的内存(同样,我不认为内存压力是问题)。
大家怎么看?同样,任务管理器中的句柄/用户对象/等看起来不错,但是我可能还缺少其他东西吗?
原帖
我一直致力于将 Silverlight Toolkit DataGrid 移植到 WinRT,它在简单的测试(各种配置和多达 10000 行)中做得很好。但是,当我尝试将它嵌入到另一个 WinRT 应用程序时,我遇到了一个零星的异常(System.Exception 类型,在 App.UnhandledException 处理程序中引发),这证明很难调试。
Not enough quota is available to process this command. (Exception from HRESULT: 0x80070718)
错误始终是可重现的,但不是确定性的。也就是说,我可以在每次运行应用程序时实现它,但它并不总是通过执行相同的确切步骤集相同的次数来实现。该错误似乎发生在页面转换上(无论是向前导航到新页面还是返回到前一页),而不是(例如)在更改数据网格的 ItemsSource 时发生。
应用程序结构基本上是通过层次结构递归访问,每个层次结构级别显示一个页面。在层次结构中当前节点的页面上,显示了每个子节点和一些孙节点,并且可以显示任何子节点的数据网格。在实践中,我始终使用以下导航结构重现这一点:
Root page: shows no datagrid
Child page: shows one datagrid and a few listviews
Grandchild page: shows two datagrids, one bound to the
same source as Child page, the other one empty
一个典型的测试场景是,从 Root 开始,移动到 Child,移动到 Grandchild,移动回 Child,然后当我再次尝试导航到 Grandchild 时,它失败了,除了我上面提到的异常。但它可能会在我第一次击中孙子时失败,或者它可能让我在失败之前来回移动几次。
调用堆栈上只有一个托管帧,即未处理的异常事件处理程序。这是非常无益的。切换到混合模式调试,我得到以下信息:
WinRTClient.exe!WinRTClient.App.InitializeComponent.AnonymousMethod__14(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) Line 50 + 0x20 bytes C#
[Native to Managed Transition]
Windows.UI.Xaml.dll!DirectUI::CFTMEventSource<Windows::UI::Xaml::IUnhandledExceptionEventHandler,Windows::UI::Xaml::IApplication,Windows::UI::Xaml::IUnhandledExceptionEventArgs>::Raise(Windows::UI::Xaml::IApplication * pSource, Windows::UI::Xaml::IUnhandledExceptionEventArgs * pArgs) Line 327 C++
Windows.UI.Xaml.dll!DirectUI::Application::RaiseUnhandledExceptionEventHelper(long hrEncountered, unsigned short * pszErrorMessage, unsigned int * pfIsHandled) Line 920 + 0xa bytes C++
Windows.UI.Xaml.dll!DirectUI::ErrorHelper::CallAUHandler(unsigned int errorCode, unsigned int * pfIsHandled, wchar_t * * pbstrErrorMessage) Line 39 + 0x14 bytes C++
Windows.UI.Xaml.dll!DirectUI::ErrorHelper::ProcessUnhandledErrorForUserCode(long error) Line 82 + 0x10 bytes C++
Windows.UI.Xaml.dll!AgCoreCallbacks::CallAUHandler(unsigned int errorCode) Line 1104 + 0x8 bytes C++
Windows.UI.Xaml.dll!CCoreServices::ReportUnhandledError(long errorXR) Line 6582 C++
Windows.UI.Xaml.dll!CXcpDispatcher::Tick() Line 1126 + 0xb bytes C++
Windows.UI.Xaml.dll!CXcpDispatcher::OnReentrancyProtectedWindowMessage(HWND__ * hwnd, unsigned int msg, unsigned int wParam, long lParam) Line 653 C++
Windows.UI.Xaml.dll!CXcpDispatcher::WindowProc(HWND__ * hwnd, unsigned int msg, unsigned int wParam, long lParam) Line 401 + 0x24 bytes C++
user32.dll!_InternalCallWinProc@20() + 0x23 bytes
user32.dll!_UserCallWinProcCheckWow@36() + 0xbd bytes
user32.dll!_DispatchMessageWorker@8() + 0xf8 bytes
user32.dll!_DispatchMessageW@4() + 0x10 bytes
Windows.UI.dll!Windows::UI::Core::CDispatcher::ProcessMessage(int bDrainQueue, int * pbAnyMessages) Line 121 C++
Windows.UI.dll!Windows::UI::Core::CDispatcher::ProcessEvents(Windows::UI::Core::CoreProcessEventsOption options) Line 184 + 0x10 bytes C++
Windows.UI.Xaml.dll!CJupiterWindow::RunCoreWindowMessageLoop() Line 416 + 0xb bytes C++
Windows.UI.Xaml.dll!CJupiterControl::RunMessageLoop() Line 714 + 0x5 bytes C++
Windows.UI.Xaml.dll!DirectUI::DXamlCore::RunMessageLoop() Line 2539 + 0x5 bytes C++
Windows.UI.Xaml.dll!DirectUI::FrameworkView::Run() Line 91 C++
twinapi.dll!`Windows::ApplicationModel::Core::CoreApplicationViewAgileContainer::RuntimeClassInitialize'::`55'::<lambda_A2234BA2CCD64E2C>::operator()(void * pv) Line 560 C++
twinapi.dll!`Windows::ApplicationModel::Core::CoreApplicationViewAgileContainer::RuntimeClassInitialize'::`55'::<lambda_A2234BA2CCD64E2C>::<helper_func>(void * pv) Line 613 + 0xe bytes C++
SHCore.dll!_SHWaitForThreadWithWakeMask@12() + 0xceab bytes
kernel32.dll!@BaseThreadInitThunk@12() + 0xe bytes
ntdll.dll!___RtlUserThreadStart@8() + 0x27 bytes
ntdll.dll!__RtlUserThreadStart@8() + 0x1b bytes
这向我表明,无论我做错了什么,直到在应用程序的消息循环中至少一个循环之后才会注册(我还尝试使用“Debug | Exceptions ...”打破所有抛出的异常 - 就我可以说,没有东西被扔掉和吞下)。我看到的有趣的堆栈帧是WindowProc
、OnReentrancyProtectedWindowMessage
和Tick
。是 0x402 (1026),这msg
对我来说没有任何意义。此页面列出了在以下上下文中使用的消息:
CBEM_SETIMAGELIST
DDM_CLOSE
DM_REPOSITION
HKM_GETHOTKEY
PBM_SETPOS
RB_DELETEBAND
SB_GETTEXTA
TB_CHECKBUTTON
TBM_GETRANGEMAX
WM_PSD_MINMARGINRECT
...但这对我来说也没什么意义(它甚至可能不相关)。
我能想到的三个理论是:
- 内存压力。但是我遇到了这个问题,我有 24% 的物理内存可用,而应用程序消耗的内存不到 100MB。其他时候,该应用程序在导航一段时间并占用 400MB 内存时不会遇到任何问题
- 线程问题,例如从工作线程访问 UI 线程。而且,事实上,我确实在后台线程上进行了数据访问。但这是使用在 WinForms 环境和 Outlook 插件中非常可靠的(移植的)框架,我认为线程使用是安全的。此外,我可以在这个应用程序中使用相同的数据,而不会出现任何问题,只是绑定到 ListViews 等等。最后,配置 Grandchild 节点,以便在第一个数据网格中选择一行会启动对该行的详细信息项的请求,该请求将显示在第二个数据网格中(最初是空的,并且可以保持不变而不会阻止异常)。这发生在没有页面转换的情况下,只要我选择摆弄选择,它就可以完美地工作。但是导航回 Child 可能会立即杀死我,
- 某种资源耗尽,可能是 GUI 句柄。但我不认为我对这个系统施加了那么大的压力。在一次执行中,中断异常处理程序,任务管理器报告使用 662 个句柄、21 个用户对象和 12 个 GDI 对象的进程,而 Tweetro 分别使用 734、37 和 19 个没有问题。在这个类别中我还可能缺少什么?
我有足够的可用磁盘空间,并且除了配置文件之外,我没有将磁盘用于任何其他内容(并且在添加数据网格之前一切正常)。
我的下一个想法是尝试遍历数据网格代码中一些潜在的“有趣”部分,并跳过任何有问题的部分。我确实尝试过使用数据网格的 ArrangeOverride,但异常似乎并不关心我是否这样做。另外,我不确定这是一个有用的策略。由于直到消息循环的一个循环之后才会引发异常,并且由于我无法确定它何时会发生,所以我需要涵盖大量的排列,运行每个排列大量次,隔离问题代码。
在调试和发布模式下都会引发错误。而且,作为最后的背景说明,我们在这里处理的数据量很小,比我单独运行的 10000 行数据网格要小得多。它可能大约有 50-100 行,可能有 30-40 列。在抛出异常之前,数据和网格似乎可以正常工作并且响应良好。
所以,这就是我来找你的原因。我的两个问题是:
- 错误信息是否为您提供了有关可能是什么问题的任何提示?
- 您将使用什么调试策略来隔离问题代码?
非常感谢您提供的任何帮助!