Recently I had a very strange exception and took me some time to figure out what was the problem. So lets imagine that I want to execute a piece of code in a different app domain, lets call it “NewDomain”, and that NewDomain throws a custom exception “MyException”.
The code of MyException is this:
[Serializable]public class MyException : ApplicationException{public MyException() : base() { }public MyException(string message) : base(message) { myCustomMessage = "Dear user you got an exception: " + message; }public MyException(string message, Exception innerException) :base(message, innerException) { myCustomMessage = "Dear user you got an exception: " + message; }public override string Message {get{return myCustomMessage; } }private string myCustomMessage; }
If this exception is thrown across app domains you will get this nice exception:
The constructor to deserialize an object of type 'MyException’ was not found.
Server stack trace:
at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)
at System.Runtime.Serialization.ObjectManager.FixupSpecialObject(ObjectHolder holder)
at System.Runtime.Serialization.ObjectManager.DoFixups()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Remoting.Channels.CrossAppDomainSerializer.DeserializeObject(MemoryStream stm)
at System.Runtime.Remoting.Channels.CrossAppDomainSerializer.DeserializeMessageParts(MemoryStream stm)
at System.Runtime.Remoting.Messaging.SmuggledMethodReturnMessage.FixupForNewAppDomain()
at System.Runtime.Remoting.Channels.CrossAppDomainSink.SyncProcessMessage(IMessage reqMsg)
…
I didn’t know what the problem was at the beginning, because my class was marked with the attribute Serializable so what was the real problem???
The problem happens because I was transmitting the exception object across a stream. And across streams mark the class as Serializable is not enough.
So the solution is actually simple.
- I need to add a protected constructor with the following signature
- My MyException class needs to implement ISerializable, the class System.Exception already implements it
- I need to implement the method
- Attribute the method with the following attribute
protected MyException(SerializationInfo info, StreamingContext context)
GetObjectData(SerializationInfo info, StreamingContext context)
I need all of this because the constructor is called during deserialization to reconstitute the exception object transmitted over a stream, and I need the method GetObjectData because I need to serialize my fields.
So, MyException class will lock like this:
[Serializable]public class MyException : ApplicationException{public MyException() : base() { }public MyException(string message) : base(message) { myCustomMessage = "Dear user you got an exception: " + message; }public MyException(string message, Exception innerException) :base(message, innerException) { myCustomMessage = "Dear user you got an exception: " + message; }protected MyException(SerializationInfo info, StreamingContext context) : base(info, context) { myCustomMessage = info.GetString("myCustomMessage"); } [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("myCustomMessage", myCustomMessage);base.GetObjectData(info, context); }public override string Message {get{return myCustomMessage; } }private string myCustomMessage; }
Like I wrote I just had to implement a constructor to deserialize my fields and a method to serialize them.
Image may be NSFW.Clik here to view.