Прикрепить StackTrace к исключению без метаданных в С#/.NET
У меня есть компонент, который обрабатывает ошибки с использованием возвращаемых значений в отличие от стандартной обработки исключений. В дополнение к коду ошибки он также возвращает трассировку стека, где произошла ошибка. Обертка, которую я использую для вызова компонента, будет интерпретировать коды возврата и выдавать исключение.
Я хотел бы, чтобы оболочка выдавала исключение, которое включает в себя информацию о трассировке захваченного стека из компонента. Я бы хотел, чтобы это выглядело так, как если бы исключение было выброшено с исходного сайта ошибки, даже если оно было выброшено в другое место. Более конкретно, идентификатор, такой как трассировка стека, отображаемая визуальным студийным тестировщиком, чтобы отобразить правильное расположение
Есть ли способ сделать это? Было бы неплохо, если бы я мог избежать трюков с отражением на нижнем уровне, обращающихся к закрытым членам, но я возьму то, что могу получить.
РЕДАКТИРОВАТЬ 1:
Я не заинтересован в том, как захватить трассировку стека, я заинтересован в прикреплении трассировки стека, которая уже была захвачена, в Exception
ИЗМЕНИТЬ 2:
Я попытался переопределить свойство StackTrace, но визуальная студия извлекает данные трассировки стека из другого места и, кажется, полностью игнорирует переопределенное свойство.
CustomException GenerateExcpetion()
{
return new CustomException();
}
void ThrowException(Exception ex)
{
Trace.WriteLine("Displaying Exception");
Trace.WriteLine(ex.ToString());
var edi = ExceptionDispatchInfo.Capture(ex);
edi.Throw();
}
[TestMethod]
public void Test006()
{
var ex = GenerateExcpetion();
ThrowException(ex);
}
public class CustomException : Exception
{
string _stackTrace;
public CustomException()
{
_stackTrace = Environment.StackTrace;
}
public override string StackTrace
{
get
{
return base.StackTrace;
}
}
}
Метод Excpetion.ToString() извлекает данные трассировки стека из частного свойства, и поэтому трассировка стека, исходящая из переопределения, не отображается.
CustomException: выбрано исключение типа "CustomException".
ExceptionDispatchInfo ищет данные трассировки стека из частного свойства, и поэтому он не может найти ни одну из этих данных, и когда вы бросаете это настраиваемое исключение, к исключению добавляется новая трассировка стека с местоположением, которое его отбрасывают. Если вы используете throw напрямую, информация о закрытом стеке устанавливается в место, где произошел выброс.
Ответы
Ответ 1
Хорошо, что нет ничего элегантного, вот мой подход, основанный на размышлениях.
public static class ExceptionUtilities
{
private static readonly FieldInfo STACK_TRACE_STRING_FI = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly Type TRACE_FORMAT_TI = Type.GetType("System.Diagnostics.StackTrace").GetNestedType("TraceFormat", BindingFlags.NonPublic);
private static readonly MethodInfo TRACE_TO_STRING_MI = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { TRACE_FORMAT_TI }, null);
public static Exception SetStackTrace(this Exception target, StackTrace stack)
{
var getStackTraceString = TRACE_TO_STRING_MI.Invoke(stack, new object[] { Enum.GetValues(TRACE_FORMAT_TI).GetValue(0) });
STACK_TRACE_STRING_FI.SetValue(target, getStackTraceString);
return target;
}
}
Написание форматированной строки StackTrace в свойство _stackTraceString кажется достаточным для того, чтобы обмануть визуальный студийный тестовый бегун и методы Exception.ToString(), полагая, что стек был создан броском (фактически ничего не бросая).
См. ниже для использования:
StackTrace GetDeeperStackTrace(int depth)
{
if (depth > 0)
{
return GetDeeperStackTrace(depth - 1);
}
else
{
return new StackTrace(0, true);
}
}
[TestMethod]
public void Test007()
{
Exception needStackTrace = new Exception("Some exception");
var st = GetDeeperStackTrace(3);
needStackTrace.SetStackTrace(st);
Trace.Write(needStackTrace.ToString());
throw new Exception("Nested has custom stack trace", needStackTrace);
}
Ответ 2
Просто создайте свой собственный тип Exception
и переопределите свойство StackTrace
:
class MyException : Exception
{
private string oldStackTrace;
public MyException(string message, string stackTrace) : base(message)
{
this.oldStackTrace = stackTrace;
}
public override string StackTrace
{
get
{
return this.oldStackTrace;
}
}
}
Ответ 3
Вы можете использовать: Environment.StackTrace для захвата stacktrace при возникновении ошибки в компоненте, а затем вернуть его вместе с другими сведениями об ошибках или повторной попыткой.
Вы можете вручную создавать стековые кадры для создания полной трассы: StackFrame
MSDN: StackTrace