2012-02-26 13 views
25

Niedawno uaktualniliśmy bardzo duży projekt z platformy .NET 3.5-4, a początkowo wszystko działało tak samo. Ale teraz zaczęły pojawiać się błędy w operacjach wklejania kopii. Udało mi się zrobić małą, odtwarzalną aplikację, która pokazuje różne zachowanie w .NET 3.5 i 4. Znalazłem również obejście (ręcznie serializować dane do schowka), ale muszę wiedzieć "dlaczego" istnieje różnica w zachowaniu.Schowek zachowuje się inaczej w .NET 3.5 i 4, ale dlaczego?

Jest to mała aplikacja Test zrobiłem:

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 wyjście:
serializer: wszystko dobre
Schowek: wszystko dobre

.NET 4 wyjścia:
serializer : wszystkie dobre
Schowek: lista jest pusta

Spojrzałem na źródło .NET dla klasy Schowek & DataObject, ale nie widziałem, który serializator został użyty. Dokumentacja MSDN mówi, że typ musi być możliwy do serializacji, co w tym przypadku ma zarówno klasę List <>, jak i Element. Kopiowanie obiektu Element działa dobrze, ale jak tylko skopiuję listę elementów, zepsuje się.

Aby przetestować, stworzyłem 2 projekty C# "Console Application" w Visual Studio 2010 SP1. Pierwszy projekt, który pozostał przy domyślnym ustawieniu "Struktura docelowa" "Profil klienta .NET Framework 4". Drugi projekt, który zmodyfikowałem, aby korzystać z "profilu klienta .NET Framework 3.5".

Dodatkowe informacje o moich formularzy DLL wersji:
Original filename: System.Windows.Forms.dll
wersja Plik/wersja prouct: 4.0.30319.235
Język: Angielski (Stany Zjednoczone)
Data modyfikacji: 16 -02-2012 22:50

+0

Dla mnie działa dobrze z .NET 4 ... drukuje "wszystko dobrze" dla obu przypadków –

+0

Hm, na moim komputerze, działa dobrze z 3.5 i kończy się niepowodzeniem na 4.0 ustawiony jako ramka docelowa, podobnie jak OP states (zgłasza wyjątek COM "Invalid FORMATETC structure" w Clipboard.GetDataObject(). GetData()). Również znalazłem podobny problem tutaj: https://connect.microsoft.com/VisualStudio/feedback/details/488627/comexception-invalid-formatetc-structure-while-pasting-marshalbyref-data-from-datagridview – Alan

+0

(przy użyciu VS2010 SP1 na XP SP3) – Alan

Odpowiedz

26

I repro. Możesz uzyskać lepszy wgląd w błąd za pomocą Debug + wyjątki, zaznacz pole wyboru Wyrzucane dla wyjątków CLR. Spowoduje to zatrzymanie programu, gdy wewnętrzny kod wyjątku zostanie zgłoszony przez kod schowka w strukturze. Metoda implementacji IDataObject.GetDataHere() kończy się niepowodzeniem z wyjątkiem COM, "Niepoprawna struktura FORMATETC (wyjątek z HRESULT: 0x80040064 (DV_E_FORMATETC))".

Coś jest nie tak z tym formatem. Staje się to jasne po ustawieniu punktu przerwania po instrukcji Clipboard.SetDataObject (obj). I umieścić Clipboard.GetDataObject(). GetFormats() w wyrażeniu watch debugger. Widzę:

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

zauważyć jak ciąg jest obcięty, część PublicKeyToken została zmanipulowana. Możesz dowolnie zmienić ten obcięty łańcuch, zmieniając nazwę przestrzeni nazw i nazwę projektu. Spraw, aby były wystarczająco krótkie, a program nie zawiedzie.

Najwyraźniej jest to przyczyną problemu. Długość ciągu jest przypięta do 127 znaków, dowolnego typu, którego pełna nazwa jest dłuższa niż ta, która spowoduje ten błąd. Z dużym prawdopodobieństwem, że będzie to rodzaj ogólny, ponieważ mają bardzo długie nazwy.

Zgłoś ten błąd na stronie connect.microsoft.com. Twój kod bardzo dobrze pokazuje błąd, wystarczy zamieścić link w raporcie o błędzie. Nie mam bardzo dobrego obejścia, a zapewnienie, że nazwa jest wystarczająco krótka, nie jest zbyt praktyczne. Ale można z kodu:

 // 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; 

UPDATE: Ten błąd jest zgłaszane ustalone w VS2013.

+0

Ahh ... teraz ma to więcej sensu. Wyjaśnia również, dlaczego moje ręczne rozwiązanie problemu serializacji działa. Wielkie dzięki. – bitmonk8

+0

Zapisano przy połączeniu: https://connect.microsoft.com/VisualStudio/feedback/details/726652/clipboard-truncates-type-name-to-127- characters –

+0

Whoops ... Wygląda na to, że mamy podwójną rejestrację. https://connect.microsoft.com/VisualStudio/feedback/details/726654/clipboard-has-different-undocumented-behavior-in-net-3-5-and-4 – bitmonk8

Powiązane problemy