Буфер обмена ведет себя по-разному в .NET 3.5 и 4, но почему? - PullRequest
25 голосов
/ 26 февраля 2012

Недавно мы обновили очень большой проект с .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 output:Сериализатор: все хорошоБуфер обмена: все хорошо

.NET 4 вывод:Сериализатор: все хорошоБуфер обмена: список пуст

Я посмотрел на источник .NET для класса Clipboard & DataObject, но не смог увидеть, какой сериализатор использовался.В документации MSDN сказано, что тип должен быть сериализуемым, что в данном случае относится к классам List <> и Element.Копирование объекта Element работает просто отлично, но как только я копирую список элементов, он ломается.

Для проверки я создал 2 проекта C # «Консольное приложение» в Visual Studio 2010 SP1.Первый проект, который я оставил с настройкой по умолчанию «Целевой фреймворк» «.NET Framework 4 Client Profile».Второй проект, который я изменил, чтобы использовать «.NET Framework 3.5 Client Profile».

Дополнительная информация о моей версии DLL библиотеки форм:Оригинальное имя файла: System.Windows.Forms.dllВерсия файла / версия продукта: 4.0.30319.235Язык: английский (США)Дата изменения: 16-02-2012 22:50

1 Ответ

26 голосов
/ 26 февраля 2012

Я репро. Вы можете получить более подробную информацию об ошибке с помощью Debug + Exceptions, отметив флажок Thrown для исключений CLR. Это остановит программу, когда внутреннее исключение выдается кодом буфера обмена в платформе. Сбой метода реализации IDataObject.GetDataHere () с исключением COM: «Неверная структура FORMATETC (исключение из HRESULT: 0x80040064 (DV_E_FORMATETC))».

Что-то не так с форматом. Это становится понятным, когда вы устанавливаете точку останова после оператора Clipboard.SetDataObject (obj). И поместите Clipboard.GetDataObject (). GetFormats () в выражение просмотра отладчика. Я вижу:

"System.Collections.Generic.List`1 [[ClipboardTest.Program + Element, ConsoleApplication1, версия = 1.0.0.0, культура = нейтральная, общедоступная"

Обратите внимание, как строка усекается, часть 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...