13

Hãy xem xét sau đây để được chiết xuất từ ​​một Windows 8 Metro/WinRT ứng dụng, đã được giảm đến mức tối thiểu cần thiết để hiển thị sự bất thường:Unhandled ngoại lệ xử lý không được gọi cho Metro/WinRT UI async xử lý sự kiện khoảng trống

public class App : Application 
{ 
    public App() 
    { 
     UnhandledException += (sender, e) => e.Handled = true; 
    } 
} 

public class MainPage : Page 
{ 
    private void Button_Click_1(object sender, RoutedEventArgs e) 
    { 
     throw new NotSupportedException(); 
    } 

    private async void Button_Click_2(object sender, RoutedEventArgs e) 
    { 
     throw new NotSupportedException(); 
    } 
} 

Vì vậy, được cung cấp một giao diện người dùng Metro với hai nút và trình xử lý sự kiện nhấp chuột của họ, sự khác biệt duy nhất là trình xử lý sự kiện thứ hai được đánh dấu là async.

Sau đó nhấp vào mỗi nút, tôi mong đợi trình xử lý UnhandledException sẽ được gọi trong cả hai trường hợp vì chúng (cả hai) được nhập thông qua chuỗi giao diện người dùng và ngữ cảnh đồng bộ hóa liên quan. Sự hiểu biết của tôi là, đối với phương thức async void, bất kỳ trường hợp ngoại lệ nào cũng phải được chụp và 'bị thu hồi' (giữ nguyên ngăn xếp ban đầu) qua ngữ cảnh đồng bộ hóa ban đầu, cũng được nêu rõ trong Async/Await FAQ.

Nhưng trình xử lý UnhandledException là không phải được gọi trong trường hợp async, do đó, ứng dụng gặp sự cố! Vì điều này thách thức những gì tôi xem xét một mô hình khác rất trực quan, tôi cần phải biết tại sao! Có, tôi biết tôi có thể quấn cơ thể của người xử lý trong một try { } catch { }, nhưng câu hỏi của tôi là tại sao không phải là xử lý UnhandledException backstop được gọi là?

Tiếp tục nhấn mạnh tại sao điều này không có ý nghĩa, hãy xem xét các chiết xuất thực tế giống hệt nhau sau đây từ một ứng dụng WPF cũng sử dụng async/chờ đợi và nhắm mục tiêu .NET Framework 4.5:

public class App : Application 
{ 
    public App() 
    { 
     DispatcherUnhandledException += (sender, e) => e.Handled = true; 
    } 
} 

public class MainWindow : Window 
{ 
    private void Button_Click_1(object sender, RoutedEventArgs e) 
    { 
     throw new NotSupportedException(); 
    } 

    private async void Button_Click_2(object sender, RoutedEventArgs e) 
    { 
     throw new NotSupportedException(); 
    } 
} 

[Có một sự khác biệt tinh tế WPF có cả trình xử lý sự kiện Application DispatcherUnhandledException cũng như trình xử lý sự kiện AppDomain UnhandledException, nhưng bạn chỉ có thể đánh dấu ngoại lệ là 'được xử lý' trong DispatcherUnhandledException, điều này phù hợp với trình xử lý sự kiện UnhandledException của Metro/WinRT ở trên.]

Sau đó nhấp vào từng mông bật, trình xử lý DispatcherUnhandledException thực sự được gọi là trong cả hai trường hợp, như mong đợi và ứng dụng không không phải là lỗi.

+0

Bạn đã bao giờ tìm thấy cách khắc phục hay giải pháp cho điều này? Tôi đang gặp chính xác cùng một vấn đề - loại một vấn đề tê liệt với UnhandledException cho lỗi đăng nhập. –

+0

Đừng để ngoại lệ thoát khỏi trình xử lý sự kiện 'async void'; tức là quấn cơ thể trong một khối 'try {} catch {}', hoặc truyền cơ thể như một lambda đến một hàm trợ giúp - kết thúc tốt đẹp lambda để tránh lặp lại chính bạn. –

+0

Không phải là vấn đề với sự kiện Application.UnhandledException của Win 8.1 – foson

Trả lời

5

Đã trả lời ở đây: No UnhandledException fired from async event callback

Đây là giới hạn được biết đến của WinRT. Hy vọng rằng, nó sẽ được sửa trong bản cập nhật tiếp theo.

+0

Cảm ơn bạn đã liên kết Pierre. Thật không may nó không có những gì tôi sẽ gọi một câu trả lời. Tôi cũng tìm thấy [Application.UnhandledException không thể nắm bắt được sự kiện] (http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/f434ddc7-351f-43c5-9535-0eab2048042a/), vì vậy tôi 'không đơn độc trong việc thẩm vấn hành vi này. Một con chim nhỏ nói với tôi rằng đây là một vấn đề đã được biết đến mà không được khắc phục đúng lúc để được thả. Tôi thực sự sẽ đánh giá cao nếu một người nào đó ở bên trong tại Microsoft sẽ trả lời/bình luận. –

+0

"đây là sự cố đã biết chưa được khắc phục kịp thời để phát hành". Đó là câu trả lời đúng không? Bạn muốn biết thêm điều gì nữa? –

+0

Tài khoản gián tiếp từ người biết ai đó, v.v. chỉ là tin đồn chứ không phải là câu trả lời. "Tôi thực sự sẽ đánh giá cao nếu ai đó ở bên trong Microsoft sẽ trả lời/bình luận." –

-2

Hãy thử UnobservedTaskException trường hợp TaskScheduler

+1

'async void' không sử dụng tác vụ. Nó sử dụng bối cảnh đồng bộ hóa. Xem Câu hỏi thường gặp về Async/Await. –

3

Như đã giải thích trong tài liệu (nguồn: http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.application.unhandledexception.aspx):

Điều quan trọng là phải nhận thức được một số hạn chế của sự kiện Application.UnhandledException. Sự kiện này chỉ được sử dụng với ngoại lệ mà khung XAML gặp phải. Các ngoại lệ gặp phải bởi các thành phần Windows Runtime khác hoặc các phần của ứng dụng mà không được kết nối với khung XAML sẽ không dẫn đến sự kiện này được nâng lên.

Ví dụ: nếu thành phần Windows khác gọi vào mã ứng dụng và ngoại lệ được ném và không bị bắt, thì sự kiện UnhandledException sẽ không được nâng lên.Nếu ứng dụng tạo ra một chuỗi công nhân, và sau đó đặt ra một ngoại lệ về chuỗi công nhân, sự kiện UnhandledException sẽ không được nâng lên.

Như đã chỉ ra trong this conversation, cách duy nhất để lấy ngoại lệ xảy ra trong một sợi công nhân là để bọc chúng trong một khối try/catch. Do đó, đây là cách giải quyết Tôi đang sử dụng trong ứng dụng của tôi: thay vì sử dụng Task.Run hoặc tương đương để thực thi mã trên một sợi công nhân từ UI, tôi đang sử dụng phương pháp này:

/// <summary> 
/// Runs code in a worker thread and retrieves a related exception if any. 
/// </summary> 
/// <param name="target">The target.</param> 
/// <param name="action">The action.</param> 
public static void SafeRun(this DependencyObject target, Action action) 
{ 
    Task task = ThreadPool.RunAsync(o => 
    { 
     try 
     { 
      action(); 
     } 
     catch (Exception ex) 
     { 
      /* Call the same error logging logic as in the UnhandledException event handler here. */ 
     } 
    }).AsTask(); 
    task.Wait(); 
} 

Điểm mấu chốt là để đăng nhập tất cả các lỗi trong ứng dụng của bạn, bạn nên:

  1. Subscribre đến UnhandledException kiện để có được XAML đống lỗi liên quan,
  2. hành động Bọc xảy ra trong một đề người lao động trong một khối try/catch.
+0

Tôi không tạo bất kỳ chủ đề nào. Trình xử lý sự kiện 'async void' của ví dụ chạy trên luồng UI và ngữ cảnh đồng bộ hóa liên quan. Có * giải pháp thay thế * là không cho phép thoát ngoại lệ, như đã nêu. –

0

Từ quan điểm của tôi là answere chỉ đúng được downvoted đây (Hãy thử UnobservedTaskException trường hợp TaskScheduler)

vấn đề là bạn đang sử dụng 'async khoảng trống', nơi các trường hợp ngoại lệ không thể được xử lý. Đây không phải là một hạn chế của WinRT mà là hành vi thiết kế của async. Bạn cần phải hiểu async sâu để thực hiện đúng cách xử lý ngoại lệ tại đây. Xem bài viết này: http://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Trường hợp ngoại lệ là whe được thu hồi lại GC thu thập các tác vụ như không được giám sát. Bạn có thể lấy chúng bằng cách đăng ký sự kiện TaskScheduler UnobservedTaskException. Lưu ý - phải mất một thời gian cho đến khi ngoại lệ đến đó, bởi vì nó được ghi chú với bộ thu gom rác.

Nói chung không sử dụng 'async khoảng trống', nhưng trong xử lý sự kiện UI bạn phải, vì vậy đây là cách duy nhất ngươi ..

+0

Bạn đã đọc bài viết bạn đề cập chưa? Nó nói giống như bình luận của tôi vào ngày 23 tháng 10 năm 12: "Với các phương thức void không đồng bộ, ** không có đối tượng Task **, vì vậy bất kỳ ngoại lệ nào được ném ra khỏi một phương thức void async sẽ được ** nâng lên trực tiếp trên SynchronizationContext * * đã hoạt động khi phương thức void async bắt đầu. " –

+0

Rất tiếc, bạn đã đúng! Tôi đã thử nghiệm nó một lần nữa và WinRT bị treo ngay lập tức. Caliburn chấp nhận xử lý sự kiện ** async Task ** và thực hiện ghi nhật ký ngoại lệ, vì vậy điều này sẽ giúp cho tất cả các sự kiện Click etc. Thay vì xử lý ** async void ** chúng ta sử dụng loại FireAndForget-Trigger để khởi động các phương thức (cả đồng bộ và async) trong nền thông qua Task.Run và xử lý việc xử lý ngoại lệ. –

+1

bây giờ chúng ta có thể hy vọng cho một tương lai tốt hơn: http://msdn.microsoft.com/en-us/library/windows/apps/dn263110.aspx - xem phần ** "Bề mặt tất cả các lỗi" tính năng ** - có vẻ như như họ có kế hoạch xử lý các lỗi tốt hơn. –