gpt4 book ai didi

c# - DataGrid 的 WinRT 端口中的神秘 "Not enough quota is available to process this command"

转载 作者:行者123 更新时间:2023-12-02 11:41:34 25 4
gpt4 key购买 nike

9 月 26 日编辑

有关完整背景,请参见下文。 tl; dr:数据网格控件导致奇怪的异常,我正在寻找帮助隔离原因并找到解决方案。

我进一步缩小了范围。我已经能够在较小的测试应用程序中重现该行为,并更可靠地触发不稳定的行为。

我绝对可以排除线程和(我认为)内存问题。新应用程序不使用任务或其他线程/异步功能,我可以通过添加属性来触发未处理的异常,这些属性将常量返回到 DataGrid 中显示的对象类。这向我表明问题要么是非托管资源耗尽,要么是我还没有想到的问题。

修订后的程序结构如下。我创建了一个名为 EntityCollectionGridView 的用户控件它有一个标签和一个数据网格。在控件的 Loaded 事件处理程序中,我分配了一个 List<TestClass>到具有 1000 或 10000 行的数据网格,让网格生成列。此用户控件在页面的 OnNavigatedTo 中的 MainPage.xaml 中实例化了 2-4 次。事件(或 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 时,除了我上面提到的异常(exception),它都失败了。但它可能会在我第一次击中孙子时失败,或者它可能让我在失败之前来回移动几次。

调用堆栈上只有一个托管帧,即未处理的异常事件处理程序。这是非常没有帮助的。切换到混合模式调试,我得到以下信息:
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 . msg是 0x402 (1026),这对我来说没有任何意义。 This page列出在以下上下文中使用的消息:
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 插件中非常可靠的(移植)框架,我认为线程使用是安全的。此外,我可以在这个应用程序中使用相同的数据,而不会出现任何绑定(bind)到 ListViews 等的问题。最后,Grandchild 节点被配置为选择第一个数据网格中的一行会启动对该行详细信息项的请求,这些项显示在第二个数据网格中(它最初是空的,并且可以在不阻止异常的情况下保持不变)。这发生在没有页面过渡的情况下,只要我选择摆弄选择,就可以完美地工作。但是导航回 Child 可能会立即杀死我,即使此时应该没有数据访问,因此不是我所知道的线程操作。
  • 资源枯竭 某种形式,也许是 GUI 句柄。但我认为我没有给这个系统施加太大的压力。在一次执行中,中断异常处理程序,任务管理器报告使用 662 个句柄、21 个用户对象和 12 个 GDI 对象的进程,而 Tweetro 分别使用 734、37 和 19 个没有问题。在这个类别中我还缺少什么?

  • 我有足够的可用磁盘空间,并且我不会将磁盘用于配置文件以外的任何内容(并且在添加数据网格之前一切正常)。

    我的下一个想法是尝试遍历 datagrid 代码中一些潜在的“有趣”部分并跳过任何有问题的部分。我确实尝试过使用数据网格的排列覆盖,但异常似乎并不关心我是否这样做了。另外,我不确定这是一个有用的策略。由于异常直到消息循环的一个循环之后才会被抛出,而且由于我无法确定它何时发生,我需要覆盖大量的排列,运行每个排列很多次,隔离问题代码。

    在 Debug 和 Release 模式下都会抛出错误。而且,作为最后的背景说明,我们在这里处理的数据量很小,远小于我单独运行数据网格的 10000 行。它大概有 50-100 行,大概有 30-40 列。在抛出异常之前,数据和网格似乎可以正常工作并响应良好。

    所以,这就是我来找你的原因。我的两个问题是:
  • 错误信息是否为您提供了可能是什么问题的任何提示?
  • 您将使用什么调试策略来隔离问题代码?

  • 非常感谢您提供的任何帮助!

    最佳答案

    好的,还有一些 critical input from Tim Heuer [MSFT] ,我想出了发生了什么以及如何解决这个问题。

    令人惊讶的是,我最初的三个猜测都不正确。这与内存、线程或系统资源无关。相反,它与 Windows 消息传递系统的限制有关。显然,它有点像堆栈溢出异常,因为当您一次对可视化树进行太多更改时,异步更新队列会变得很长,以至于它会跳闸并抛出异常。

    在这种情况下,问题是有足够的 UIElements 进入我正在使用的数据网格,允许网格一次生成所有自己的列在某些情况下可能会超过限制。我同时使用了多个网格,并且所有网格都是为了响应页面导航事件而加载的,这使得确定起来更加棘手。

    幸运的是,我遇到的限制不是可视化树或 XAML UI 子系统本身的限制,只是用于更新它的消息传递中的限制。这意味着如果我可以在调度程序时钟的多个滴答声上展开相同的操作,我就可以完成相同的最终结果。

    我最终做的是指示我的数据网格不要自动生成自己的列。相反,我将网格嵌入到用户控件中,当加载数据时,它会解析出所需的列并将它们加载到列表中。然后,我调用了以下方法:

    void LoadNextColumns(List<ColumnDisplaySetup> colDef, int startIdx, int numToLoad)
    {
    for (int idx = startIdx; idx < startIdx + numToLoad && idx < colDef.Count; idx++)
    {
    DataGridTextColumn newCol = new DataGridTextColumn();
    newCol.Header = colDef[idx].Header;
    newCol.Binding = new Binding() { Path = new PropertyPath(colDef[idx].Property) };
    dgMainGrid.Columns.Add(newCol);
    }

    if (startIdx + numToLoad < colDef.Count)
    {
    Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
    LoadNextColumns(colDef, startIdx + numToLoad, numToLoad);
    });
    }
    }

    ( ColumnDisplaySetup 是一种普通类型,用于存放解析出的配置或从文件加载的配置。)

    该方法分别使用以下参数调用:列列表 0 和我任意猜测的 5 作为一次加载的相当安全的列数;但这个数字是基于测试和预期可以同时加载大量网格。我向 Tim 询问了可能为流程的这一部分提供信息的更多信息,如果我了解更多关于如何确定多少是安全的,我会在这里报告。

    在实践中,这似乎工作得很好,虽然它会导致你期望的那种渐进式渲染,列明显弹出。我希望这可以通过使用 numToLoad 的最大可能值来改善。并通过其他 UI 技巧。我可能会研究在生成列时隐藏网格,并仅在一切准备就绪时才显示结果。最终决定将归结为哪种感觉更“快速和流畅”。

    同样,如果我得到它,我会用更多信息更新这个答案,但我希望这有助于将来遇到类似问题的人。在投入了比我愿意承认的时间更多的时间之后,我不想让其他人为此而自杀。

    关于c# - DataGrid 的 WinRT 端口中的神秘 "Not enough quota is available to process this command",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12584619/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com