Подтвердить что ты не робот

Прикрепить 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 напрямую, информация о закрытом стеке устанавливается в место, где произошел выброс.

4b9b3361

Ответ 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