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

bit*_*nk8 25 .net c# clipboard .net-4.0

我们最近将一个非常大的项目从.NET framework 3.5升级到了4,最初一切看起来都是一样的.但是现在错误已经开始出现在复制粘贴操作上.我已经设法制作了一个可重复的小应用程序,它显示了.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<Element> TestSerializer(List<Element> obj)
        {
            var memoryStream = new MemoryStream();
            var formatter = new BinaryFormatter();
            formatter.Serialize(memoryStream, obj);
            return (List<Element>)formatter.Deserialize(new MemoryStream(memoryStream.GetBuffer()));
        }

        public static List<Element> TestClipboard(List<Element> obj)
        {
            Clipboard.SetDataObject(obj);
            return (List<Element>)Clipboard.GetDataObject().GetData(typeof(List<Element>));
        }

        public static void DumpObject(string testName, List<Element> 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<Element> { new Element("all good") };
            DumpObject("Serializer", TestSerializer(copyData));
            DumpObject("Clipboard", TestClipboard(copyData));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

.NET 3.5输出:
Serializer:所有好的
剪贴板:一切都很好

.NET 4输出:
Serializer:所有好的
剪贴板: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

Han*_*ant 26

我责备.您可以通过Debug + Exceptions更深入地了解该错误,勾选CLR异常的Thrown复选框.当框架中的剪贴板代码抛出内部异常时,这将停止程序.IDataObject.GetDataHere()实现方法失败,出现COM异常,"无效的FORMATETC结构(来自HRESULT的异常: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<object>();
        envelope.AddRange(obj);
        Clipboard.SetDataObject(envelope);

        // Retrieve from clipboard, unwrap back to original type
        envelope = (List<object>)Clipboard.GetDataObject().GetData(typeof(List<object>));
        var retval = new List<Element>();
        retval.AddRange(envelope.Cast<Element>());
        return retval;
Run Code Online (Sandbox Code Playgroud)

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