2012-09-25 13 views
35
のWinRTのポートに

「が足りませんクォータは、このコマンドを処理するのに利用可能である」編集9月26日謎のDataGrid

は完全な背景については、以下を参照してください。 tl; dr:データグリッドコントロールが奇数の例外を引き起こしています。私は原因を特定し、解決策を見つけるのを助けたいと考えています。

これをもう少し絞り込んだ。私は、不安定な振る舞いのより信頼性の高いトリガーで小さなテストアプリでその振る舞いを再現することができました。

私は間違いなくスレッドの問題と(私は思うが)メモリの問題を除外することができます。新しいアプリケーションでは、タスクやその他のスレッド/非同期機能は使用されず、DataGridに表示されるオブジェクトのクラスに定数を返すプロパティを追加するだけで、未処理の例外をトリガーできます。これは、問題が管理されていないリソースの枯渇か、まだ考えていないことのいずれかであることを私に示しています。

改訂されたプログラムは、このように構成されています。私はラベルとデータグリッドを持つEntityCollectionGridViewと呼ばれるユーザーコントロールを作成しました。コントロールのLoadedイベントハンドラでは、グリッドに列を生成させるために、1000または10000行のデータグリッドにList<TestClass>を割り当てます。このユーザーコントロールは、ページのOnNavigatedToイベント(またはLoaded、重要ではないようです)のMainPage.xamlで2〜4回インスタンス化されます。例外が発生した場合は、MainPageが表示された直後に発生します。

興味深いことに、表示される行の数によって動作が異なるようには見えません(10000行では確実に動作しますが、各グリッドでは1000行だけで確実に動作しません)与えられた時間にロードされたすべてのグリッドで表示する20のプロパティで、4つのグリッドが正常に動作します。 35のプロパティと4つのグリッドで、例外がスローされます。しかし、2つのグリッドを削除すると、35のプロパティを持つ同じクラスが正常に動作します。

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

ので、バッキング・データには、追加のメモリがありません(と、再び、私は「ドン:私はTestClassに追加するすべてのプロパティが20〜35列からジャンプする

注の形式です。とにかくメモリの圧力が問題だと思う)。

あなたはどう思いますか?繰り返しますが、タスクマネージャーのハンドル/ユーザーオブジェクト/ etcはうまく見えますが、他に何か不足している可能性がありますか?

オリジナルポスト

私はWinRTのにSilverlightのツールキットのDataGridのポートに取り組んできた、それは簡単なテスト(様々な構成と10000行まで)で十分に行っています。しかし、私はそれを別のWinRTアプリケーションに埋め込もうとしたので、デバッグが非常に困難であることが判明している散発的な例外(App.UnhandledExceptionハンドラで発生したSystem.Exception型の例外)が発生しました。

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

エラーは一貫して再現可能ですが、確定的ではありません。つまり、私はアプリケーションを実行するたびにそれを実現させることができますが、同じ正確なステップセットを同じ回数実行することによって常に起こるとは限りません。このエラーは、ページ遷移(新しいページを前のページに移動するか、前のページに戻すか)に発生するように見えますが、DataGridのItemsSourceを変更する場合などはそうではありません。

アプリケーション構造は、基本的には階層を介した再帰的アクセスで、各階層レベルにページが表示されます。階層内の現在のノードのページには、各子ノードといくつかの孫ノードが表示され、どのサブノードに対しても、データグリッドが表示されます。実際には、私は一貫して、次のナビゲーション構造でこれを再現:

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 

典型的なテストシナリオがある、私が移動しようとすると、その後、ルートから始まる子に移動し、孫に移動し、子供に戻って移動し、孫にもう一度、私は上記の例外で失敗します。しかし、それは私がGrandchildを初めて叩くときに失敗するか、失敗する前に数回前後に動かすかもしれません。

コールスタックには、未処理の例外イベントハンドラであるマネージフレームが1つしかありません。これは非常に役に立たない。混合モードのデバッグへの切り替え、私は以下の取得:

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  

これは何でも私が間違ってやっていることは、アプリケーションのメッセージループに少なくとも1サイクル後まで登録していないことを私に示して(私も破壊しようとしました"Debug | Exceptions ..."を使用してスローされたすべての例外について - 私が知る限り、何もスローされずに飲み込まれます)。興味のあるスタックフレームは、WindowProc,OnReentrancyProtectedWindowMessageTickです。 msgは0x402(1026)です。これは私に何の意味もありません。 This pageリスト次のコンテキストで使用されるように、そのメッセージ:

CBEM_SETIMAGELIST 
DDM_CLOSE 
DM_REPOSITION 
HKM_GETHOTKEY 
PBM_SETPOS 
RB_DELETEBAND 
SB_GETTEXTA 
TB_CHECKBUTTON 
TBM_GETRANGEMAX 
WM_PSD_MINMARGINRECT 

...しかし、それは私にはあまり何も意味しない、のいずれか(それも適切でない可能性があります)。私が思い付くことができ

3つの理論はこれらは以下のとおりです。

  1. メモリ圧力。しかし、私は物理メモリの24%を無料で利用し、アプリケーションは100MB未満のメモリを消費しています。他の時間には、アプリケーションはしばらくの間ナビゲートして400MBのメモリをラップアップする問題を打つことはありません。
  2. ワーカーの問題、ワーカースレッドからのUIスレッドへのアクセスなどのスレッドの問題。そして、実際には、バックグラウンドスレッドでデータアクセスが発生しています。しかし、これはWinForms環境とOutlookプラグインで非常に信頼性の高い(移植された)フレームワークを使用しており、スレッドの使用は安全だと思います。さらに、私はListViewsだけでバインディングに問題なく、このアプリケーションで同じデータを使用することができます。最後に、第1のデータグリッド内の行を選択すると、第2のデータグリッドに表示される行の詳細項目の要求が開始されるように、ノードが構成されます(最初は空です。これはページ遷移なしで起こり、選択したものを選ぶ限り、完璧に動作します。しかし、その時点でデータへのアクセスがなく、したがって私が知っているスレッド操作ではないはずであるにもかかわらず、子へのナビゲートはすぐに私を殺すかもしれません。
  3. リソースの枯渇、多分GUIハンドル。しかし、私はこのシステムに大きな圧力をかけているとは思わない。 1つの実行で、例外ハンドラを壊すと、Task Managerは問題なく734,37、および19をそれぞれ使用しているTweetroと比較して、662ハンドル、21ユーザーオブジェクト、および12 GDIオブジェクトを使用してプロセスを報告します。このカテゴリには他に何が欠けていますか?

私は十分な空きディスク容量を持っていますが、構成ファイル以外にもディスクを使用していません(データグリッドを追加する前に問題なく動作しています)。

私の次の考えは、データグリッドコードの潜在的な「興味深い」部分を踏んで、疑わしいものを飛び越そうとしていました。私はDataGridのArrangeOverrideでそれを試しましたが、例外は私がそうしたかどうか気にしていないようでした。また、私はこれが有用な戦略であるとは確信していません。例外がメッセージループのサイクルの後に投げ込まれるまでではないので、いつ起こるのか分からないので、膨大な数の並べ替えをカバーする必要があります。問題のコードを特定することができます。

デバッグモードとリリースモードの両方でエラーがスローされます。また、最終的なバックグラウンドノートとして、ここで扱っているデータの量は小さく、私の10000行のデータグリッドを単独で実行するよりはるかに小さいです。おそらく50〜100行、おそらく30〜40列のオーダーです。例外がスローされる前に、データとグリッドが正常に動作しているように見えます。

だから私はあなたに来るのです。私の2つの質問は次のとおりです。

  1. エラー情報は、問題の可能性に関するヒントを教えてくれますか?
  2. 問題のコードを特定するためにどのようなデバッグ戦略を使用しますか?

事前にお申し込みいただきありがとうございます。

答えて

42

[OK]を、いくつかのcritical input from Tim Heuer [MSFT]と、私は何が起こっていたとこの問題を回避する方法を考え出しました。

驚いたことに、私の最初の3つの推測はどれも正しいものではありませんでした。これはメモリ、スレッディング、システムリソースに関するものではありません。代わりに、Windowsのメッセージングシステムの制限がありました。明らかに、ビジュアルツリーを一度に多くの変更を行うと、非同期の更新キューが長くなりすぎてワイヤがトリップし、例外がスローされるという点で、スタックオーバーフロー例外のようなものです。

この場合、データグリッドに入るUIElementsが十分にあり、グリッドがすべての列を一度に生成できるようにしている場合があります。私は一度に多数のグリッドを使用していましたが、すべての読み込みはページナビゲーションイベントに応答していました。

ありがたいことに、私が実行していた制限は、ビジュアルツリーまたはXAML UIサブシステム自体の制限ではなく、メッセージを更新するために使用されました。これは、ディスパッチャのクロックの複数のティックにわたって同じオペレーションを広げることができれば、私は同じ最終結果を達成できることを意味します。

私がやったことは、データグリッドに独自の列を自動生成させないよう指示することでした。代わりに、データがロードされたときに必要な列を解析してリストにロードするユーザーコントロールにグリッドを埋め込みました。その後、私は次のメソッドを呼び出し:

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は、一度にロードする列のかなり安全な数です。この数は、テストと、多数のグリッドが同時に読み込まれる可能性があることに基づいています。私はTimにプロセスのこの部分を知らせる情報を求めました。どれくらい安全であるかを判断する方法については、ここで報告します。

これは実際には十分に機能しているように見えますが、列が視覚的にポップインするようなプログレッシブレンダリングが行われます。numToLoadの最大値と他のUIの巧妙さによって。列が生成されている間にグリッドを非表示にして、すべての準備が整ったときだけ結果を表示するかどうかを調べることがあります。最終的には、「迅速かつ流動的」な意思決定が下されます。

また、この回答を得るにはこの情報を追加して更新しますが、今後同様の問題に直面している人に役立つことを願っています。私がバグ狩りを認めようとするよりも時間を注ぎ込んだ後、私は他の誰かがこれを使って自分自身を殺さなければならないことを望んでいない。

-1

Windows 8.1のアプリケーションのターゲットを変更した後、この問題はWindows 8.1 Previewで修正されているようです。数千のビジュアルを画面にダンプすることで、この問題を再現することはできません。

+1

私のWin8.1マシンでも動作していません。 – digitalMoto

+0

@digitalMoto 8.1でこの失敗が見られるか、8.1でこの問題を再現できないのですか? –

+2

これは起こりそうにありません。 PostMessageのドキュメントは次のとおりです。https://msdn.microsoft.com/en-us/library/ms644944.aspx?f=255&MSPPError=-2147217396 ---「メッセージキューごとに送信されるメッセージは10,000件に制限されています。何もWindows 8.1には異なる制限があるとは言いません –