Visual Studio не показывает исходное расположение переброшенного внутреннего исключения в C # - PullRequest
2 голосов
/ 09 декабря 2011

Итак, я следовал рекомендованному ответу, показанному на Как перебрасывать внутреннее исключение TargetInvocationException без потери трассировки стека и в итоге получил код, который выглядит следующим образом:

// Inside DpmEntrypoint static class.
public static object Invoke(Delegate d, object[] args)
{
    try
    {
        // Invoke directly if not networked.
        if (LocalNode.Singleton == null)
            return d.DynamicInvoke(args);

        // Get the network name of the object and the name of the method.
        string objectName = (d.Target as ITransparent).NetworkName;
        string methodName = d.Method.Name;

        // Get our local node and invoke the method.
        return LocalNode.Singleton.Invoke(objectName, methodName, args);
   }
   catch (Exception ex)
   {
        ex.Rethrow();
        return null;
    }
}

// Inside ExceptionExtensions static class.
public static void Rethrow(this Exception ex)
{
    if (ex is TargetInvocationException)
        ex.InnerException.Rethrow();
    else
    {
        typeof(Exception).GetMethod("PrepForRemoting",
            BindingFlags.NonPublic | BindingFlags.Instance)
            .Invoke(ex, new object[0]);
        throw ex;
    }
}

В этом случае постпроцессор запускается над сборками после времени компиляции, а указанные методы помещаются в делегат и вызываются с помощью метода, описанного выше. Чтобы затем вызвать делегата, я должен использовать Invoke где-нибудь вдоль линии (даже если это в отражении, используемом внутри Singleton.Invoke).

Приведенный выше метод Ретроу правильно сохраняет трассировку стека следующим образом:

Трассировка стека серверов: at Example.ExampleController.NullTest () в C: \ Серверное хранилище \ Проекты \ Redpoint \ Pivot \ Example \ ExampleController.cs: строка 19 at Example.ExampleWorld.t_Spawned (Отправитель объекта, EventArgs e) в C: \ Серверное хранилище \ Проекты \ Redpoint \ Pivot \ Example \ ExampleWorld.cs: строка 29 в Pivot.Core.Actor.OnSpawned__Distributed0 () в C: \ Серверное хранилище \ Проекты \ Redpoint \ Pivot \ Pivot.Core \ Actor.cs: строка 62

Исключение переброшено в [0]: в Process4.Providers.ExceptionExtensions.Rethrow (Exception ex) в Process4.Providers.ExceptionExtensions.Rethrow (Exception ex) в Process4.Providers.DpmEntrypoint.Invoke (делегат d, объект [] args) в Pivot.Core.Actor.OnSpawned () в Pivot.Core.GameInfo.set_World__Distributed0 (значение WorldInfo) в C: \ Хранение на сервере \ Проекты \ Redpoint \ Pivot \ Pivot.Core \ GameInfo.cs: строка 35

Исключение переброшено в 1 : в Process4.Providers.ExceptionExtensions.Rethrow (Exception ex) в Process4.Providers.ExceptionExtensions.Rethrow (Exception ex) в Process4.Providers.DpmEntrypoint.SetProperty (Делегат d, Object [] args) в Pivot.Core.GameInfo.set_World (значение WorldInfo) at Example.ExampleGame.t_Spawned (Отправитель объекта, EventArgs e) в C: \ Серверное хранилище \ Проекты \ Redpoint \ Pivot \ Example \ ExampleGame.cs: строка 25 в Pivot.Core.Actor.OnSpawned__Distributed0 () в C: \ Серверное хранилище \ Проекты \ Redpoint \ Pivot \ Pivot.Core \ Actor.cs: строка 62

Исключение переброшено в [2]: в Process4.Providers.ExceptionExtensions.Rethrow (Exception ex) в Process4.Providers.ExceptionExtensions.Rethrow (Exception ex) в Process4.Providers.DpmEntrypoint.Invoke (делегат d, объект [] args) в Pivot.Core.Actor.OnSpawned () в Pivot.Engine.set_Game (значение GameInfo) в C: \ Хранение на сервере \ Проекты \ Redpoint \ Pivot \ Pivot.Core \ Engine.cs: строка 47 at Example.Program.Main (String [] args) в C: \ Серверное хранилище \ Проекты \ Redpoint \ Pivot \ Example \ Program.cs: строка 14 в System.AppDomain._nExecuteAssembly (сборка сборки, аргументы String []) в System.AppDomain.ExecuteAssembly (String assemblyFile, Evidence assemblySecurity, String [] args) в Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly () в System.Threading.ThreadHelper.ThreadStart_Context (состояние объекта) в System.Threading.ExecutionContext.Run (ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта) в System.Threading.ThreadHelper.ThreadStart ()

но у меня проблема в том, что даже когда библиотека скомпилирована в режиме выпуска, Visual Studio по-прежнему показывает источник исключения в Rethrow вместо NullTest (где выбрасывается исключение NullReferenceException).

Поскольку все методы в приложении будут подключены, создание исключения TargetInvocationException бесполезно для разработчика; их больше интересует, где произошло оригинальное исключение в их коде.

Без возможности перебрасывать внутреннее исключение в том виде, в каком оно есть, оно в основном делает всю систему исключений в .NET при использовании с библиотекой распределенной обработки без того, чтобы разработчик не знал, что происходит за кулисами (что является совершенно противоположной целью библиотеки).

Кто-нибудь знает способ заставить Visual Studio показать его в том месте, где он был изначально брошен?

РЕДАКТИРОВАТЬ:

Я думаю о решениипроблема с использованием C ++ / CLI, поскольку это дает мне доступ к некоторым специальным функциям, как описано в статье MSDN Транспортировка исключений между потоками .

Проблема в том, что эти функции не позволяют мне "транспортировать управляемые исключения"и, следовательно, rethrow_exception создает исключение SEHException, когда я пытаюсь использовать его для управляемого исключения.Если кто-нибудь знает способ обойти эту проблему, то ее можно решить с помощью C ++ / CLI (если бы только был способ получить точный указатель текущего исключения, тогда можно было бы использовать инструкцию rethrow IL!).

namespace Rethrow {
    [System::Runtime::CompilerServices::Extension]
    public ref class ExceptionExtensions abstract sealed
    {
    public: 
        [System::Runtime::CompilerServices::Extension]
        static void Rethrow(System::Exception^ s)
        {
            std::rethrow_exception(std::copy_exception<System::Exception^>(s));
            throw;
        }
    };
}

Ответы [ 3 ]

0 голосов
/ 09 декабря 2011

throw ex; будет разматывать стек .

catch (Exception ex)
{
    throw;
    return null;
}

достаточно.Но помните, что это синтаксис C #.

0 голосов
/ 15 декабря 2011

Мне удалось решить эту проблему, изменив постпроцессор так, чтобы он вызывал метод напрямую через соответствующий делегат (поскольку постпроцессор знает во время выполнения сигнатуры методов и может генерировать соответствующие совпадающие делегаты). При использовании Invoke () для делегата он не переносит исключения с TargetInvocationException; это происходит только при динамическом вызове метода.

К сожалению, это решение фактически не решает динамически вызываемые методы и передачу внутренних исключений; он просто использует преимущества постпроцессора, который уже существует в процессе сборки для этой конкретной библиотеки (то есть это решение не решает проблему для тех, кто строит библиотеку, которая все равно не выполняет постобработку).

0 голосов
/ 09 декабря 2011

Хммм ... Попробуйте заменить " throw ex " на эту конструкцию:

Exception HighelLevelException = new Exception("An inner error occured!", ex);
throw HighelLevelException;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...