【问题标题】:Clipboard behaves differently in .NET 3.5 and 4, but why?剪贴板在 .NET 3.5 和 4 中的行为不同,但为什么呢?
【发布时间】:2012-02-26 11:47:10
【问题描述】:

我们最近将一个非常大的项目从 .NET 框架 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 输出:
序列化器:一切都好
剪贴板:列表为空

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

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

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

【问题讨论】:

  • 对我来说,它适用于 .NET 4... 它在两种情况下都显示“一切都好”
  • 嗯,在我的机器上,它适用于 3.5,但在 4.0 设置为目标框架时失败,就像 OP 状态一样(它在 Clipboard.GetDataObject( ).GetData())。另外,我在这里发现了一个类似的问题:connect.microsoft.com/VisualStudio/feedback/details/488627/…
  • @ThomasLevesque 我很好奇,所以我也进行了测试,得到的结果与 bitmonk8 相同。 3.5 还可以,但 4.0 不行。 System.Windows.Forms dll 在这两种情况下都是 4.0。

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


【解决方案1】:

我复制。您可以使用 Debug + Exceptions 更深入地了解错误,勾选 CLR 异常的 Throw 复选框。当框架中的剪贴板代码引发内部异常时,这将停止程序。 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 中修复。

【讨论】:

  • 啊……现在更有意义了。它还解释了为什么我的手动序列化解决方法有效。非常感谢。
  • 没问题,他们会解决的。如果您可以记录 System.Windows.Forms.dll 的文件版本,将会很有用。导航到 c:\windows\microsoft.net\assembly\gac_msil 并从那里向下钻取。右键单击 DLL,属性,版本。在报告中记录文件版本和日期。我猜这是由最近的安全补丁引起的,或者之前应该已经注意到了。而且我在参考源代码中没有看到明显的原因。
  • 我已将 DLL 版本信息添加到 bug 和原始问题中(以防其他人遇到类似问题)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多