You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现跨进程IPC异常序列化及基类型反序列化(含内部异常)

跨进程IPC异常传递实现方案

核心思路

  1. 异常类型适配:序列化时遍历异常的继承链,找到第一个存在于进程A、B共享程序集(如mscorlib、System.Private.CoreLib)的基类型,作为targetType写入序列化数据;反序列化时基于targetType构造异常实例。
  2. 递归处理内部异常:对InnerException执行相同的类型适配和序列化逻辑,保证异常链完整传递。
  3. 引用序列化支持:针对需要按引用传递的类型,通过序列化框架的内置配置或自定义逻辑保留引用关系。

Json.net 实现方案

1. 共享基类型获取工具方法

private static Type GetSharedBaseType(Exception ex)
{
    var currentType = ex.GetType();
    // 可根据实际运行环境扩展共享程序集列表
    var sharedAssemblies = new HashSet<string> { "mscorlib", "System.Runtime", "System.Private.CoreLib" };
    
    while (currentType != typeof(Exception))
    {
        if (sharedAssemblies.Contains(currentType.Assembly.GetName().Name))
        {
            return currentType;
        }
        currentType = currentType.BaseType;
    }
    return typeof(Exception);
}

2. 自定义异常转换器

public class CrossProcessExceptionConverter : JsonConverter<Exception>
{
    public override void WriteJson(JsonWriter writer, Exception value, JsonSerializer serializer)
    {
        var sharedType = GetSharedBaseType(value);
        var exceptionData = new JObject
        {
            ["targetType"] = sharedType.AssemblyQualifiedName,
            ["message"] = value.Message,
            ["stackTrace"] = value.StackTrace,
            ["source"] = value.Source
        };

        // 递归序列化内部异常
        if (value.InnerException != null)
        {
            exceptionData["innerException"] = JToken.FromObject(value.InnerException, serializer);
        }

        exceptionData.WriteTo(writer);
    }

    public override Exception ReadJson(JsonReader reader, Type objectType, Exception existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var exceptionData = JObject.Load(reader);
        var targetType = Type.GetType(exceptionData["targetType"].ToString()) ?? typeof(Exception);

        // 构造目标异常实例(默认使用带message参数的构造函数)
        var exception = (Exception)Activator.CreateInstance(targetType, exceptionData["message"].ToString());
        exception.Source = exceptionData["source"]?.ToString();
        exception.StackTrace = exceptionData["stackTrace"]?.ToString();

        // 递归反序列化内部异常
        if (exceptionData.TryGetValue("innerException", out var innerToken) && innerToken.Type != JTokenType.Null)
        {
            exception.InnerException = serializer.Deserialize<Exception>(innerToken.CreateReader());
        }

        return exception;
    }
}

3. 使用方式

// 序列化配置(启用引用保留)
var jsonSettings = new JsonSerializerSettings
{
    Converters = { new CrossProcessExceptionConverter() },
    PreserveReferencesHandling = PreserveReferencesHandling.Objects
};

// 进程B侧序列化异常
var exceptionJson = JsonConvert.SerializeObject(thrownException, jsonSettings);

// 进程A侧反序列化异常
var receivedException = JsonConvert.DeserializeObject<Exception>(exceptionJson, jsonSettings);

BinaryFormatter 实现方案

1. 自定义代理选择器与序列化代理

public class ExceptionSurrogateSelector : ISurrogateSelector
{
    private readonly ISurrogateSelector _nextSelector;

    public ExceptionSurrogateSelector(ISurrogateSelector nextSelector = null)
    {
        _nextSelector = nextSelector;
    }

    public void ChainSelector(ISurrogateSelector selector) => throw new NotImplementedException();
    public ISurrogateSelector GetNextSelector() => _nextSelector;

    public ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector)
    {
        if (typeof(Exception).IsAssignableFrom(type))
        {
            selector = this;
            return new ExceptionSerializationSurrogate();
        }

        selector = _nextSelector;
        return _nextSelector?.GetSurrogate(type, context, out selector);
    }
}

public class ExceptionSerializationSurrogate : ISerializationSurrogate
{
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        var ex = (Exception)obj;
        var sharedType = GetSharedBaseType(ex);
        
        info.AddValue("targetType", sharedType.AssemblyQualifiedName);
        info.AddValue("message", ex.Message);
        info.AddValue("stackTrace", ex.StackTrace);
        info.AddValue("source", ex.Source);
        info.AddValue("innerException", ex.InnerException, typeof(Exception));
    }

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        var targetTypeStr = info.GetString("targetType");
        var targetType = Type.GetType(targetTypeStr) ?? typeof(Exception);
        
        var exception = (Exception)Activator.CreateInstance(targetType, info.GetString("message"));
        exception.Source = info.GetString("source");
        exception.StackTrace = info.GetString("stackTrace");
        exception.InnerException = (Exception)info.GetValue("innerException", typeof(Exception));
        
        return exception;
    }

    private static Type GetSharedBaseType(Exception ex)
    {
        var currentType = ex.GetType();
        var sharedAssemblies = new HashSet<string> { "mscorlib", "System.Runtime", "System.Private.CoreLib" };
        
        while (currentType != typeof(Exception))
        {
            if (sharedAssemblies.Contains(currentType.Assembly.GetName().Name))
            {
                return currentType;
            }
            currentType = currentType.BaseType;
        }
        return typeof(Exception);
    }
}

2. 使用方式

// 初始化BinaryFormatter并绑定代理选择器
var formatter = new BinaryFormatter();
formatter.SurrogateSelector = new ExceptionSurrogateSelector();

// 进程B侧序列化异常
byte[] exceptionBytes;
using (var ms = new MemoryStream())
{
    formatter.Serialize(ms, thrownException);
    exceptionBytes = ms.ToArray();
}

// 进程A侧反序列化异常
Exception receivedException;
using (var ms = new MemoryStream(exceptionBytes))
{
    receivedException = (Exception)formatter.Deserialize(ms);
}

关键注意事项

  • 共享程序集适配:根据.NET版本调整sharedAssemblies集合(.NET Framework用mscorlib,.NET Core/.NET 5+用System.Private.CoreLib)。
  • 异常构造函数兼容性:确保targetType对应的异常类型存在接受string message的构造函数,系统内置异常均满足此要求。
  • 引用处理:Json.net通过PreserveReferencesHandling配置保留对象引用;BinaryFormatter默认支持引用序列化,自定义代理逻辑无需额外处理。

内容的提问来源于stack exchange,提问作者Timo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 02:31:10