Cross AppDomain exception serialization

Given two application domains: in the first, Library1 and CommonLibrary are loaded. In the second, Library2 and CommonLibrary are loaded.

Library2 defines a Library2Exception that is inherited from CommonException (defined in CommonLibrary). When I call the first AppDomain method in MarshallByRef of the second AppDomain that throws a Library2Exception, a SerializationException is thrown.

Indeed, .Net tries to deserialize the Library2Exception, but this type is defined in Library2, which is not found in the first AppDomain. I want this to become a CommonException that I can handle.

So my questions are:

+4
source share
3 answers

I found! Override GetObjectData to change the type of exception:

[Serializable] public class CommonException : Exception { public CommonException() { } public CommonException(string message) : base(message) { } public CommonException(string message, Exception inner) : base(message, inner) { } protected CommonException( SerializationInfo info, StreamingContext context) : base(info, context) { } public override void GetObjectData( SerializationInfo info, StreamingContext context) { if (context.State == StreamingContextStates.CrossAppDomain) info.SetType(typeof(CommonException)); base.GetObjectData(info, context); } } 
+3
source

You must either load Library2 in the first appdomain, or add some kind of exception defined in CommonLibrary.

PS Exceptions are thrown by reference (within the same application domain) because they are reference types, but they are thrown "by value" between different application domains (since they are not descendants of MarshalByRef), and you cannot change this behavior. Cosider:

 //Oops! I can't do that! public class MyException : Exception, MarshalByRef { } 

PSS You can use serialization surrogates or something like that to solve your problem, but I think it is much cleaner and easier to explicitly throw a generic exception type.

+2
source

Here is an example of the binding binding you requested, this routine is a custom Serialized with a SerializationBinder used as a parameter

 // ... This is a class object of type Foo... public bool Serialize(string sPath, System.Runtime.Serialization.SerializationBinder serializationBinder) { bool bSuccessful = false; if (serializationBinder == null) return false; try { using (FileStream fStream = new FileStream(sPath, FileMode.Create)) { try { BinaryFormatter bf = new BinaryFormatter(); bf.Binder = serializationBinder; bf.Serialize(fStream, this._someFoo); bSuccessful = true; } catch (System.Runtime.Serialization.SerializationException sEx) { System.Diagnostics.Debug.WriteLine(sEx.ToString()); bSuccessful = false; } } } catch (System.IO.IOException ioEx) { System.Diagnostics.Debug.WriteLine(string.Format("[Serialize(...)] - IO EXCEPTION> DETAILS ARE {0}", ioEx.ToString())); bSuccessful = false; } return bSuccessful; } public bool Deserialize(string sFileName, System.Runtime.Serialization.SerializationBinder serializationBinder) { bool bSuccessful = false; // if (!System.IO.File.Exists(sFileName)) return false; if (serializationBinder == null) return false; this._foo = new Foo(); // try { using (FileStream fStream = new FileStream(sFileName, FileMode.Open)) { try { BinaryFormatter bf = new BinaryFormatter(); bf.Binder = serializationBinder; this._foo = (Foo)bf.Deserialize(fStream); bSuccessful = true; } catch (System.Runtime.Serialization.SerializationException sEx) { System.Diagnostics.Debug.WriteLine(string.Format("[DeSerialize(...)] - SERIALIZATION EXCEPTION> DETAILS ARE {0}", sEx.ToString())); bSuccessful = false; } } } catch (System.IO.IOException ioEx) { System.Diagnostics.Debug.WriteLine(string.Format("[DeSerialize(...)] - IO EXCEPTION> DETAILS ARE {0}", ioEx.ToString())); bSuccessful = false; } return (bSuccessful == true); } // End class method for object class type Foo public class BarBinder : System.Runtime.Serialization.SerializationBinder { public override Type BindToType(string assemblyName, string typeName) { Type typeToDeserialize = null; try { // For each assemblyName/typeName that you want to deserialize to // a different type, set typeToDeserialize to the desired type. string assemVer1 = System.Reflection.Assembly.GetExecutingAssembly().FullName; if (assemblyName.StartsWith("Foo")) { assemblyName = assemVer1; typeName = "FooBar" + typeName.Substring(typeName.LastIndexOf("."), (typeName.Length - typeName.LastIndexOf("."))); } typeToDeserialize = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName)); } catch (System.Exception ex1) { throw ex1; } finally { } return typeToDeserialize; } } 

And called like this:

 _foo.DeSerialize(@"C:\foo.dat", new BarBinder()); 

What happens when a “BarBinder” is created and assigned to the BinaryFormatter Binder property, since the serializable data has a type name of Foo.SomeClass, we applied “BarBinder” by renaming the type name to “FooBar.SomeClass” 'so that the data belongs to a different type ...

0
source

Source: https://habr.com/ru/post/1303211/


All Articles