C# – In C#, how can I serialize System.Exception? (.Net CF 2.0)

ccompact-frameworkexceptionserialization

I want to write an Exception to an MS Message Queue. When I attempt it I get an exception. So I tried simplifying it by using the XmlSerializer which still raises an exception, but it gave me a bit more info:

{"There was an error reflecting type
'System.Exception'."}

with InnerException:

{"Cannot serialize member
System.Exception.Data of type
System.Collections.IDictionary,
because it implements IDictionary."}

Sample Code:

        Exception e = new Exception("Hello, world!");
        MemoryStream stream = new MemoryStream();
        XmlSerializer x = new XmlSerializer(e.GetType()); // Exception raised on this line

        x.Serialize(stream, e);
        stream.Close();

EDIT:
I tried to keep this a simple as possible, but I may have overdone it. I want the whole bit, stack trace, message, custom exception type, and custom exception properties. I may even want to throw the exception again.

Best Answer

I was looking at Jason Jackson's answer, but it didn't make sense to me that I'm having problems with this even though System.Exception implements ISerializable. So I bypassed the XmlSerializer by wrapping the exception in a class that uses a BinaryFormatter instead. When the XmlSerialization of the MS Message Queuing objects kicks in all it will see is a class with a public byte array.

Here's what I came up with:

public class WrappedException {
    public byte[] Data;

    public WrappedException() {
    }

    public WrappedException(Exception e) {
        SetException(e);
    }

    public Exception GetException() {
        Exception result;
        BinaryFormatter bf = new BinaryFormatter();
        MemoryStream stream = new MemoryStream(Data);
        result = (Exception)bf.Deserialize(stream);
        stream.Close();
        return result;
    }

    public void SetException(Exception e) {
        MemoryStream stream = new MemoryStream();
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(stream, e);
        Data = stream.ToArray();
        stream.Close();
    }
}

The first test worked perfectly, but I was still concerned about custom exceptions. So I tossed together my own custom exception. Then I just dropped a button on a blank form. Here's the code:

[Serializable]

public class MyException : Exception, ISerializable {
    public int ErrorCode = 10;
    public MyException(SerializationInfo info, StreamingContext context)
        : base(info, context) {

        ErrorCode = info.GetInt32("ErrorCode");
    }

    public MyException(string message)
        : base(message) {
    }

    #region ISerializable Members
    void ISerializable.GetObjectData(SerializationInfo info, 
        StreamingContext context) {

        base.GetObjectData(info, context);
        info.AddValue("ErrorCode", ErrorCode);
    }

    #endregion
}

private void button1_Click(object sender, EventArgs e) {
    MyException ex = new MyException("Hello, world!");
    ex.ErrorCode = 20;
    WrappedException reply = new WrappedException(ex);
    XmlSerializer x = new XmlSerializer(reply.GetType());
    MemoryStream stream = new MemoryStream();
    x.Serialize(stream, reply);
    stream.Position = 0;
    WrappedException reply2 = (WrappedException)x.Deserialize(stream);
    MyException ex2 = (MyException)reply2.GetException();
    stream.Close();
    Text = ex2.ErrorCode.ToString(); // form shows 20

    // throw ex2;

    }

Although it seemed like all of other exception types that I looked up are marked with the SerializableAttribute, I'm going to have to be careful about custom exceptions that are not marked with the SerializableAttribute.

EDIT: Getting ahead of myself. I didn't realize that BinaryFormatter is not implemented on CF.

EDIT: Above code snippets were in a desktop project. In the CF version, the WrappedException will basically look the same I just need to implement my own BinaryFormater, but I'm very open to suggestions on that one.