如何实现跨进程IPC异常序列化及基类型反序列化(含内部异常)
跨进程IPC异常传递实现方案
核心思路
- 异常类型适配:序列化时遍历异常的继承链,找到第一个存在于进程A、B共享程序集(如
mscorlib、System.Private.CoreLib)的基类型,作为targetType写入序列化数据;反序列化时基于targetType构造异常实例。 - 递归处理内部异常:对
InnerException执行相同的类型适配和序列化逻辑,保证异常链完整传递。 - 引用序列化支持:针对需要按引用传递的类型,通过序列化框架的内置配置或自定义逻辑保留引用关系。
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
相关产品推荐
相关产品推荐

