Deserializing in a custom formatter

  • Thread starter Thread starter Markus
  • Start date Start date
M

Markus

Hello all,

I am working on a custom serialization formatter class and
when I try to deserialize a color, I get the exception:
An unhandled exception of
type 'System.MissingMethodException' occurred in
mscorlib.dll
Additional information: Constructor on type
System.Drawing.Color not found.

Note that Color is a structure with no constructors
mentioned in the documentation (just a few methods that
return a color). I don't know if this has any affect on
the system constructor used in deserialization. My code
does work on objects such as fonts which define the
ISerializeable interface.

This is the code line that generates the exception:
return type.Assembly.CreateInstance(type.FullName,
false, BindingFlags.CreateInstance, binder, infoarray,
culture, attributes);

Where:
type is System.Drawing.Color
binder is null
infoarray is an object array containing Color's
fields (4 private members: value, knownColor, state, and
name)
culture is null
attributes is null

After some experimenting, it appears that this exception
is generated if the system can't find a constructor with
the BindingFlags that have been passed. I was able to
duplicate this error with a Font by changing the
BindingFlags parameter, while other modifications to this
call such as an incorrect parameter list generate other
exceptions.

I know it can be done because the SoapFormatter can
correctly store and retrieve colors (I tested this and my
code gathers the same 4 private fields that appear in
soap's xml file).

Does anyone know which BindingFlags to use to locate a
constructor for Color, and how to establish the correct
settings for any given object? I tried several
permutations, but nothing worked so far. Is there a way I
can search for the system's constructor and determine what
its flags should be?

Any pointers on where I can read-up more on writing custom
formatters would be greatly appreciated!

Thanks for all tips (more code follows),
Markus

Here is a code section which I generated for anyone to
duplicate the exception. To execute the CreateColor()
method, generate a new C# Windows Application Project,
double-click on the form, in the XXXX_Load() event add a
call to CreateColor(). The last line in CreateColor() will
generate the exception. The namespaces listed are all that
should be needed - I tested this before posting. Note,
there are 3 commented code lines which, if added to the
code, will do the serialization on a font (the same 3
lines referring to a Color would need to be removed). With
fonts, the code works. Sorry about formatting problems.


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Collections.Specialized;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Reflection;
using System.Globalization;

public void CreateColor()
{
int col;
FormatterConverter _converter = new
FormatterConverter();
unchecked
{
col=(int)(0xFF808080); // a nice
gray
}
System.Drawing.Color test=Color.FromArgb(col);
// System.Drawing.Font test=new Font("Microsoft Sans
Serif",16);
FieldInfo[] fi=test.GetType().GetFields
(BindingFlags.GetField | BindingFlags.Instance |
BindingFlags.NonPublic);
SerializationInfo info = new SerializationInfo
(test.GetType(), _converter);

//serialize to a SerializationInfo object
StreamingContext context = new StreamingContext
(StreamingContextStates.Persistence);
if (test is ISerializable)
{
ISerializable serial = (ISerializable)
((object)test);
serial.GetObjectData(info, context);
}
else foreach( FieldInfo f in fi)
{
if ((f.Attributes &
FieldAttributes.NotSerialized)!
=FieldAttributes.NotSerialized)
{
info.AddValue(f.Name,f.GetValue
(test));
}
}
Binder binder = null; // Use the
default binder
CultureInfo culture = null; // Use the
default culture for the thread
object[] attributes = null; // No
activate attributes

// Move the initialization data into an object
array
object[] infoarray=new object[info.MemberCount];
SerializationInfoEnumerator sie=info.GetEnumerator
();
sie.Reset();
for (int i=0;i < (info.MemberCount);++i)
{
sie.MoveNext();
infoarray=sie.Current.Value;
}
System.Drawing.Color test2=(System.Drawing.Color)
(test.GetType().Assembly.CreateInstance(test.GetType
().FullName,
false, BindingFlags.CreateInstance,
binder, new object[] { info, context }, culture,
attributes));
// System.Drawing.Font test2=(System.Drawing.Font)
(test.GetType().Assembly.CreateInstance(test.GetType
().FullName,
// false, BindingFlags.CreateInstance,
binder, infoarray, culture, attributes));
}
 
Well, just my luck, as soon as I posted, I found the answer - and msdn decided to redo their website so I couldn't reply until now. Apologies if anyone was working on a detailed answer.

For those interested, the following article describes the steps that the formatter follows during de-/serialization - starting on page 5:

http://msdn.microsoft.com/msdnmag/issues/02/04/net

Basically, the FormatterServices class provides the methods needed to access the members to be serialized and the means to deserialize them. I implemented these steps and now my color deserializes - and the serialization code is also shorter.

Markus
 
Back
Top