Итак, я следовал рекомендованному ответу, показанному на Как перебрасывать внутреннее исключение 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;
}
};
}