Control.EndInvoke сбрасывает стек вызовов для исключения - PullRequest
7 голосов
/ 12 апреля 2010

Я не много занимаюсь программированием Windows GUI, так что все это может быть общеизвестно для людей, более знакомых с WinForms, чем я. К сожалению, я не смог найти никаких ресурсов, чтобы объяснить проблему, с которой я столкнулся сегодня во время отладки.

Если мы вызываем EndInvoke для асинхронного делегата. Мы получим любое исключение во время выполнения метода повторно. Стек вызовов будет отражать исходный источник исключения.

Однако, если мы делаем нечто подобное в Windows.Forms.Control, реализация Control.EndInvoke сбрасывает стек вызовов. Это можно наблюдать с помощью простого теста или путем просмотра кода в Reflector. Соответствующая выдержка из кода EndInvoke находится здесь:

if (entry.exception != null)
{
   throw entry.exception;
}

Я понимаю, что Begin / EndInvoke на Control и асинхронные делегаты различаются, но я ожидал бы похожее поведение на Control.EndInvoke.

Есть ли какая-либо причина, по которой Control не делает то, что делают асинхронные делегаты, чтобы сохранить исходный стек вызовов?

Ответы [ 4 ]

1 голос
/ 14 апреля 2010

Обратите также внимание, что Control.EndInvoke является одним из немногих управляемых EndInvokes в платформе (поэтому вы можете увидеть код в Reflector). У них, вероятно, должен быть неуправляемый помощник, который бросает с исходным стеком.

На самом деле, я думаю, что это only managed EndInvoke, но есть и другие управляемые подпрограммы End* с параметром IAsyncResult. Я не проверил все из них, но кажется, что все те, которые я рассмотрел, просто выдают исключение или эффективно используют решение Стивена Клири о переадресации для использования .NET 4 GetWaiter.GetResult, в котором есть некоторые управляемые и неуправляемые махинации, чтобы попробовать чтобы восстановить стек для исключений.

1 голос
/ 12 мая 2010

Я не уверен, почему Control не делает этого (возможно, просто недосмотр), но вы можете обойти это в .NET 4.0, запланировав задачу в форме пользовательского интерфейса:

    private BackgroundWorker bgw;
    private TaskFactory uiTaskFactory;

    private void Form1_Load(object sender, EventArgs e)
    {
        this.uiTaskFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
        this.bgw = new BackgroundWorker();
        this.bgw.DoWork += bgw_DoWork;
        this.bgw.RunWorkerAsync();
    }

    void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        var task = this.uiTaskFactory.StartNew(this.OuterTaskFunction);
        try
        {
            task.Wait();
        }
        catch (Exception ex)
        {
            // Note: Full stack trace preserved
            MessageBox.Show(ex.InnerException.ToString());
        }
    }

    void OuterTaskFunction()
    {
        this.InnerTaskFunction();
    }

    void InnerTaskFunction()
    {
        throw new InvalidOperationException("Blah.");
    }
1 голос
/ 12 апреля 2010

Я не знаю реальной причины, но могу предположить, что асинхронные делегаты похожи на RPC, тогда как управляющие делегаты могут основываться на отправке сообщений Win32. Различные технологии, поэтому влияние этой функции не может быть одинаковым. Асинхронный делегат выиграл бы от всего кода удаленного взаимодействия, для которого разработчик написал бы код для передачи стека вызовов исключений между различными процессами или компьютерами, в то время как управляющие делегаты будут имитировать RPC с PostMessage в одном и том же процессе. Другая команда, другой код.

0 голосов
/ 14 мая 2010

Я не прочитал 100% вашего сообщения, поэтому я не уверен, поможет ли это, или я просто говорю очевидные вещи, но когда ловится исключение и вы пишете

"throw iAmAnCaughtExceptionInstance;"

стек вызовов не будет сохранен, вы должны просто написать

"бросить";

и затем стек вызовов сохраняется

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...