剪贴板在.NET 3.5和4中的行为有所不同,但是为什么呢? [英] Clipboard behaves differently in .NET 3.5 and 4, but why?

查看:125
本文介绍了剪贴板在.NET 3.5和4中的行为有所不同,但是为什么呢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We recently upgraded a very large project from .NET framework 3.5 to 4, and initially everything seemed to work the same. But now bugs have started to appear on copy paste operations. I have managed to make a small reproducible app, which shows the different behavior in .NET 3.5 and 4. I have also found a workaround (manually serialize the data to the clipboard), but I'm left with a need to know "why" there is a difference in behavior.

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

This is the small test app I made:

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 3.5 output:
Serializer : all good
Clipboard : all good

.NET 4输出:

序列化程序:都很好

剪贴板:列表为空

.NET 4 output:
Serializer : all good
Clipboard : List is null

我已经在.NET源代码中找到了剪贴板和DataObject类,但是我看不到使用了什么序列化器。 MSDN文档说类型必须是可序列化的,在这种情况下List<>和Element类都是可序列化的。复制Element对象效果很好,但是一旦复制元素列表,它就会中断。

I have looked at the .NET source for the Clipboard & DataObject class, but I couldn't see what serializer was used. The MSDN documentation says that the type must be serializable, which in this case both the List<> and Element classes are. Copying an Element object works just fine, but as soon as I copy a list of elements, it breaks.

为了进行测试,我创建了2个C# Console Application项目在Visual Studio 2010 SP1中。我留下的第一个项目的默认目标框架设置为 .NET Framework 4 Client Profile。我修改为使用 .NET Framework 3.5客户端配置文件的第二个项目。

To test, I have created 2 C# "Console Application" projects in Visual Studio 2010 SP1. The first project I have left with the default "Target framework" setting of ".NET Framework 4 Client Profile". The second project I have modified to use ".NET Framework 3.5 Client Profile".

有关我的Forms DLL版本的其他信息:

原始文件名:System.Windows.Forms.dll

文件版本/产品版本:4.0.30319.235

语言:英语(美国)

修改日期:2012年2月16日22:50

Additional information about my Forms DLL version:
Original filename: System.Windows.Forms.dll
File version/Prouct version : 4.0.30319.235
Language: English (United States)
Date modified: 16-02-2012 22:50

推荐答案

我复制了。您可以通过Debug + Exceptions来更深入地了解该错误,选中CLR例外的Throwed复选框。当框架中的剪贴板代码引发内部异常时,这将停止程序。 IDataObject.GetDataHere()实现方法因COM异常而失败,无效的FORMATETC结构(HRESULT的异常:0x80040064(DV_E_FORMATETC))。

I repro. You can get more insight into the bug with Debug + Exceptions, tick the Thrown checkbox for CLR exceptions. That will stop the program when an internal exception is thrown by the clipboard code in the framework. The IDataObject.GetDataHere() implementation method fails with a COM exception, "Invalid FORMATETC structure (Exception from HRESULT: 0x80040064 (DV_E_FORMATETC))".

出问题了格式。当您在Clipboard.SetDataObject(obj)语句之后设置断点时,这一点变得很清楚。并将Clipboard.GetDataObject()。GetFormats()放入调试器监视表达式中。我看到了:

There is something wrong with the format. That becomes clear when you set a breakpoint after the Clipboard.SetDataObject(obj) statement. And put Clipboard.GetDataObject().GetFormats() in a debugger watch expression. I see:


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

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

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

Note how the string is truncated, the PublicKeyToken part got mangled. You can arbitrarily alter this truncated string by changing the namespace name and the project name. Make them short enough and the program won't fail.

很显然,这就是问题的原因。字符串长度被限制为127个字符,任何全名超过该长度的类型都将导致此失败。

Clearly this is the cause of the problem. The string length is clipped to 127 characters, any type whose full name is longer than that is going to cause this failure. With a high likelihood that this will be a generic type since they have very long names.

请使用connect.microsoft.com报告此错误,因为它们的名称很长,因此很有可能成为通用类型。您的代码很好地演示了该错误,只需在错误报告中发布指向该错误的链接就足够了。我没有很好的解决方法,要确保名称足够简短不是很实际。但是,您可以使用以下代码:

Please report this bug at connect.microsoft.com. Your code demonstrates the bug very well, just posting a link to it in your bug report will be sufficient. I don't have a very good workaround, ensuring the name is short enough is not very practical. But you can with code like this:

        // 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中修复。


UPDATE: this bug is reported fixed in VS2013.

这篇关于剪贴板在.NET 3.5和4中的行为有所不同,但是为什么呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆