Таинственная "недостаточно квота доступна для обработки этой команды" в порту 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

...но для меня это тоже ничего не значит (возможно, это даже не имеет значения).

три теории, которые я могу придумать, таковы:

  1. нехватка памяти. но я столкнулся с этим с 24% моей физической памяти бесплатно и приложение потребляет менее 100 МБ памяти. В других случаях приложение не будет иметь никаких проблем навигация вокруг некоторое время и вешалка до 400MB памяти
  2. проблемы с потоками, например, доступ к потоку пользовательского интерфейса из рабочего потока. И, на самом деле, у меня есть доступ к данным происходит в фоновом потоке. Но это использование (портированной) структуры, которая была очень надежной в среде WinForms и в плагине Outlook, и я думаю, что использование потока безопасно. Кроме того, я могу использовать те же данные в этом приложении без каких-либо проблем привязки только к ListViews и так далее. Наконец, узел внука настроен таким образом, что выбор строки в первом datagrid запускает запрос для подробных элементов строки, которые отображаются во втором datagrid (который изначально пуст и может оставаться таковым без предотвращения исключения). Это происходит без перехода страницы и работает безупречно до тех пор, пока я решаю возиться с выбором. Но возвращение к ребенку может убить меня сразу же, даже несмотря на то, что в этот момент не должно быть доступа к данным и поэтому я не знаю о потоковых операциях.
  3. истощение ресурсов какой-то, может быть, 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. Я больше не могу воссоздавать эту проблему, сбрасывая тысячи визуальных эффектов на экран.