4

I am attempting to create a Clipboard stack in C#. Clipboard data is stored in System.Windows.Forms.DataObject objects. I wanted to store each clipboard entry (IDataObject) directly in a Generic list. Due to the way Bitmaps (seem to be) stored I am thinking I need to perform a deep copy first before I add it to the list.

I attempted to use Binary serialization (see below) to create a deep copy but since System.Windows.Forms.DataObject is not marked as serializable the serialization step fails. Any ideas?

public IDataObject GetClipboardData()
{
    MemoryStream memoryStream = new MemoryStream();
    BinaryFormatter binaryFormatter = new BinaryFormatter();
    binaryFormatter.Serialize(memoryStream, Clipboard.GetDataObject());
    memoryStream.Position = 0;
    return (IDataObject) binaryFormatter.Deserialize(memoryStream);
}
cgray4
  • 81
  • 2
  • 5

3 Answers3

3

I wrote the code below for another question and maybe it could come in useful for you in this scenario:

    public static class GhettoSerializer
    {
            // you could make this a factory method if your type
            // has a constructor that appeals to you (i.e. default 
            // parameterless constructor)
            public static void Initialize<T>(T instance, IDictionary<string, object> values)
            {
                    var props = typeof(T).GetProperties();

                    // my approach does nothing to handle rare properties with array indexers
                    var matches = props.Join(
                            values,
                            pi => pi.Name,
                            kvp => kvp.Key,
                            (property, kvp) =>
                                    new {
                                            Set = new Action<object,object,object[]>(property.SetValue), 
                                            kvp.Value
                                    }
                    );

                    foreach (var match in matches)
                            match.Set(instance, match.Value, null);
            }
            public static IDictionary<string, object> Serialize<T>(T instance)
            {
                    var props = typeof(T).GetProperties();

                    var ret = new Dictionary<string, object>();

                    foreach (var property in props)
                    {
                            if (!property.CanWrite || !property.CanRead)
                                    continue;
                            ret.Add(property.Name, property.GetValue(instance, null));
                    }

                    return ret;
            }
    }

However I don't think this will be the final solution to your problem though it may give you a place to start.

cfeduke
  • 23,100
  • 10
  • 61
  • 65
  • Thanks for taking a shot at this (points for class naming convention as well). Unfortunately IDataObject contains no properties. Data is "extracted" using methods so the above code returns an empty Dictionary. – cgray4 Oct 16 '08 at 17:00
  • Yes, it’s a bug in the code. Instead of `typeof(T).GetProperties()`, it should say `instance.GetType().GetProperties()`. In fact, I would argue it should actually use the fields rather than properties, but that’s a design choice. – Timwi Sep 05 '10 at 23:41
0

Copy of my answer to: difference between DataContract attribute and Serializable attribute in .net

My answer fits much better here than there, although above question ends with:

"... or maybe a different way of creating a deepclone?"

I once did some inspection to an object structure via Reflection to find all assemblies required for deserialization and serialize them alongside for bootstrapping.

With a bit of work one could build a similar method for deep copying. Basically you need a recursive method that carrys along a Dictionary to detect circular references. Inside the method you inspect all fields about like this:

private void InspectRecursively(object input,
    Dictionary<object, bool> processedObjects)
{
  if ((input != null) && !processedObjects.ContainsKey(input))
  {
    processedObjects.Add(input, true);

    List<FieldInfo> fields = type.GetFields(BindingFlags.Instance |
        BindingFlags.Public | BindingFlags.NonPublic );
    foreach (FieldInfo field in fields)
    {
      object nextInput = field.GetValue(input);

      if (nextInput is System.Collections.IEnumerable)
      {
        System.Collections.IEnumerator enumerator = (nextInput as
            System.Collections.IEnumerable).GetEnumerator();

        while (enumerator.MoveNext())
        {
          InspectRecursively(enumerator.Current, processedObjects);
        }
      }
      else
      {
        InspectRecursively(nextInput, processedObjects);
      }
    }
  }
}

To get it working you need to add an output object and something like System.Runtime.Serialization.FormatterServices.GetUninitializedObject(Type type) to create the most shallowest copy (even without copying references) of each field's value. Finally you can set each field with something like field.SetValue(input, output)

However this implementation does not support registered event handlers, which is _un_supported by deserializing, too. Additionally each object in the hierarchy will be broken, if its class' constructor needs to initialize anything but setting all fields. The last point only work with serialization, if the class has a respective implementation, e.g. method marked [OnDeserialized], implements ISerializable,... .

Community
  • 1
  • 1
No answer
  • 907
  • 1
  • 9
  • 10
0

Look up the docks for Serializable and find the stuff about serialization helpers. You can wrap the bitmap in your own serialization code the integrates with the .net framework.

DrFloyd5
  • 13,555
  • 3
  • 25
  • 34