C# DataGrid 的 WinRT 端口中出现神秘的“没有足够的配额来处理此命令”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12584619/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mysterious "Not enough quota is available to process this command" in WinRT port of DataGrid
提问by Dominic P
Edit Sept 26
9 月 26 日编辑
See below for the full background. tl;dr: A data grid control is causing odd exceptions, and I am looking for help isolating the cause and finding a solution.
有关完整背景,请参见下文。tl; dr:数据网格控件导致奇怪的异常,我正在寻找帮助隔离原因并找到解决方案。
I've narrowed this down a bit further. I have been able to reproduce the behavior in a smaller test app with more reliable triggering of the erratic behavior.
我进一步缩小了范围。我已经能够在较小的测试应用程序中重现该行为,并更可靠地触发不稳定的行为。
I can definitely rule out both threading and (I think) memory issues. The new app uses no Tasks or other threading/asynchronous features, and I can trigger the unhandled exception simply by adding properties that return a constant to the class of objects shown in the DataGrid. This indicates to me that the problem is either in unmanaged resource exhaustion or something I haven't thought of yet.
我绝对可以排除线程和(我认为)内存问题。新应用程序不使用任务或其他线程/异步功能,我可以通过添加属性来触发未处理的异常,这些属性将常量返回到 DataGrid 中显示的对象类。这向我表明问题要么是非托管资源耗尽,要么是我还没有想到的问题。
The revised program is structured like this. I have created a user control called EntityCollectionGridViewwhich has a label and a data grid. In the control's Loaded event handler, I assign a List<TestClass>to the data grid with 1000 or 10000 rows, letting the grid generate the columns. This user control is instantiated 2-4 times in MainPage.xaml in the page's OnNavigatedToevent (or Loaded, it doesn't seem to matter). If an exception occurs, it occurs immediately after MainPage is shown.
修订后的程序结构如下。我创建了一个名为的用户控件EntityCollectionGridView,它有一个标签和一个数据网格。在控件的 Loaded 事件处理程序中,我将 a 分配给List<TestClass>具有 1000 或 10000 行的数据网格,让网格生成列。此用户控件在页面OnNavigatedTo事件的MainPage.xaml 中实例化 2-4 次(或者Loaded,这似乎无关紧要)。如果发生异常,则在显示 MainPage 后立即发生。
The interesting thing is, the behavior doesn't seem to vary with the number of rows shown (it will work reliably with 10000 rows or fail reliably with only 1000 rows in each grid) but rather with the total number of columns in all the grids loaded at a given time. With 20 properties to show, 4 grids works fine. With 35 properties and 4 grids, the exception is thrown. But if I eliminate two grids, the same class with 35 properties will work fine.
有趣的是,行为似乎并不随显示的行数而变化(它可以在 10000 行时可靠地工作,或者在每个网格中只有 1000 行时可靠地失败),而是随所有网格中的总列数而变化在给定的时间加载。要显示 20 个属性,4 个网格可以正常工作。有 35 个属性和 4 个网格,抛出异常。但是如果我消除两个网格,具有 35 个属性的同一个类将正常工作。
Note that all of the properties I add to TestClassto jump from 20 to 35 columns are of the form:
请注意,我添加到TestClass从 20 列跳转到 35 列的所有属性都具有以下形式:
public string StringXYZ { get { return "asdfasdfasdfasdfasf"; } }
So, there's no additional memory in the backing data (and again, I don't think memory pressure is the problem anyway).
因此,后备数据中没有额外的内存(再说一次,我不认为内存压力是问题所在)。
What do you all think? Again, the handles/user objects/etc in Task Manager look good, but is there something else I might be missing?
大家怎么看?同样,任务管理器中的句柄/用户对象/等看起来不错,但还有什么我可能遗漏的吗?
Original post
原帖
I have been working on a port of the Silverlight Toolkit DataGrid to WinRT, and it has done well enough in simple tests (a variety of configurations and up to 10000 rows). However, as I have tried to embed it into another WinRT app I have run into a sporadic exception (of type System.Exception, raised in the App.UnhandledException handler) that is proving very difficult to debug.
我一直致力于将 Silverlight Toolkit DataGrid 移植到 WinRT,它在简单测试(各种配置和多达 10000 行)中做得足够好。但是,当我尝试将它嵌入到另一个 WinRT 应用程序时,我遇到了一个非常难以调试的偶发异常(System.Exception 类型,在 App.UnhandledException 处理程序中引发)。
Not enough quota is available to process this command. (Exception from HRESULT: 0x80070718)
The error is consistently reproducible, but not deterministically. That is, I can make it happen every time I run the App, but it doesn't always happen by performing the same exact set of steps the same number of times. The error seems to occur on page transitions (whether navigating to a new page forward or back to a previous page), and not (for instance) when changing the ItemsSource of the datagrid.
该错误始终可重现,但不是确定性的。也就是说,我可以在每次运行应用程序时实现它,但它并不总是通过执行相同次数的相同步骤来实现。该错误似乎发生在页面转换时(无论是向前导航到新页面还是返回到上一页),而不是(例如)更改数据网格的 ItemsSource 时。
The application structure is basically recursive access through a hierarchy, with a page shown at each hierarchy level. On the page for the current node in the hierarchy, each child node and some grandchild nodes are shown, and for any subnode a datagrid may be shown. In practice, I consistently reproduce this with the following navigation structure:
应用程序结构基本上是通过层次结构递归访问,在每个层次结构级别显示一个页面。在层次结构中当前节点的页面上,显示了每个子节点和一些孙节点,并且可以显示任何子节点的数据网格。在实践中,我始终使用以下导航结构重现这一点:
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
A typical test scenario is, start at Root, move to Child, move to Grandchild, move back to Child, and then when I try to navigate to Grandchild again, it fails with the exception I mentioned above. But it might fail the first time I hit Grandchild, or it might let me move back and forth a few times before failing.
一个典型的测试场景是,从 Root 开始,移到 Child,移到 Grandchild,移回 Child,然后当我再次尝试导航到 Grandchild 时,除了我上面提到的例外,它都失败了。但它可能会在我第一次击中孙子时失败,或者它可能让我在失败之前来回移动几次。
The call stack has only one managed frame on it, which is the unhandled exception event handler. This is very unhelpful. Switching to mixed mode debugging, I get the following:
调用堆栈上只有一个托管帧,即未处理的异常事件处理程序。这是非常没有帮助的。切换到混合模式调试,我得到以下信息:
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
This indicates to me that whatever I'm doing wrong doesn't register until after at least one cycle in the app's message loop (and I also tried breaking on all thrown exceptions using "Debug | Exceptions..." -- as far as I can tell, nothing is thrown and swallowed). The interesting stack frames I see are WindowProc, OnReentrancyProtectedWindowMessage, and Tick. The msgis 0x402 (1026), which doesn't mean anything to me. This pagelists that message as used in the following contexts:
这向我表明,无论我做错了什么,在应用程序的消息循环中至少经过一个循环后才会注册(并且我还尝试使用“Debug | Exceptions...”来打破所有抛出的异常——就我可以说,没有任何东西被扔掉和吞下)。我看到的有趣的堆栈帧WindowProc,OnReentrancyProtectedWindowMessage和Tick。该msg是0x402(1026),这并不意味着什么给我。此页面列出了在以下上下文中使用的消息:
CBEM_SETIMAGELIST
DDM_CLOSE
DM_REPOSITION
HKM_GETHOTKEY
PBM_SETPOS
RB_DELETEBAND
SB_GETTEXTA
TB_CHECKBUTTON
TBM_GETRANGEMAX
WM_PSD_MINMARGINRECT
...but that doesn't mean anything much to me, either (it might not even be relevant).
......但这对我来说也没有什么意义(它甚至可能不相关)。
The three theories I can come up with are these:
我能提出的三个理论是:
- Memory pressure.But I have run into this with 24% of my physical memory free and the app consuming less than 100MB of memory. Other times, the app won't have hit any problems navigating around a while and racking up 400MB of memory
- Threading problems,such as access to the UI thread from a worker thread. And, in fact, I do have data access happening on a background thread. But this is using a (ported) framework that has been very reliable in a WinForms environment and in an Outlook plugin, and I think the thread use is safe. Additionally, I can use the same data in this app without any problems binding just to ListViews and so forth. Finally, the Grandchild node is configured such that selecting a row in the first datagrid kicks off a request for the row's detail items, which are displayed in the second datagrid (which is initially empty, and can remain so without preventing the exception). This happens without a page transition and works flawlessly for as long as I choose to fiddle with the selection. But navigating back to Child might kill me right away, even though there should be no data access at that point and therefore not threading operations that I know of.
- Resource exhaustionof some kind, maybe GUI handles. But I don't think I'm putting that much pressure on this system. In one execution, breaking in the exception handler, Task Manager reports the process using 662 handles, 21 User objects, and 12 GDI objects, as compared to Tweetro which is using 734, 37, and 19 respectively without problems. What else might I be missing in this category?
- 内存压力。但是我遇到了这个问题,我的物理内存有 24% 可用,应用程序消耗的内存不到 100MB。其他时候,该应用程序在导航一段时间并占用 400MB 内存时不会遇到任何问题
- 线程问题,例如从工作线程访问 UI 线程。而且,事实上,我确实在后台线程上进行了数据访问。但这是使用在 WinForms 环境和 Outlook 插件中非常可靠的(移植)框架,我认为线程使用是安全的。此外,我可以在这个应用程序中使用相同的数据,而不会出现任何绑定到 ListViews 等的问题。最后,Grandchild 节点被配置为选择第一个数据网格中的一行会启动对该行的详细信息项的请求,这些项显示在第二个数据网格中(它最初是空的,并且可以保持为空而不会阻止异常)。这发生在没有页面过渡的情况下,只要我选择摆弄选择,就可以完美地工作。但是导航回到 Child 可能会立即杀死我,
- 某种资源耗尽,也许是 GUI 处理。但我认为我没有给这个系统施加太大的压力。在一次执行中,中断异常处理程序,任务管理器报告使用 662 个句柄、21 个用户对象和 12 个 GDI 对象的进程,而 Tweetro 分别使用 734、37 和 19 个没有问题。在这个类别中我还缺少什么?
I have plenty of disk space free, and am not using the disk for anything other than configuration files anyway (and all that worked fine before adding the datagrids).
我有足够的可用磁盘空间,并且我不会将磁盘用于配置文件以外的任何内容(并且在添加数据网格之前一切正常)。
My next thought was to try to step through some of the potential 'interesting' parts of the datagrid code and jump over any that were questionable. I did try that with the datagrid's ArrangeOverride, but the exception didn't seem to care whether I did that or not. Also, I am not sure this is a useful strategy. Since the exception isn't being thrown until after a cycle on the message loop, and since I can't know for sure when it's about to happen, I would need to cover a huge number of permutations, running each permutation a whole lot of times, to isolate the problem code.
我的下一个想法是尝试遍历 datagrid 代码中一些潜在的“有趣”部分并跳过任何有问题的部分。我确实尝试过使用数据网格的排列覆盖,但异常似乎并不关心我是否这样做了。另外,我不确定这是一个有用的策略。由于异常直到消息循环的一个循环之后才会被抛出,而且由于我不能确定它什么时候发生,我需要覆盖大量的排列,运行每个排列很多次,隔离问题代码。
The error is thrown in both Debug and Release modes. And, as a final background note, the amount of data we're dealing with here is small, much smaller than my 10000-row runs of the datagrid in isolation. It's probably on the order of 50-100 rows, with perhaps 30-40 columns. And before the exception is thrown, the data and the grids seem to work and respond fine.
在 Debug 和 Release 模式下都会抛出错误。而且,作为最后的背景说明,我们在这里处理的数据量很小,远小于我单独运行数据网格的 10000 行。它大概有 50-100 行,大概有 30-40 列。在抛出异常之前,数据和网格似乎可以正常工作并响应良好。
So, that's why I come to you. My two questions are:
所以,这就是我来找你的原因。我的两个问题是:
- Does the error information give you any hints as to what might be the problem?
- What debugging strategy would you use to isolate the problem code?
- 错误信息是否为您提供了可能是什么问题的任何提示?
- 您将使用什么调试策略来隔离问题代码?
Many thanks in advance for any help you can provide!
非常感谢您提供的任何帮助!
采纳答案by Dominic P
OK, with some critical input from Tim Heuer [MSFT], I figured out what was going on and how to get around this problem.
好的,通过Tim Heuer [MSFT] 的一些重要意见,我弄清楚了发生了什么以及如何解决这个问题。
Surprisingly, none of my three initial guesses were correct. This wasn't about memory, threading, or system resources. Instead, it was about limitations in the Windows messaging system. Apparently it is a little like a stack overflow exception, in that when you make too many changes to the visual tree all at once, the asynchronous update queue gets so long that it trips a wire and the exception gets thrown.
令人惊讶的是,我最初的三个猜测都不正确。这与内存、线程或系统资源无关。相反,它与 Windows 消息传递系统的限制有关。显然,它有点像堆栈溢出异常,因为当您一次对可视化树进行太多更改时,异步更新队列会变得很长,以至于它会跳闸并抛出异常。
In this case, the problem is that there are enough UIElements going into the data grid I am working with that allowing the grid to generate all its own columns all at once can in some cases exceed the limit. I was using a number of grids all at once, and all loading in response to page navigation events, which made it all the trickier to nail down.
在这种情况下,问题是有足够的 UIElements 进入我正在使用的数据网格,允许网格一次生成所有自己的列在某些情况下可能会超过限制。我同时使用了许多网格,并且所有网格都是为了响应页面导航事件而加载的,这使得确定起来更加棘手。
Thankfully, the limitations I was running into were NOT limitations in the visual tree or the XAML UI subsystem itself, just in the messaging used to update it. This means that if I could spread out the same operations over multiple ticks of the dispatcher's clock, I could accomplish the same end result.
幸运的是,我遇到的限制不是可视化树或 XAML UI 子系统本身的限制,只是用于更新它的消息传递中的限制。这意味着如果我可以在调度程序时钟的多个滴答声上展开相同的操作,我就可以完成相同的最终结果。
What I ended up doing was to instruct my data grid not to autogenerate its own columns. Instead, I embedded the grid into a user control that, when the data was loaded, would parse out the columns needed and load them into a list. Then, I called the following method:
我最终做的是指示我的数据网格不要自动生成自己的列。相反,我将网格嵌入到用户控件中,当加载数据时,它会解析出所需的列并将它们加载到列表中。然后,我调用了以下方法:
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);
});
}
}
(ColumnDisplaySetupis a trivial type used to house the parsed-out configuration or a configuration loaded from a file.)
(ColumnDisplaySetup是一种普通类型,用于存放解析出的配置或从文件加载的配置。)
This method is called with the following arguments, respectively: list of columns, 0, and my arbitrary guess of 5 as a fairly safe number of columns to load at a time; but this number is based on testing and the expectation that a good number of grids could be loading simultaneously. I asked Tim for more information that might inform this part of the process, and will report back here if I learn more about how to determine how much is safe.
该方法分别使用以下参数调用:列列表 0 和我任意猜测的 5 作为一次加载的相当安全的列数;但这个数字是基于测试和预期可以同时加载大量网格。我向 Tim 询问了可能为流程的这一部分提供信息的更多信息,如果我了解更多关于如何确定多少是安全的,我会在这里报告。
In practice, this seems to work adequately, although it results in the sort of progressive rendering you'd expect, with the columns visibly popping in. I expect this could be improved both by using the maximum possible value for numToLoadand by other UI sleight-of-hand. I may investigate hiding the grid while the columns are generated and only showing the result when everything is ready. Ultimately the decision will come down to which feels more 'fast and fluid'.
在实践中,这似乎工作得很好,虽然它会导致你期望的那种渐进式渲染,列明显弹出。我希望这可以通过使用最大可能值numToLoad和其他 UI 技巧来改进 -手。我可能会研究在生成列时隐藏网格,并仅在一切准备就绪时才显示结果。最终决定将归结为哪种感觉更“快速和流畅”。
Again, I will update this answer with more information if I get it, but I hope this helps someone facing similar problems in the future. After pouring more time than I'd care to admit into the bug hunt, I don't want anyone else to have to kill themselves over this.
同样,如果我得到它,我会用更多信息更新这个答案,但我希望这有助于将来遇到类似问题的人。在投入了比我愿意承认的时间更多的时间之后,我不想让其他人为此而自杀。
回答by Gary Johnson
It appears that this issue is fixed in Windows 8.1 Preview, after retargeting my application for Windows 8.1. I can no longer recreate this issue by dumping thousands of visuals onto the screen.
在为 Windows 8.1 重新定位我的应用程序后,此问题似乎已在 Windows 8.1 预览版中得到解决。我无法再通过将数千个视觉效果倾倒到屏幕上来重现这个问题。

