2012-09-25 17 views
35

Chỉnh sửa lúc: Tháng Bảy 26Mysterious "Không đủ hạn ngạch hiện có sẵn để xử lý lệnh này" tại cảng WinRT của DataGrid

Xem bên dưới cho nền đầy đủ. tl; dr: Một điều khiển lưới dữ liệu gây ra các ngoại lệ lẻ, và tôi đang tìm kiếm sự giúp đỡ cô lập nguyên nhân và tìm ra giải pháp.

Tôi đã thu hẹp điều này xuống thêm một chút. Tôi đã có thể tái tạo hành vi trong một ứng dụng thử nghiệm nhỏ hơn với hoạt động đáng tin cậy hơn về hành vi thất thường.

Tôi chắc chắn có thể loại trừ cả vấn đề về luồng và (tôi nghĩ). Ứng dụng mới không sử dụng Tác vụ hoặc các tính năng không đồng bộ/luồng khác và tôi có thể kích hoạt ngoại lệ không được xử lý đơn giản bằng cách thêm các thuộc tính trả về hằng số cho lớp đối tượng được hiển thị trong DataGrid. Điều này cho tôi thấy rằng vấn đề là do cạn kiệt tài nguyên không được quản lý hoặc điều tôi chưa nghĩ đến.

Chương trình sửa đổi được cấu trúc như thế này. Tôi đã tạo một điều khiển người dùng có tên là EntityCollectionGridView có một nhãn và một lưới dữ liệu. Trong trình xử lý sự kiện được tải của điều khiển, tôi gán một List<TestClass> vào lưới dữ liệu với 1000 hoặc 10000 hàng, cho phép lưới tạo ra các cột. Điều khiển người dùng này được khởi tạo 2-4 lần trong MainPage.xaml trong sự kiện OnNavigatedTo của trang (hoặc Loaded, điều này dường như không quan trọng). Nếu một ngoại lệ xảy ra, nó xảy ra ngay sau khi MainPage được hiển thị.

Điều thú vị là, hành vi dường như không thay đổi với số hàng được hiển thị (nó sẽ hoạt động đáng tin cậy với 10000 hàng hoặc không đáng tin cậy chỉ với 1000 hàng trong mỗi lưới) mà đúng hơn với tổng số cột trong tất cả các lưới được nạp tại một thời điểm nhất định. Với 20 tài sản để hiển thị, 4 lưới hoạt động tốt. Với 35 thuộc tính và 4 lưới, ngoại lệ được ném ra. Nhưng nếu tôi loại bỏ hai lưới, cùng một lớp với 35 thuộc tính sẽ hoạt động tốt.

Lưu ý rằng tất cả các thuộc tính tôi thêm vào TestClass để nhảy 20-35 cột có dạng:

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

Vì vậy, không có bộ nhớ bổ sung trong dữ liệu ủng hộ (và một lần nữa, tôi không' t nghĩ rằng áp lực bộ nhớ là vấn đề anyway).

Tất cả các bạn nghĩ gì? Một lần nữa, các xử lý/đối tượng người dùng/etc trong Task Manager trông tốt, nhưng có cái gì khác tôi có thể bị mất?

bài gốc

Tôi đã được làm việc trên một cổng của Silverlight Toolkit DataGrid để WinRT, và nó đã làm đủ tốt trong các thử nghiệm đơn giản (nhiều cấu hình và lên đến 10000 hàng). Tuy nhiên, như tôi đã cố gắng nhúng nó vào một ứng dụng WinRT khác, tôi đã chạy vào một ngoại lệ lẻ tẻ (kiểu System.Exception, được nêu ra trong trình xử lý App.UnhandledException) đang chứng minh rất khó gỡ lỗi.

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

Lỗi này được lặp lại liên tục, nhưng không xác định. Đó là, tôi có thể làm cho nó xảy ra mỗi khi tôi chạy ứng dụng, nhưng nó không phải luôn luôn xảy ra bằng cách thực hiện cùng một tập hợp chính xác các bước cùng một số lần. Lỗi dường như xảy ra trên các chuyển tiếp trang (cho dù điều hướng đến một trang mới chuyển tiếp hoặc quay lại trang trước), và không (ví dụ) khi thay đổi ItemsSource của datagrid.

Cấu trúc ứng dụng về cơ bản là truy cập đệ quy thông qua một hệ thống phân cấp, với một trang được hiển thị ở mỗi cấp phân cấp. Trên trang cho nút hiện tại trong cấu trúc phân cấp, mỗi nút con và một số nút cháu được hiển thị và cho bất kỳ nút con nào, một mạng dữ liệu có thể được hiển thị. Trong thực tế, tôi liên tục tái sản xuất này với cấu trúc điều hướng sau:

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 

Một kịch bản thử nghiệm điển hình là, bắt đầu từ gốc, di chuyển đến trẻ em, chuyển sang Cháu, di chuyển trở lại để trẻ em, và sau đó khi tôi cố gắng để di chuyển với Grandchild một lần nữa, nó không thành công với ngoại lệ mà tôi đã đề cập ở trên. Nhưng nó có thể thất bại trong lần đầu tiên tôi đánh Grandchild, hoặc nó có thể cho phép tôi di chuyển qua lại một vài lần trước khi thất bại.

Ngăn xếp cuộc gọi chỉ có một khung được quản lý trên đó, đó là trình xử lý sự kiện ngoại lệ chưa xử lý. Điều này rất vô ích. Chuyển sang chế độ gỡ lỗi hỗn hợp, tôi nhận được như sau:

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  

Điều này cho thấy với tôi rằng bất cứ điều gì tôi đang làm sai không đăng ký cho đến sau khi ít nhất một chu kỳ trong vòng lặp thông điệp của ứng dụng (và tôi cũng đã cố gắng phá vỡ trên tất cả các ngoại lệ được ném bằng cách sử dụng "Gỡ lỗi | Ngoại lệ ..." - theo như tôi có thể biết, không có gì bị ném và nuốt phải). Các khung xếp chồng thú vị tôi thấy là WindowProc, OnReentrancyProtectedWindowMessageTick. msg là 0x402 (1026), điều này không có ý nghĩa gì đối với tôi. This page danh sách rằng thông điệp được sử dụng trong bối cảnh sau đây:

CBEM_SETIMAGELIST 
DDM_CLOSE 
DM_REPOSITION 
HKM_GETHOTKEY 
PBM_SETPOS 
RB_DELETEBAND 
SB_GETTEXTA 
TB_CHECKBUTTON 
TBM_GETRANGEMAX 
WM_PSD_MINMARGINRECT 

... nhưng điều đó không có ý nghĩa gì nhiều với tôi, hoặc là (nó có thể thậm chí không có liên quan).

Ba lý thuyết tôi có thể đưa ra là những: áp

  1. Memory. Nhưng tôi đã chạy vào điều này với 24% bộ nhớ vật lý của tôi miễn phí và các ứng dụng tiêu thụ ít hơn 100MB bộ nhớ. Lần khác, ứng dụng sẽ không gặp phải bất kỳ sự cố nào khi điều hướng trong một khoảng thời gian và tăng 400MB bộ nhớ
  2. Sự cố về luồng, chẳng hạn như truy cập vào chuỗi giao diện người dùng từ chuỗi công nhân. Và, trên thực tế, tôi có quyền truy cập dữ liệu xảy ra trên một chuỗi nền. Nhưng điều này là sử dụng một khuôn khổ (ported) đã được rất đáng tin cậy trong một môi trường WinForms và trong một plugin Outlook, và tôi nghĩ rằng việc sử dụng thread là an toàn. Ngoài ra, tôi có thể sử dụng cùng một dữ liệu trong ứng dụng này mà không có bất kỳ vấn đề nào ràng buộc chỉ với ListView và vv. Cuối cùng, nút Grandchild được cấu hình sao cho chọn một hàng trong datagrid đầu tiên khởi tạo một yêu cầu cho các mục chi tiết của hàng, được hiển thị trong datagrid thứ hai (ban đầu là rỗng, và có thể giữ nguyên như vậy mà không ngăn cản ngoại lệ). Điều này xảy ra mà không cần quá trình chuyển đổi trang và hoạt động hoàn hảo cho đến khi nào tôi chọn để chọn lựa. Nhưng điều hướng trở lại Child có thể giết tôi ngay lập tức, mặc dù không nên truy cập dữ liệu tại thời điểm đó và do đó không phải là hoạt động luồng mà tôi biết.
  3. cạn kiệt tài nguyên loại nào đó, có thể là xử lý GUI. Nhưng tôi không nghĩ rằng tôi đang gây áp lực lớn cho hệ thống này. Trong một thực thi, phá vỡ xử lý ngoại lệ, Task Manager báo cáo quá trình sử dụng 662 xử lý, 21 đối tượng người dùng và 12 đối tượng GDI, so với Tweetro đang sử dụng 734, 37 và 19 tương ứng mà không gặp sự cố. Tôi có thể thiếu gì khác trong thể loại này?

Tôi có nhiều dung lượng đĩa trống và không sử dụng đĩa cho bất kỳ tệp nào khác ngoài tệp cấu hình (và tất cả đều hoạt động tốt trước khi thêm thẻ dữ liệu).

Suy nghĩ tiếp theo của tôi là cố gắng bước qua một số phần 'thú vị' tiềm năng của mã datagrid và nhảy qua bất kỳ phần nào có vấn đề. Tôi đã thử với ArrangeOverride của datagrid, nhưng ngoại lệ dường như không quan tâm liệu tôi có làm điều đó hay không. Ngoài ra, tôi không chắc đây là một chiến lược hữu ích. Vì ngoại lệ không được ném cho đến sau một chu kỳ trên vòng lặp tin nhắn, và vì tôi không thể biết chắc chắn khi nào nó sắp xảy ra, tôi sẽ cần phải bao gồm một số lượng lớn hoán vị, chạy mỗi hoán vị rất nhiều lần, để cô lập mã vấn đề.

Lỗi được đưa ra trong cả chế độ Gỡ lỗi và Phát hành. Và, như là một lưu ý nền tảng cuối cùng, lượng dữ liệu mà chúng ta đang xử lý ở đây là nhỏ, nhỏ hơn nhiều so với dòng chạy dữ liệu 10000 hàng của tôi trong sự cô lập. Nó có thể là theo thứ tự của 50-100 hàng, có lẽ 30-40 cột. Và trước khi ngoại lệ được ném, dữ liệu và các lưới dường như hoạt động và phản hồi tốt.

Vì vậy, đó là lý do tôi đến với bạn. Hai câu hỏi của tôi là:

  1. Thông tin lỗi có cung cấp cho bạn bất kỳ gợi ý nào về vấn đề gì không?
  2. Bạn sẽ sử dụng chiến lược gỡ lỗi nào để tách biệt mã sự cố?

Rất cám ơn trước vì bất kỳ trợ giúp nào bạn có thể cung cấp!

Trả lời

42

OK, với một số critical input from Tim Heuer [MSFT], tôi đã tìm hiểu điều gì đang diễn ra và cách giải quyết vấn đề này.

Đáng ngạc nhiên, không có ba dự đoán ban đầu nào của tôi là chính xác. Đây không phải là về bộ nhớ, luồng hoặc tài nguyên hệ thống. Thay vào đó, đó là về những hạn chế trong hệ thống nhắn tin Windows. Dường như nó hơi giống như một ngoại lệ tràn ngăn xếp, trong đó khi bạn thực hiện quá nhiều thay đổi đối với cây trực quan cùng một lúc, hàng đợi cập nhật không đồng bộ dài đến nỗi nó đi một dây và ngoại lệ bị ném. Trong trường hợp này, vấn đề là có đủ UIElements đi vào lưới dữ liệu Tôi đang làm việc với điều đó cho phép lưới điện để tạo ra tất cả các cột riêng của mình cùng một lúc có thể trong một số trường hợp vượt quá giới hạn. Tôi đã sử dụng một số lưới cùng một lúc, và tất cả tải để đáp ứng với sự kiện điều hướng trang, mà làm cho nó tất cả các trickier để móng tay xuống.

Rất may, những giới hạn mà tôi đã gặp phải KHÔNG phải là giới hạn trong cây thị giác hoặc hệ thống con giao diện người dùng XAML, chỉ trong tin nhắn được sử dụng để cập nhật nó. Điều này có nghĩa rằng nếu tôi có thể trải ra các hoạt động tương tự trên nhiều dấu tích của đồng hồ của người điều phối, tôi có thể thực hiện cùng một kết quả cuối cùng.

Điều tôi đã làm là hướng dẫn lưới dữ liệu của mình không tự động tạo các cột riêng. Thay vào đó, tôi nhúng lưới vào điều khiển người dùng, khi dữ liệu được tải, sẽ phân tích các cột cần thiết và tải chúng vào một danh sách. Sau đó, tôi được gọi là phương pháp sau đây:

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 là một loại tầm thường dùng để chứa các cấu hình phân tích cú pháp-out hoặc một cấu hình được nạp từ một tập tin.)

Phương pháp này được gọi với các đối số sau, tương ứng: danh sách các cột, 0, và dự đoán tùy ý của tôi là 5 số lượng cột khá an toàn để tải cùng một lúc; nhưng con số này được dựa trên thử nghiệm và kỳ vọng rằng một số lượng lớn các lưới có thể được tải đồng thời. Tôi hỏi Tim để biết thêm thông tin có thể thông báo cho phần này của quy trình và sẽ báo cáo lại ở đây nếu tôi tìm hiểu thêm về cách xác định mức độ an toàn. Trong thực tế, điều này dường như hoạt động đầy đủ, mặc dù kết quả là kiểu hiển thị lũy tiến bạn mong đợi, với các cột hiển thị rõ ràng. Tôi hy vọng điều này có thể được cải thiện cả bằng cách sử dụng giá trị tối đa có thể cho numToLoad và bằng giao diện người dùng khác. Tôi có thể điều tra ẩn lưới trong khi các cột được tạo ra và chỉ hiển thị kết quả khi mọi thứ đã sẵn sàng. Cuối cùng quyết định sẽ đi xuống mà cảm thấy nhiều hơn 'nhanh chóng và chất lỏng'.

Một lần nữa, tôi sẽ cập nhật câu trả lời này với nhiều thông tin hơn nếu tôi nhận được, nhưng tôi hy vọng điều này sẽ giúp một người nào đó gặp phải các sự cố tương tự trong tương lai. Sau khi rót thêm thời gian hơn là tôi muốn thú nhận vào cuộc săn lùng lỗi, tôi không muốn bất cứ ai khác phải tự giết mình trong chuyện này.

-1

Dường như sự cố này được khắc phục trong Xem trước Windows 8.1, sau khi nhắm mục tiêu lại ứng dụng của tôi cho Windows 8.1. Tôi không còn có thể tạo lại vấn đề này bằng cách bán hàng nghìn hình ảnh lên màn hình.

+1

Dường như nó cũng không hoạt động trên máy Win8.1 của tôi. – digitalMoto

+0

@digitalMoto Bạn có nghĩa là bạn thấy lỗi này trong 8.1, hoặc bạn không thể sao chép vấn đề này trong 8.1? –

+2

Điều này có vẻ khó xảy ra. Tài liệu PostMessage tại đây: https://msdn.microsoft.com/en-us/library/ms644944.aspx?f=255&MSPPError=-2147217396 --- nói rằng "Có giới hạn 10.000 tin nhắn được đăng trên mỗi hàng đợi tin nhắn". và không có gì nói rằng có một giới hạn khác trong Windows 8.1 –

Các vấn đề liên quan