Я не делаю большого программирования Windows GUI, таким образом, это может все быть общеизвестным людям, более знакомым с WinForms, чем я. К сожалению, я не смог найти, что любые ресурсы объясняют проблему, я встретился сегодня во время отладки.
Если мы называем EndInvoke на асинхронном делегате. Мы получим любое исключение, выданное во время осуществления повторно брошенного метода. Стек вызовов отразит первоисточник исключения.
Однако, если мы делаем что-то подобное в Windows. Формы. Управление, реализация Управления. EndInvoke сбрасывает стек вызовов. Это может наблюдаться простым тестом или путем рассмотрения кода в Отражателе. Выборка соответствующих норм от EndInvoke здесь:
if (entry.exception != null)
{
throw entry.exception;
}
Я понимаю, что Begin/EndInvoke на Управлении и асинхронных делегатах отличаются, но я ожидал бы подобное поведение на Управлении. EndInvoke.
Есть ли какая-либо причина, Управление не делает то, что это - асинхронные делегаты, делают для сохранения стопки первоначального вызова?
Обратите также внимание, что Control.EndInvoke
является одним из немногих управляемых EndInvokes
в Framework (поэтому вы можете увидеть код в Reflector). Возможно, им следовало бы иметь неуправляемый помощник, который бросает с исходным стеком на месте.
На самом деле, я думаю, что это единственный управляемый EndInvoke
, но есть и другие управляемые End*
процедуры с параметром IAsyncResult
. Я не проверял их все, но, похоже, все те, которые я просмотрел, просто бросают исключение или эффективно используют решение Стивена Клири о переходе к использованию GetWaiter.GetResult
из .NET 4, в котором есть некоторые управляемые и неуправляемые махинации, чтобы попытаться восстановить стек для исключений.
Я не знаю настоящей причины, но могу предположить, что асинхронные делегаты сродни RPC, а управляющие делегаты могут основываться на отправке сообщений Win32. Различные технологии, поэтому влияние этой функции может быть разным. Асинхронный делегат получит выгоду от всего кода удаленного взаимодействия, для которого разработчик написал бы код для передачи стека вызовов исключений между разными процессами или компьютерами, в то время как делегаты управления будут имитировать RPC с PostMessage в рамках одного и того же процесса. Другая команда, другой код.
Я не прочитал 100% вашего сообщения, поэтому не уверен, помогает ли это, или я просто говорю очевидные вещи, {{1} } но когда исключение перехвачено и вы пишете
"throw iAmAnCaughtExceptionInstance;"
стек вызовов не будет сохранен, вы должны просто написать
"throw;"
, а затем стек вызовов будет сохранен
Я не уверен, почему Control не делает этого (возможно, это просто недосмотр), но вы можете обойти это в .NET 4.0, запланировав Task для формы пользовательского интерфейса:
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.");
}