Modifica Sept 26Misterioso "Non è sufficiente quota è disponibile per elaborare questo comando" nel porto di WinRT DataGrid
Vedere sotto per l'intero sfondo. tl; dr: un controllo della griglia di dati sta causando strane eccezioni, e sto cercando aiuto per isolare la causa e trovare una soluzione.
Ho ristretto un po 'di più. Sono stato in grado di riprodurre il comportamento in un'app di prova più piccola con un innesco più affidabile del comportamento irregolare.
Posso senz'altro escludere problemi di threading e (penso) di memoria. La nuova app non utilizza attività o altre funzionalità di threading/asincrone e posso attivare l'eccezione non gestita semplicemente aggiungendo le proprietà che restituiscono una costante alla classe di oggetti mostrata in DataGrid. Questo mi indica che il problema è o nell'esaurimento delle risorse non gestito o qualcosa a cui non ho ancora pensato.
Il programma modificato è strutturato in questo modo. Ho creato un controllo utente chiamato EntityCollectionGridView
che ha un'etichetta e una griglia di dati. Nel gestore di eventi Loaded del controllo, assegno un List<TestClass>
alla griglia di dati con 1000 o 10000 righe, lasciando che la griglia generi le colonne. Questo controllo utente viene istanziato 2-4 volte in MainPage.xaml nell'evento OnNavigatedTo
della pagina (o Loaded
, non sembra avere importanza). Se si verifica un'eccezione, si verifica immediatamente dopo la visualizzazione di MainPage.
La cosa interessante è che il comportamento non sembra variare con il numero di righe mostrato (funzionerà in modo affidabile con 10000 righe o fallirà in modo affidabile con solo 1000 righe in ogni griglia) ma piuttosto con il numero totale di colonne in tutte le griglie caricate in un dato momento. Con 20 proprietà da mostrare, 4 griglie funzionano bene. Con 35 proprietà e 4 griglie, viene generata l'eccezione. Ma se elimino due griglie, la stessa classe con 35 proprietà funzionerà correttamente.
Si noti che tutte le proprietà aggiungo a TestClass
per saltare da 20 a 35 colonne sono della forma:
public string StringXYZ { get { return "asdfasdfasdfasdfasf"; } }
Quindi, non c'è memoria aggiuntiva nei dati backing (e ancora una volta, I don' Penso che la pressione della memoria sia comunque il problema).
Cosa ne pensate? Ancora, gli handle/oggetti utente/etc in Task Manager sembrano buoni, ma c'è qualcos'altro che potrebbe mancare?
Original post
Ho lavorato su una porta della Silverlight Toolkit DataGrid a WinRT, e lo ha fatto abbastanza bene nei test semplici (una varietà di configurazioni e fino a 10000 righe). Tuttavia, poiché ho provato a incorporarlo in un'altra app WinRT, ho eseguito un'eccezione sporadica (di tipo System.Exception, generata nel gestore App.UnhandledException) che si sta dimostrando molto difficile da eseguire il debug.
Not enough quota is available to process this command. (Exception from HRESULT: 0x80070718)
L'errore è costantemente riproducibile, ma non deterministico. Cioè, posso farlo accadere ogni volta che eseguo l'app, ma non sempre accade eseguendo lo stesso esatto set di passaggi lo stesso numero di volte. Sembra che l'errore si verifichi sulle transizioni di pagina (se si sta navigando verso una nuova pagina in avanti o su una pagina precedente), e non (ad esempio) quando si modifica l'origine articoli del datagrid.
La struttura dell'applicazione è fondamentalmente l'accesso ricorsivo attraverso una gerarchia, con una pagina visualizzata a ogni livello gerarchico. Nella pagina per il nodo corrente nella gerarchia, vengono mostrati ciascun nodo figlio e alcuni nodi nipotino, e per ogni sottonodo può essere mostrato un datagrid. In pratica, ho sempre riprodurre questo con la seguente struttura di navigazione:
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
Un tipico scenario di test è, iniziare alla radice, spostarsi Bambino, passare al nipote, tornare al bambino, e poi quando provo a navigare al nipote di nuovo, fallisce con l'eccezione che ho menzionato sopra. Ma potrebbe fallire la prima volta che colpisco Nipote, o potrebbe farmi muovere avanti e indietro un paio di volte prima di fallire.
Lo stack di chiamate ha solo un frame gestito su di esso, che è il gestore di eventi di eccezione non gestito. Questo è molto inutile. Il passaggio a debug modalità mista, ottengo il seguente:
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
Questo indica a me che tutto quello che sto facendo male non registra fino a dopo almeno un ciclo nel ciclo di messaggi del app (e ho provato anche la rottura su tutte le eccezioni generate usando "Debug | Exceptions ..." - per quanto posso dire, nulla viene gettato e ingoiato). I telai di stack interessanti che vedo sono WindowProc
, OnReentrancyProtectedWindowMessage
e Tick
. Il msg
è 0x402 (1026), il che non significa nulla per me. This page liste di messaggi come quello usato nei seguenti contesti:
CBEM_SETIMAGELIST
DDM_CLOSE
DM_REPOSITION
HKM_GETHOTKEY
PBM_SETPOS
RB_DELETEBAND
SB_GETTEXTA
TB_CHECKBUTTON
TBM_GETRANGEMAX
WM_PSD_MINMARGINRECT
... ma questo non significa niente molto per me, sia (che potrebbe anche non essere rilevante).
I tre teorie che posso venire con sono queste: la pressione
- memoria. Ma mi sono imbattuto in questo con il 24% della mia memoria fisica libera e l'app che consumava meno di 100 MB di memoria. Altre volte, l'app non ha riscontrato problemi durante la navigazione e accumula 400 MB di memoria
- Problemi di threading, come l'accesso al thread dell'interfaccia utente da un thread di lavoro. E, in effetti, ho accesso ai dati in corso su un thread in background. Ma questo sta usando un framework (porting) che è stato molto affidabile in un ambiente WinForms e in un plugin di Outlook, e penso che l'uso del thread sia sicuro. Inoltre, posso utilizzare gli stessi dati in questa app senza problemi di binding solo a ListViews e così via. Infine, il nodo Grandchild è configurato in modo tale che la selezione di una riga nel primo datagrid avvia una richiesta per gli elementi di dettaglio della riga, che vengono visualizzati nel secondo datagrid (che è inizialmente vuoto e può rimanere tale senza impedire l'eccezione). Questo accade senza una transizione di pagina e funziona perfettamente fino a quando scelgo di giocare con la selezione. Ma tornare a Child potrebbe uccidere me subito, anche se a quel punto non dovrebbero esserci accesso ai dati e quindi non operazioni di threading che io conosca.
- esaurimento risorse di qualche tipo, forse maniglie GUI. Ma non penso di mettere molta pressione su questo sistema. In una esecuzione, interrompendo il gestore di eccezioni, Task Manager segnala il processo utilizzando 662 handle, 21 oggetti utente e 12 oggetti GDI, rispetto a Tweetro che utilizza rispettivamente 734, 37 e 19 senza problemi. Cos'altro potrei mancare in questa categoria?
Ho un sacco di spazio libero su disco e non sto utilizzando il disco per qualcosa di diverso dai file di configurazione comunque (e tutto ciò ha funzionato bene prima di aggiungere i datagrids).
Il mio prossimo pensiero è stato quello di provare a passare attraverso alcune delle potenziali parti "interessanti" del codice datagrid e saltare su quelle che erano discutibili. L'ho provato con ArrangeOverride del datagrid, ma a questa eccezione sembrava non importare se l'avessi fatto o no. Inoltre, non sono sicuro che questa sia una strategia utile. Dal momento che l'eccezione non viene lanciata fino a dopo un ciclo nel ciclo dei messaggi, e poiché non posso sapere con certezza quando sta per accadere, avrei bisogno di coprire un numero enorme di permutazioni, eseguendo ogni permutazione un sacco di volte, per isolare il codice problema.
L'errore viene generato in entrambe le modalità Debug e Release. E, come nota finale, la quantità di dati con cui abbiamo a che fare qui è piccola, molto più piccola delle mie serie da 10000 righe del datagrid in isolamento. Probabilmente è dell'ordine di 50-100 righe, con forse 30-40 colonne. E prima che l'eccezione venga lanciata, i dati e le griglie sembrano funzionare e rispondono bene.
Quindi, ecco perché vengo da voi. Le mie due domande sono:
- Le informazioni di errore danno qualche suggerimento su quale potrebbe essere il problema?
- Quale strategia di debug verrebbe utilizzata per isolare il codice problema?
Mille grazie in anticipo per qualsiasi aiuto tu possa fornire!
Non sembra funzionare anche sulla mia macchina Win8.1. – digitalMoto
@digitalMoto Vuoi dire che vedi questo errore in 8.1, o che non riesci a riprodurre questo problema in 8.1? –
Questo sembra improbabile. La documentazione di PostMessage qui: https://msdn.microsoft.com/en-us/library/ms644944.aspx?f=255&MSPPError=-2147217396 --- afferma che "C'è un limite di 10.000 messaggi inviati per coda di messaggi". e niente dice che c'è un limite diverso in Windows 8.1 –