剪贴板在.NET 3.5和4中表现不同,但为什么呢?

我们最近将一个非常大的项目从.NET framework 3.5升级到了4,最初一切看起来都是一样的。 但是现在bug已经开始出现在复制粘贴操作上了。 我已经成功制作了一个可重复的小应用程序,它显示了.NET 3.5和4中的不同行为。我还找到了一种解决方法(手动将数据序列化到剪贴板),但我需要知道“为什么“行为有所不同。

这是我制作的小测试应用程序:

using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Windows.Forms; namespace ClipboardTest { public class Program { [Serializable] public class Element { public Element(string name) { this.name = name; } public string name; } public static List TestSerializer(List obj) { var memoryStream = new MemoryStream(); var formatter = new BinaryFormatter(); formatter.Serialize(memoryStream, obj); return (List)formatter.Deserialize(new MemoryStream(memoryStream.GetBuffer())); } public static List TestClipboard(List obj) { Clipboard.SetDataObject(obj); return (List)Clipboard.GetDataObject().GetData(typeof(List)); } public static void DumpObject(string testName, List obj) { if (obj == null) { Console.WriteLine("{0} : List is null", testName); return; } foreach (var prop in obj) { Console.WriteLine("{0} : {1}", testName, prop.name); } } [STAThread] static void Main() { var copyData = new List { new Element("all good") }; DumpObject("Serializer", TestSerializer(copyData)); DumpObject("Clipboard", TestClipboard(copyData)); } } } 

.NET 3.5输出:
串行器:一切都好
剪贴板:一切都很好

.NET 4输出:
串行器:一切都好
剪贴板:List为null

我查看了Clipboard&DataObject类的.NET源代码,但是我看不到使用了什么序列化程序。 MSDN文档说该类型必须是可序列化的,在这种情况下,List 和Element类都是可序列化的。 复制一个Element对象可以正常工作,但只要复制一个元素列表就会中断。

为了测试,我在Visual Studio 2010 SP1中创建了2个C#“控制台应用程序”项目。 我剩下的第一个项目是“.NET Framework 4 Client Profile”的默认“Target framework”设置。 我修改的第二个项目使用“.NET Framework 3.5 Client Profile”。

有关我的Forms DLL版本的其他信息:
原始文件名:System.Windows.Forms.dll
文件版本/ Prouct版本:4.0.30319.235
语言:英语(美国)
修改日期:16-02-2012 22:50

我责备。 您可以通过Debug + Exceptions更深入地了解该错误,勾选CLRexception的Thrown复选框。 当框架中的剪贴板代码抛出内部exception时,这将停止程序。 IDataObject.GetDataHere()实现方法失败,出现COMexception,“无效的FORMATETC结构(HRESULTexception:0x80040064(DV_E_FORMATETC))”。

格式有问题。 在Clipboard.SetDataObject(obj)语句之后设置断点时,这一点就变得清晰了。 并在调试器监视表达式中放入Clipboard.GetDataObject()。GetFormats()。 我知道了:

“System.Collections.Generic.List`1 [[ClipboardTest.Program + Element,ConsoleApplication1,Version = 1.0.0.0,Culture = neutral,Public”

注意字符串是如何被截断的,PublicKeyToken部分被破坏了。 您可以通过更改命名空间名称和项目名称来任意更改此截断的字符串。 让它们足够短,程序不会失败。

显然,这是问题的原因。 字符串长度被剪切为127个字符,任何类型的全名都比这个更长,这将导致此失败。 由于它们具有很长的名称,因此它很可能是通用类型。

请在connect.microsoft.com上报告此错误。 您的代码很好地演示了该错误,只需在您的错误报告中发布一个链接即可。 我没有很好的解决方法,确保名称足够短不太实用。 但你可以使用这样的代码:

  // Put it on the clipboard, use a wrapper type with a short name var envelope = new List(); envelope.AddRange(obj); Clipboard.SetDataObject(envelope); // Retrieve from clipboard, unwrap back to original type envelope = (List)Clipboard.GetDataObject().GetData(typeof(List)); var retval = new List(); retval.AddRange(envelope.Cast()); return retval; 

更新:此错误在VS2013中报告已修复。