Таинственная "недостаточно квота доступна для обработки этой команды" в порту WinRT DataGrid
Редактировать 26 Сентября
см. ниже для полного фона. tl; dr: элемент управления сеткой данных вызывает нечетные исключения, и я ищу помощь в изоляции причины и поиске решения.
я сузил это немного дальше. Я смог воспроизвести поведение в меньшем тестовом приложении с более надежным запуском неустойчивого поведения.
я определенно могу исключить как резьбу, так и (я думаю) память проблемы. Новое приложение не использует задачи или другие потоковые / асинхронные функции, и я могу вызвать необработанное исключение, просто добавив свойства, возвращающие константу классу объектов, показанных в DataGrid. Это указывает на то, что проблема заключается либо в неуправляемом исчерпании ресурсов, либо в чем-то, о чем я еще не думал.
пересмотренная программа структурирована следующим образом. Я создал пользовательский элемент управления EntityCollectionGridView
который имеет метку и сетку данных. В управлении Загруженный обработчик событий, я назначаю List<TestClass>
в таблице с 1000 или 10000 строк, позволяя генерировать сетку колонн. Этот пользовательский элемент управления создается 2-4 раза в MainPage.xaml на странице OnNavigatedTo
событие (или Loaded
, это не важно). Если возникает исключение, оно возникает сразу после отображения MainPage.
интересно то, что поведение, похоже, не зависит от количества показанных строк (оно будет надежно работать с 10000 строками или с ошибкой только 1000 строк в каждой сетке), а общее число столбцов во всех сетях, загруженных в данный момент времени. С 20 свойствами, котор нужно показать, 4 решетки работают отлично. С 35 свойствами и 4 сетками создается исключение. Но если я исключу две сетки, один и тот же класс с 35 свойствами будет работать нормально.
обратите внимание, что все свойства, которые я добавляю в TestClass
перейти от 20 до 35 столбцов имеют вид:
public string StringXYZ { get { return "asdfasdfasdfasdfasf"; } }
таким образом, в резервных данных нет дополнительной памяти (и опять же, я не думаю, что давление памяти является проблемой в любом случае).
что вы все думаете? Опять же, дескрипторы / пользовательские объекты / etc в Диспетчере задач выглядят хорошо, но есть ли что-то еще, что я могу пропустить?
оригинальное сообщение
я работал над портом Silverlight Toolkit DataGrid для WinRT, и он достаточно хорошо справился с простыми тестами (различные конфигурации и до 10000 строк). Однако, как я попытался встраивать его в другое приложение WinRT я столкнулся с спорадическим исключением (типа System.Исключение, поднятое в приложении.Unhandledexception handler), который оказывается очень сложным для отладки.
Not enough quota is available to process this command. (Exception from HRESULT: 0x80070718)
ошибка последовательно воспроизводима, но не детерминированно. То есть, я могу сделать это каждый раз, когда я запускаю приложение, но это не всегда происходит, выполняя один и тот же точный набор шагов одно и то же количество раз. Кажется, что ошибка возникает при переходах страницы (будь то переход к новому страница вперед или назад к предыдущей странице), а не (например) при изменении ItemsSource datagrid.
структура приложения в основном рекурсивный доступ через иерархию, со страницей, показанной на каждом уровне иерархии. На странице для текущего узла в иерархии показаны каждый дочерний узел и некоторые узлы внука, а для любого подузла может быть показана datagrid. На практике, я постоянно воспроизводить это с следующей навигации структура:
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
типичный тестовый сценарий-начать с корня, перейти к ребенку, перейти к внуку, вернуться к ребенку, а затем, когда я снова пытаюсь перейти к внуку, он терпит неудачу с исключением, о котором я упоминал выше. Но он может потерпеть неудачу, когда я в первый раз ударю внука, или может позволить мне несколько раз двигаться вперед и назад, прежде чем потерпеть неудачу.
стек вызовов имеет только один управляемый фрейм, который является обработчиком необработанных событий исключений. Это очень бесполезно. Переключаясь на отладку в смешанном режиме, я получаю следующее:
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
. The msg
это 0x402 (1026), что для меня ничего не значит. на этой странице перечисляет это сообщение, используемое в следующих контекстах:
CBEM_SETIMAGELIST
DDM_CLOSE
DM_REPOSITION
HKM_GETHOTKEY
PBM_SETPOS
RB_DELETEBAND
SB_GETTEXTA
TB_CHECKBUTTON
TBM_GETRANGEMAX
WM_PSD_MINMARGINRECT
...но для меня это тоже ничего не значит (возможно, это даже не имеет значения).
три теории, которые я могу придумать, таковы:
- нехватка памяти. но я столкнулся с этим с 24% моей физической памяти бесплатно и приложение потребляет менее 100 МБ памяти. В других случаях приложение не будет иметь никаких проблем навигация вокруг некоторое время и вешалка до 400MB памяти
- проблемы с потоками, например, доступ к потоку пользовательского интерфейса из рабочего потока. И, на самом деле, у меня есть доступ к данным происходит в фоновом потоке. Но это использование (портированной) структуры, которая была очень надежной в среде WinForms и в плагине Outlook, и я думаю, что использование потока безопасно. Кроме того, я могу использовать те же данные в этом приложении без каких-либо проблем привязки только к ListViews и так далее. Наконец, узел внука настроен таким образом, что выбор строки в первом datagrid запускает запрос для подробных элементов строки, которые отображаются во втором datagrid (который изначально пуст и может оставаться таковым без предотвращения исключения). Это происходит без перехода страницы и работает безупречно до тех пор, пока я решаю возиться с выбором. Но возвращение к ребенку может убить меня сразу же, даже несмотря на то, что в этот момент не должно быть доступа к данным и поэтому я не знаю о потоковых операциях.
- истощение ресурсов какой-то, может быть, GUI ручки. Но я не думаю, что оказываю такое давление на эту систему. В одном выполнении, взломав обработчик исключений, Диспетчер задач сообщает о процессе, используя 662 дескриптора, 21 объект пользователя и 12 объектов GDI, по сравнению с Tweetro, который использует 734, 37 и 19 соответственно без проблем. Что еще я могу упустить в этом? категория?
у меня много свободного места на диске, и я не использую диск ни для чего, кроме файлов конфигурации (и все это отлично работало перед добавлением datagrids).
ошибка возникает как в режиме отладки, так и в режиме выпуска. И, в качестве заключительного Примечания, объем данных, с которыми мы имеем дело здесь, невелик, намного меньше, чем моя 10000-строка запускает datagrid в изоляции. Это, вероятно, порядка 50-100 строк, возможно, с 30-40 столбцами. И прежде чем исключение будет выброшено, данные и сетки, похоже, работают и реагируют нормально.
2 ответов
хорошо, с некоторыми критический вход от Тима Хойера [MSFT], я понял, что происходит, и как обойти эту проблему.
Удивительно, но ни одна из моих трех первоначальных догадок не была правильной. Речь шла не о памяти, потоках или системных ресурсах. Вместо этого речь шла об ограничениях в системе обмена сообщениями Windows. По-видимому, это немного похоже на исключение переполнения стека, поскольку, когда вы делаете слишком много изменений в визуальном дереве сразу, асинхронный очередь обновлений становится настолько длинной, что она отключает провод, и возникает исключение.
в этом случае проблема заключается в том, что в сетке данных, с которой я работаю, достаточно UIElements, что позволяет сетке генерировать все свои собственные столбцы сразу, в некоторых случаях может превышать предел. Я использовал несколько сеток сразу, и все они загружались в ответ на события навигации по страницам, что делало задачу более сложной.
к счастью, ограничения, которые я был запуск не был ограничениями в визуальном дереве или самой подсистеме пользовательского интерфейса XAML, а только в сообщениях, используемых для его обновления. Это означает, что если бы я мог распределять одни и те же операции по нескольким тактам часов диспетчера, я мог бы достичь одного и того же конечного результата.
я решил было поручить моей сетки данных не автоматически заполнять свои столбцы. Вместо этого я встроил сетку в пользовательский элемент управления, который при загрузке данных будет анализировать необходимые столбцы и загрузите их в список. Затем я вызвал следующий метод:
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 как довольно безопасном количестве столбцов для загрузки за раз; но это число основано на тестировании и ожидании, что может загружаться большое количество сеток одновременно. Я попросил Тима предоставить больше информации, которая могла бы помочь в этой части процесса, и сообщу здесь, Если узнаю больше о том, как определить, насколько безопасно.
на практике это, кажется, работает адекватно,хотя это приводит к прогрессивному рендерингу, который вы ожидаете, с заметными столбцами. Я ожидаю, что это может быть улучшено с помощью максимально возможного значения numToLoad
и другим UI ловкостью рук. Я могу исследовать скрытие сетки в то время как столбцы генерируются и показывают результат только тогда, когда все готово. В конечном счете решение будет сводиться к тому, что чувствует себя более "быстрым и текучим".
опять же, я обновлю этот ответ с дополнительной информацией, если я его получу, но я надеюсь, что это поможет кому-то, кто сталкивается с подобными проблемами в будущем. Потратив на охоту на жуков больше времени, чем мне хотелось бы, я не хочу, чтобы кто-то еще убил себя из-за этого.
похоже, что эта проблема исправлена в Windows 8.1 Preview после переназначения моего приложения для Windows 8.1. Я больше не могу воссоздавать эту проблему, сбрасывая тысячи визуальных эффектов на экран.