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

时间:2012-02-26 11:47:10

标签: c# .net .net-4.0 clipboard

我们最近将一个非常大的项目从.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));
        }
    }
}

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

.NET 4输出:
串行器:一切都好 剪贴板:列表为空

我已经查看了剪贴板和放大器的.NET源代码。 DataObject类,但我看不到使用了什么序列化程序。 MSDN文档说该类型必须是可序列化的,在这种情况下,List&lt;&gt;和元素类是。复制一个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

1 个答案:

答案 0 :(得分: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;

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