Подтвердить что ты не робот

Таинственный "Недостаточно квоты для обработки этой команды" в порт WinRT DataGrid

Изменить 26 сентября

См. ниже полный фон. tl; dr: Управление сетью данных вызывает нечетные исключения, и я ищу помощь, чтобы изолировать причину и найти решение.

Я сузил это немного дальше. Я смог воспроизвести поведение в более маленьком тестовом приложении с более надежным запуском ошибочного поведения.

Я могу определенно исключить проблемы с потоками и (я думаю). Новое приложение не использует никаких задач или других потоков/асинхронных функций, и я могу вызвать необработанное исключение, просто добавив свойства, возвращающие константу в класс объектов, показанных в DataGrid. Это указывает на то, что проблема заключается либо в неуправляемом исчерпании ресурсов, либо в том, о чем я еще не думал.

Пересмотренная программа структурирована следующим образом. Я создал пользовательский элемент управления EntityCollectionGridView, который имеет метку и сетку данных. В элементе управления Loaded event я назначаю List<TestClass> сетке данных с 1000 или 10000 строк, позволяя сетке генерировать столбцы. Этот пользовательский элемент управления создается в 2-4 раза в MainPage.xaml на странице OnNavigatedTo event (или Loaded, это, похоже, не имеет значения). Если возникает исключение, это происходит сразу же после отображения MainPage.

Интересно, что поведение, похоже, не меняется в зависимости от количества отображаемых строк (он будет надежно работать со 10000 строками или надежно с 1000 строк в каждой сетке), а скорее с общим количеством столбцов в все сетки, загруженные в данный момент времени. С 20 свойствами, чтобы показать, 4 сетки прекрасно работают. С 35 свойствами и 4 сетками исключение исключается. Но если я устраню две сетки, тот же класс с 35 свойствами будет работать нормально.

Обратите внимание, что все свойства, которые я добавляю в TestClass для перехода от 20 до 35 столбцов, имеют форму:

public string StringXYZ { get { return "asdfasdfasdfasdfasf"; } }

Итак, в резервных данных нет дополнительной памяти (и, опять же, я не думаю, что проблема с памятью является проблемой).

Что вы все думаете? Опять же, дескрипторы/пользовательские объекты /etc в диспетчере задач выглядят хорошо, но есть ли что-то еще, что я могу потерять?

Оригинальное сообщение

Я работал над портом DataLink Silverlight Toolkit для WinRT, и он достаточно хорошо прошел в простых тестах (различные конфигурации и до 10000 строк). Однако, поскольку я попытался внедрить его в другое приложение WinRT, я столкнулся с спорадическим исключением (типа System.Exception, поднятым в обработчике App.UnhandledException), что очень сложно отлаживать.

Not enough quota is available to process this command. (Exception from HRESULT: 0x80070718)

Ошибка постоянно воспроизводима, но не детерминистична. То есть я могу сделать это каждый раз, когда я запускаю приложение, но это не всегда происходит, выполняя одинаковый точный набор шагов столько же раз. Вероятно, ошибка возникает при переходе страницы (будь то переход на новую страницу вперед или назад на предыдущую страницу), а не (например) при изменении ItemsSource в datagrid.

Структура приложения - это в основном рекурсивный доступ через иерархию со страницей, отображаемой на каждом уровне иерархии. На странице текущего node в иерархии показаны каждый дочерний элемент node и некоторые узлы внуков, а для любого поднода может отображаться дататайд. На практике я последовательно воспроизвожу это со следующей структурой навигации:

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++
[email protected]()  + 0x23 bytes  
[email protected]()  + 0xbd bytes  
[email protected]()  + 0xf8 bytes 
[email protected]()  + 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++
[email protected]()  + 0xceab bytes    
[email protected]@12()  + 0xe bytes 
[email protected]()  + 0x27 bytes   
[email protected]()  + 0x1b bytes    

Это указывает мне, что все, что я делаю неправильно, не регистрируется до тех пор, пока по крайней мере один цикл в цикле сообщений приложения (и я также попытался взломать все выброшенные исключения, используя "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

... но это ничего не значит для меня ни (это может быть даже не актуально).

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

  • Давление в памяти.Но я столкнулся с этим, при этом 24% свободной физической памяти и приложение потребляют менее 100 МБ памяти. В другое время приложение не будет испытывать каких-либо проблем, перемещаясь через некоторое время и загружая 400 МБ памяти.
  • Проблемы с потоками,, такие как доступ к потоку пользовательского интерфейса из рабочего потока. И, фактически, у меня есть доступ к данным, происходящим в фоновом потоке. Но это использует (портированные) рамки, которые были очень надежны в среде WinForms и в плагине Outlook, и я думаю, что использование потоков безопасно. Кроме того, я могу использовать одни и те же данные в этом приложении без каких-либо проблем с привязкой только к ListViews и т.д. Наконец, Grandchild node сконфигурирован таким образом, что выбор строки в первом datagrid запускает запрос для элементов детализации строки, которые отображаются во втором datagrid (который изначально пуст и может оставаться таким, чтобы не предотвратить исключение), Это происходит без перехода на страницу и работает безупречно, пока я предпочитаю играть с выбором. Но переход обратно к Ребенку может сразу меня убить, хотя в этот момент не должно быть доступа к данным и, следовательно, не выполняются операции потоковой передачи, о которых я знаю.
  • Исчерпывание ресурсов, возможно, GUI-дескрипторы. Но я не думаю, что я оказываю такое давление на эту систему. В одном выполнении, разбивая обработчик исключений, диспетчер задач сообщает о процессе, используя 662 дескриптора, 21 пользовательский объект и 12 объектов GDI, по сравнению с Tweetro, который использует 734, 37 и 19 соответственно без проблем. Что еще я могу пропустить в этой категории?

У меня много свободного места на диске, и я все равно не использую диск для чего-либо, кроме конфигурационных файлов (и все это отлично работало, прежде чем добавлять datagrids).

Моя следующая мысль заключалась в том, чтобы попытаться выполнить некоторые потенциальные "интересные" части кода datagrid и перепрыгнуть через все, что было сомнительно. Я попробовал это с помощью datagrid ArrangeOverride, но исключение, похоже, не заботило, сделал я это или нет. Кроме того, я не уверен, что это полезная стратегия. Поскольку исключение не выбрасывается до цикла цикла сообщения, и, поскольку я не могу точно знать, когда это произойдет, мне нужно будет покрыть огромное количество перестановок, запустив каждую перестановку раз, чтобы изолировать код проблемы.

Ошибка возникает в режимах отладки и выпуска. И, как заключительная фоновая заметка, объем данных, с которыми мы имеем дело здесь, является небольшим, намного меньшим, чем мои 10000-рядовые прогоны datagrid в изоляции. Это, вероятно, порядка 50-100 строк, возможно, 30-40 столбцов. И до того, как будет выбрано исключение, данные и сетки, похоже, работают и прекрасно реагируют.

Итак, почему я пришел к вам. Мои два вопроса:

  • Предоставляет ли информация об ошибках какие-либо подсказки о том, что может быть проблемой?
  • Какую стратегию отладки вы использовали бы для изоляции кода проблемы?

Большое спасибо за любую помощь, которую вы можете предоставить!

4b9b3361

Ответ 1

ОК, с некоторым критическим входом от Tim Heuer [MSFT], я понял, что происходит и как обойти эту проблему.

Удивительно, но ни одно из моих трех первоначальных предположений не было правильным. Речь шла не о памяти, потоковом или системном ресурсах. Вместо этого речь шла об ограничениях в системе обмена сообщениями Windows. По-видимому, это немного похоже на исключение, поскольку при одновременном внесении слишком большого количества изменений в визуальное дерево очередь асинхронного обновления становится настолько длинной, что она отключает провод, и исключение получает.

В этом случае проблема заключается в том, что в сетку данных, с которой я работаю, имеется достаточное количество UIElements, которое позволяет сетке генерировать все свои собственные столбцы одновременно, в некоторых случаях может превышать лимит. Я использовал сразу несколько сеток и всю загрузку в ответ на события навигации по страницам, из-за которых все было сложнее прибить.

К счастью, ограничения, с которыми я столкнулся, были НЕ ограничениями в визуальном дереве или подсистеме UML 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 ловкостью рук. Я могу исследовать скрытие сетки при создании столбцов и показывать результат только тогда, когда все будет готово. В конечном итоге решение придет к тому, что станет более "быстрым и текучим".

Опять же, я обновлю этот ответ с дополнительной информацией, если я получу его, но я надеюсь, что это поможет кому-то столкнуться с подобными проблемами в будущем. После того, как вы пролили больше времени, чем я хотел бы признать в охоте за ошибками, я не хочу, чтобы кто-то еще мог убить себя над этим.

Ответ 2

Похоже, эта проблема исправлена ​​в Windows 8.1 Preview, после перенастройки моего приложения на Windows 8.1. Я больше не могу воссоздать эту проблему, сбросив тысячи изображений на экран.