剪贴板在.NET 3.5和4中的表现不同,但原因是什么

本文关键字:是什么 NET 剪贴板 | 更新日期: 2023-09-27 18:25:45

我们最近将一个非常大的项目从.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<>元素类为。复制一个Element对象效果很好,但一旦我复制了一个元素列表,它就会中断。

为了进行测试,我在VisualStudio2010SP1中创建了2个C#"控制台应用程序"项目。我留下的第一个项目是默认的"目标框架"设置".NET framework 4客户端配置文件"。我修改的第二个项目是使用".NET Framework 3.5客户端配置文件"。

有关我的窗体DLL版本的其他信息:
原始文件名:System.Windows.Forms.dll
文件版本/产品版本:4.0.30319.235
语言:英语(美国)
修改日期:2012年2月16日22:50

剪贴板在.NET 3.5和4中的表现不同,但原因是什么

I repro。您可以使用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中修复。