Udostępnij przez


Zmiany schowka formularzy systemu Windows i obiektu DataObject na platformie .NET 10

W tym artykule pokazano, jak uaktualnić operacje schowka i przeciągania oraz upuszczania w Windows Forms do nowych, bezpiecznych dla typów interfejsów API na platformie .NET 10. Dowiesz się, jak używać nowych Clipboard.TryGetData metod i Clipboard.SetDataAsJson<T>(String, T) , zrozumieć, które wbudowane typy działają bez zmian, oraz dowiedzieć się, jakie strategie umożliwiają obsługę typów niestandardowych i starszych danych po usunięciu elementu BinaryFormatter.

BinaryFormatter został usunięty ze środowiska uruchomieniowego na platformie .NET 9 z powodu luk w zabezpieczeniach. Ta zmiana przerwała operacje schowka oraz przeciągania i upuszczania na obiektach niestandardowych. Platforma .NET 10 wprowadza nowe interfejsy API, które używają serializacji JSON i metod bezpiecznych pod kątem typów w celu przywrócenia tej funkcji, poprawy bezpieczeństwa i zapewnienia lepszej obsługi błędów i zgodności między procesami.

Clipboard.SetData(String, Object) nie działa już z typami niestandardowymi, które wymagają BinaryFormatter do serializacji. Ta metoda wykonuje operację kopiowania (domyślnie), która natychmiastowo serializuje dane, ale teraz serializacja kończy się niepowodzeniem dla typów, które wymagają BinaryFormatter, ponieważ zostało usunięte. Clipboard.GetData(String) jest przestarzały na platformie .NET 10. Jeśli wymagane jest BinaryFormatter do deserializacji, ale nie jest ono włączone, GetData() zwraca instancję NotSupportedException. Użyj nowych metod Clipboard.TryGetData i Clipboard.SetDataAsJson<T>(String, T) dla operacji typowo bezpiecznych oraz serializacji JSON obiektów niestandardowych.

Poniższe sekcje zawierają szczegółowe wskazówki dotyczące migracji, wyjaśniają, które typy działają bez zmian, i pokazują, jak obsługiwać zarówno nowe scenariusze programowania, jak i starszych danych.

Wymagania wstępne

Przed kontynuowaniem zapoznaj się z następującymi pojęciami:

  • Jak aplikacje wykorzystywały BinaryFormatter w scenariuszach korzystania ze schowka oraz przeciągania i upuszczania przed platformą .NET 9.
  • Luki w zabezpieczeniach, które doprowadziły do usunięcia elementu BinaryFormatter.
  • Jak pracować z wzorcami System.Text.Json serializacji i ich ograniczeniami.

Aby uzyskać więcej informacji, zobacz następujące artykuły:

Zmiany powodujące łamanie zgodności w wyniku usunięcia BinaryFormatter

Usunięcie BinaryFormatter w .NET 9 radykalnie zmienia sposób, w jaki Windows Forms obsługuje operacje schowka oraz przeciąganie i upuszczanie w przypadku typów niestandardowych. Te zmiany wpływają na istniejące wzorce kodu i wymagają starannej migracji w celu zachowania funkcjonalności.

Typy niestandardowe nie serializują się już automatycznie

W programie .NET 8 lub starszym można umieścić dowolny obiekt niestandardowy z możliwością serializacji w schowku, wywołując metodę SetData(). Serializacja przez BinaryFormatter jest obsługiwana automatycznie. Począwszy od .NET 9, SetData() nadal wykonuje operację kopiowania, która natychmiast serializuje dane, ale ta serializacja kończy się niepowodzeniem w przypadku typów wymagających BinaryFormatter, ponieważ ta funkcjonalność została usunięta.

Następujący kod nie działa już:

[Serializable]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public static void BrokenCustomTypeExample()
{
    // This worked in .NET 8 and earlier but silently fails starting with .NET 9
    Person person = new Person { Name = "John", Age = 30 };
    Clipboard.SetData("MyApp.Person", person);  // No data is stored

    // Later attempts to retrieve the data return a NotSupportedException instance
    object data = Clipboard.GetData("MyApp.Person");
}
<Serializable>
Public Class Person
    Public Property Name As String
    Public Property Age As Integer
End Class

Public Shared Sub BrokenCustomTypeExample()
    ' This worked in .NET 8 and earlier but silently fails starting with .NET 9
    Dim person As New Person With {.Name = "John", .Age = 30}
    Clipboard.SetData("MyApp.Person", person)  ' No data is stored

    ' Later attempts to retrieve the data return a NotSupportedException instance
    Dim data As Object = Clipboard.GetData("MyApp.Person")
End Sub

Co można zobaczyć

  • Metoda SetData() kończy się bez zgłaszania wyjątku.
  • Dane zostają umieszczone w schowku, ale serializacja kończy się niepowodzeniem dla typów, które wymagają BinaryFormatter.
  • Późniejsze próby pobrania danych z GetData() zwracają instancję NotSupportedException, która wskazuje, że BinaryFormatter jest wymagane, ale nie włączone.

Wskazówki dotyczące migracji

Użyj nowej SetDataAsJson<T>() metody lub ręcznie serializuj do obiektu string lub byte[]. Aby uzyskać szczegółowe informacje, zobacz sekcję Praca z typami niestandardowymi .

Funkcja GetData() jest przestarzała — zamiast tego użyj funkcji TryGetData<T>()

Starsza GetData() metoda jest przestarzała na platformie .NET 10. Ta metoda zwraca dane pomyślnie w większości przypadków, ale gdy BinaryFormatter jest wymagana do deserializacji i nie jest włączona, GetData() zwraca instancję NotSupportedException, która wskazuje na potrzebę BinaryFormatter. Należy przeprowadzić migrację do nowych metod bezpiecznych dla typów w celu zapewnienia lepszej obsługi błędów i bezpieczeństwa typów.

W poniższym przykładzie pokazano przestarzały wzorzec, którego należy unikać:

public static void ObsoleteGetDataExample()
{
    // Don't use - GetData() is obsolete in .NET 10
    object data = Clipboard.GetData("MyApp.Person");  // Obsolete method

    // Returns a NotSupportedException instance for a custom object type
    if (data is Person person)
    {
        Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}");
    }
}
Public Shared Sub ObsoleteGetDataExample()
    ' Don't use - GetData() is obsolete in .NET 10
    Dim data As Object = Clipboard.GetData("MyApp.Person")  ' Obsolete method

    ' Returns a NotSupportedException instance for a custom object type
    If TypeOf data Is Person Then
        Dim person As Person = DirectCast(data, Person)
        Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}")
    End If
End Sub

Zamiast tego należy użyć nowoczesnego bezpiecznego typowania za pomocą TryGetData<T>().

public static void ModernTryGetDataExample()
{
    var data = new Person { Name = "Alice", Age = 28 };
    Clipboard.SetDataAsJson("MyAppData", data);

    // Use this - type-safe approach with TryGetData<T>()
    if (Clipboard.TryGetData("MyApp.Person", out Person person))
    {
        // person is guaranteed to be the correct type
        Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}");
    }
    else
    {
        // Handle the case where data isn't available or is the wrong type
        MessageBox.Show("Unable to retrieve person data from clipboard");
    }
}
Public Shared Sub ModernTryGetDataExample()
    Dim data As New Person With {.Name = "Alice", .Age = 30}
    Clipboard.SetDataAsJson("MyAppData", data)

    ' Use this - type-safe approach with TryGetData(Of T)()
    Dim person As Person = Nothing
    If Clipboard.TryGetData("MyApp.Person", person) Then
        ' person is guaranteed to be the correct type
        Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}")
    Else
        ' Handle the case where data isn't available or is the wrong type
        MessageBox.Show("Unable to retrieve person data from clipboard")
    End If
End Sub

Zalety funkcji TryGetData<T>()

  • Bezpieczeństwo typu: brak potrzeby rzutowania — metoda automatycznie zwraca precyzyjny typ, którego oczekujesz.
  • Jasna obsługa błędów: zwraca logiczny wskaźnik powodzenia zamiast używać wzorców wartości null lub wyjątków.
  • Odporność na przyszłość: zaprojektowane do współpracy z nowymi metodami serializacji oraz obsługą danych starszego typu.

Jak zidentyfikować kod, którego dotyczy problem

Wyszukaj:

  • Wszystkie wywołania GetData(), ponieważ cała metoda jest przestarzała, niezależnie od typu danych.
  • Użycie DataObject.GetData() i IDataObject.GetData() w operacjach przeciągania i upuszczania.

Wskazówki dotyczące migracji

Zastąp wszystkie wystąpienia GetData() metodami TryGetData<T>() bezpiecznymi dla typu. Aby zapoznać się z obszernymi przykładami wszystkich przeciążeń, zajrzyj do sekcji Nowe bezpieczne dla typów interfejsy API.

Nowe typowo bezpieczne interfejsy API

Platforma .NET 10 wprowadza trzy nowe rodziny interfejsów API, które zapewniają bezpieczeństwo typów, lepszą obsługę błędów i obsługę serializacji JSON dla schowka i operacji przeciągania i upuszczania:

Metody TryGetData<T>()

Rodzina TryGetData<T>() zastępuje przestarzałą GetData() metodę. Zapewnia pobieranie typów z kontrolą bezpieczeństwa oraz czytelne wskazanie powodzenia lub niepowodzenia operacji schowka.

Podstawowe pobieranie zgodne z typem

public static void BasicTypeSafeRetrievalExamples()
{
    // Retrieve text data using a standard format
    if (Clipboard.TryGetData(DataFormats.Text, out string textData))
        Console.WriteLine($"Text: {textData}");

    // Retrieve an integer using a custom format
    if (Clipboard.TryGetData("NumberData", out int numberData))
        Console.WriteLine($"Number: {numberData}");

    // Retrieve Unicode text using a standard format
    if (Clipboard.TryGetData(DataFormats.UnicodeText, out string unicodeText))
        Console.WriteLine($"Unicode: {unicodeText}");

    // Retrieve raw text data with OLE conversion control
    if (Clipboard.TryGetData(DataFormats.Text, out string rawText))
        Console.WriteLine($"Raw: {rawText}");

    // Retrieve file drops using a standard format
    if (Clipboard.TryGetData(DataFormats.FileDrop, out string[] files))
        Console.WriteLine($"Files: {string.Join(", ", files)}");
}
Public Shared Sub BasicTypeSafeRetrievalExamples()
    ' Retrieve text data using a standard format
    Dim textData As String = Nothing
    If Clipboard.TryGetData(DataFormats.Text, textData) Then
        Console.WriteLine($"Text: {textData}")
    End If

    ' Retrieve an integer using a custom format
    Dim numberData As Integer
    If Clipboard.TryGetData("NumberData", numberData) Then
        Console.WriteLine($"Number: {numberData}")
    End If

    ' Retrieve Unicode text using a standard format
    Dim unicodeText As String = Nothing
    If Clipboard.TryGetData(DataFormats.UnicodeText, unicodeText) Then
        Console.WriteLine($"Unicode: {unicodeText}")
    End If

    ' Retrieve raw text data with OLE conversion control
    Dim rawText As String = Nothing
    If Clipboard.TryGetData(DataFormats.Text, rawText) Then
        Console.WriteLine($"Raw: {rawText}")
    End If

    ' Retrieve file drops using a standard format
    Dim files As String() = Nothing
    If Clipboard.TryGetData(DataFormats.FileDrop, files) Then
        Console.WriteLine($"Files: {String.Join(", ", files)}")
    End If
End Sub

Niestandardowe typy JSON

public static void CustomJsonTypesExamples()
{
    // Retrieve a custom type stored with SetDataAsJson<T>()
    if (Clipboard.TryGetData("Person", out Person person))
        Console.WriteLine($"Person: {person.Name}");

    // Retrieve application-specific data formats
    if (Clipboard.TryGetData("MyApp.Settings", out AppSettings settings))
        Console.WriteLine($"Settings: {settings.Theme}");

    // Retrieve complex custom objects
    if (Clipboard.TryGetData("DocumentData", out DocumentInfo doc))
        Console.WriteLine($"Document: {doc.Title}");
}
Public Shared Sub CustomJsonTypesExamples()
    ' Retrieve a custom type stored with SetDataAsJson(Of T)()
    Dim person As Person = Nothing
    If Clipboard.TryGetData("Person", person) Then
        Console.WriteLine($"Person: {person.Name}")
    End If

    ' Retrieve application-specific data formats
    Dim settings As AppSettings = Nothing
    If Clipboard.TryGetData("MyApp.Settings", settings) Then
        Console.WriteLine($"Settings: {settings.Theme}")
    End If

    ' Retrieve complex custom objects
    Dim doc As DocumentInfo = Nothing
    If Clipboard.TryGetData("DocumentData", doc) Then
        Console.WriteLine($"Document: {doc.Title}")
    End If
End Sub

Metody SetDataAsJson<T>()

Te metody zapewniają automatyczną serializacji JSON przy użyciu System.Text.Json bezpiecznego magazynu typów.

Określanie formatu niestandardowego

public static void CustomFormatExample()
{
    var settings = new AppSettings { Theme = "Dark", AutoSave = true };

    // Use a custom format for better organization
    Clipboard.SetDataAsJson("MyApp.Settings", settings);
}
Public Shared Sub CustomFormatExample()
    Dim settings As New AppSettings With {.Theme = "Dark", .AutoSave = True}

    ' Use a custom format for better organization
    Clipboard.SetDataAsJson("MyApp.Settings", settings)
End Sub

Wnioskowanie automatycznego formatu

Określając nazwę typu jako format danych, TryGetData<T> można automatycznie wywnioskować format:

public static void AutomaticFormatInferenceExample()
{
    var person = new Person { Name = "Alice", Age = 25 };

    // Use the type name as the format
    Clipboard.SetDataAsJson(typeof(Person).FullName, person);

    // Retrieve the data and infer the format automatically
    if (Clipboard.TryGetData(out Person retrievedPerson))
    {
        Console.WriteLine($"Retrieved: {retrievedPerson.Name}");
    }
}
Public Shared Sub AutomaticFormatInferenceExample()
    Dim person As New Person With {.Name = "Alice", .Age = 25}

    ' Use the type name as the format
    Clipboard.SetDataAsJson(GetType(Person).FullName, person)

    ' Retrieve the data and infer the format automatically
    Dim retrievedPerson As Person = Nothing
    If Clipboard.TryGetData(retrievedPerson) Then
        Console.WriteLine($"Retrieved: {retrievedPerson.Name}")
    End If
End Sub

Interfejs ITypedDataObject

Interfejs ITypedDataObject umożliwia bezpieczne operacje przeciągania i upuszczania typu przez rozszerzenie IDataObject za pomocą metod typowych.

Począwszy od .NET 10, DataObject (często używany w scenariuszach drag-and-drop) implementuje ITypedDataObject.

Używanie obiektu ITypedDataObject w scenariuszach przeciągania i upuszczania

private void OnDragDrop(object sender, DragEventArgs e)
{
    if (e.Data is ITypedDataObject typedData)
    {
        // Retrieve files from drag data using a standard format
        if (typedData.TryGetData(DataFormats.FileDrop, out string[] files))
            Console.WriteLine($"Dropped files: {string.Join(", ", files)}");

        // Retrieve text using a standard format
        if (typedData.TryGetData(DataFormats.Text, out string text))
            Console.WriteLine($"Dropped text: {text}");

        // Retrieve custom items using an application-specific format
        if (typedData.TryGetData("CustomItem", out MyItem item))
            Console.WriteLine($"Dropped custom item: {item.Name} = {item.Value}");
    }
}
Private Sub OnDragDrop(sender As Object, e As DragEventArgs)
    If TypeOf e.Data Is ITypedDataObject Then
        Dim typedData As ITypedDataObject = CType(e.Data, ITypedDataObject)

        ' Retrieve files from drag data using a standard format
        Dim files As String() = Nothing
        If typedData.TryGetData(DataFormats.FileDrop, files) Then
            Console.WriteLine($"Dropped files: {String.Join(", ", files)}")
        End If

        ' Retrieve text using a standard format
        Dim text As String = Nothing
        If typedData.TryGetData(DataFormats.Text, text) Then
            Console.WriteLine($"Dropped text: {text}")
        End If

        ' Retrieve custom items using an application-specific format
        Dim item As MyItem = Nothing
        If typedData.TryGetData("CustomItem", item) Then
            Console.WriteLine($"Dropped custom item: {item.Name} = {item.Value}")
        End If
    End If
End Sub

Typy, które nie wymagają serializacji JSON

Wiele wbudowanych typów platformy .NET współpracuje z operacjami schowka bez konieczności serializacji JSON ani wsparcia BinaryFormatter. Te typy są automatycznie serializowane do binarnego formatu zdalnej komunikacji .NET (NRBF), co zapewnia efektywne przechowywanie i utrzymuje bezpieczeństwo typów.

Te rodzaje korzystają z NRBF, tego samego wydajnego formatu binarnego używanego przez starsze BinaryFormatter. Serializacja NRBF zapewnia następujące kluczowe korzyści:

  • Kompaktowa reprezentacja binarna: umożliwia wydajne przechowywanie i transfer.
  • Wbudowane informacje o typie: zachowuje dokładne typy platformy .NET podczas operacji okrężnych.
  • Zgodność między procesami: działa między różnymi aplikacjami platformy .NET.
  • Automatyczna serializacja: typy są serializowane bez potrzeby używania niestandardowego kodu.

Uwaga / Notatka

Metody takie jak SetData() wykonują operację kopiowania, która natychmiast serializuje dane, zapewniając, że dane schowka są zapisywane nawet po zakończeniu bieżącego procesu. Jeśli używasz typu DataObject bezpośrednio z SetDataObject(dataObject, copy), możesz kontrolować, kiedy nastąpi serializacja. Ustawienie parametru copy na true (wartość domyślna) wymusza natychmiastową serializację, podczas gdy false opóźnia serializację do momentu, gdy będzie potrzebna. Po ustawieniu copy na false i pobraniu danych w tym samym procesie, serializacja może nie być konieczna, ale dane nie będą utrwalane po zakończeniu tego procesu.

Aby uzyskać szczegółowe informacje techniczne, zobacz specyfikację formatu binarnego .NET Remoting.

Klasy obsługujące dane zakodowane w formacie NRBF są implementowane w System.Formats.Nrbf przestrzeni nazw.

Gwarancje bezpieczeństwa typów

Formularze systemu Windows udostępniają kilka mechanizmów bezpieczeństwa dla tych wbudowanych typów:

  • Dokładne dopasowywanie typów. TryGetData Zwraca tylko żądany typ.
  • Automatyczna walidacja. Windows Forms weryfikuje zgodność typów podczas deserializacji.
  • Nie ma dowolnego wykonania kodu. W przeciwieństwie do typów niestandardowych z binaryFormatter, te typy nie mogą wykonywać złośliwego kodu.
  • Wymagana jest weryfikacja zawartości. Musisz nadal weryfikować zawartość i zakresy danych dla logiki aplikacji.
  • Brak ograniczeń rozmiaru. Duże tablice lub mapy bitowe nie mają automatycznych ograniczeń. Monitorowanie użycia pamięci.

Obsługiwane typy pierwotne

Następujące typy pierwotne bezproblemowo współpracują ze schowkiem i DataObject operacjami. Nie potrzebujesz niestandardowej serializacji ani konfiguracji. System schowka automatycznie obsługuje następujące wbudowane typy platformy .NET:

  • bool, byte, char, decimal, double, short, int i long.
  • sbyte, , ushort, uint, ulong, floati string.
  • TimeSpan i DateTime.

W poniższych przykładach pokazano, jak te typy pierwotne działają bezpośrednio z metodami SetData() i :TryGetData<T>()

public static void PrimitiveTypesExample()
{
    // Numeric types
    Clipboard.SetData("MyInt", 42);
    Clipboard.SetData("MyDouble", 3.14159);
    Clipboard.SetData("MyDecimal", 123.45m);

    // Text and character types
    Clipboard.SetData("MyString", "Hello World");
    Clipboard.SetData("MyChar", 'A');

    // Boolean and date/time types
    Clipboard.SetData("MyBool", true);
    Clipboard.SetData("MyDateTime", DateTime.Now);
    Clipboard.SetData("MyTimeSpan", TimeSpan.FromMinutes(30));

    // Later retrieval with type safety
    if (Clipboard.TryGetData("MyTimeSpan", out TimeSpan value))
    {
        Console.WriteLine($"Clipboard value is: {value}");
    }
}
Public Shared Sub PrimitiveTypesExample()
    ' Numeric types
    Clipboard.SetData("MyInt", 42)
    Clipboard.SetData("MyDouble", 3.14159)
    Clipboard.SetData("MyDecimal", 123.45D)

    ' Text and character types
    Clipboard.SetData("MyString", "Hello World")
    Clipboard.SetData("MyChar", "A"c)

    ' Boolean and date/time types
    Clipboard.SetData("MyBool", True)
    Clipboard.SetData("MyDateTime", DateTime.Now)
    Clipboard.SetData("MyTimeSpan", TimeSpan.FromMinutes(30))

    ' Later retrieval with type safety
    Dim value As TimeSpan

    If Clipboard.TryGetData("MyTimeSpan", value) Then
        Console.WriteLine($"Clipboard value is: {value}")
    End If
End Sub

Kolekcje typów pierwotnych

Tablice i ogólne listy obsługiwanych typów pierwotnych działają bez dodatkowej konfiguracji. Należy jednak pamiętać o następujących ograniczeniach:

  • Wszystkie elementy tablicy i listy muszą być typami pierwotnymi.
  • Unikaj string[] i List<string> ponieważ format NRBF ma złożoność obsługi wartości null w kolekcjach ciągów.
  • Przechowuj ciągi pojedynczo lub używaj serializacji JSON dla kolekcji ciągów.

W poniższych przykładach pokazano, jak można ustawić tablice i listy w schowku:

public static void CollectionsExample()
{
    // Arrays of primitive types
    int[] numbers = { 1, 2, 3, 4, 5 };
    Clipboard.SetData("NumberArray", numbers);

    double[] coordinates = { 1.0, 2.5, 3.7 };
    Clipboard.SetData("Coordinates", coordinates);

    // Generic lists
    List<int> intList = new List<int> { 10, 20, 30 };
    Clipboard.SetData("IntList", intList);

    // Retrieval maintains type safety
    if (Clipboard.TryGetData("NumberArray", out int[] retrievedNumbers))
    {
        Console.WriteLine($"Numbers: {string.Join(", ", retrievedNumbers)}");
    }
}
Public Shared Sub CollectionsExample()
    ' Arrays of primitive types
    Dim numbers As Integer() = {1, 2, 3, 4, 5}
    Clipboard.SetData("NumberArray", numbers)

    Dim coordinates As Double() = {1.0, 2.5, 3.7}
    Clipboard.SetData("Coordinates", coordinates)

    ' Generic lists
    Dim intList As New List(Of Integer) From {10, 20, 30}
    Clipboard.SetData("IntList", intList)

    ' Retrieval maintains type safety
    Dim retrievedNumbers As Integer() = Nothing

    If Clipboard.TryGetData("NumberArray", retrievedNumbers) Then
        Console.WriteLine($"Numbers: {String.Join(", ", retrievedNumbers)}")
    End If
End Sub

Typy System.Drawing

Popularne typy grafiki z System.Drawing przestrzeni nazw działają bezproblemowo ze schowkiem i DataObject operacjami. Te typy są przydatne w przypadku aplikacji, które współpracują z elementami wizualnymi i muszą przesyłać dane związane z rysunkiem między składnikami lub aplikacjami. Należy pamiętać, że serializacja obiektu Bitmap może zużywać dużą ilość pamięci, szczególnie w przypadku dużych obrazów. Obsługiwane są następujące typy:

  • Point, PointF, Rectangle, . RectangleF
  • Size, SizeF, Color.
  • Bitmap (może zużywać znaczną ilość pamięci w przypadku serializacji).

W poniższych przykładach pokazano, jak te typy grafiki mogą być używane z operacjami schowka:

public static void SystemDrawingTypesExample()
{
    // Geometric types
    Point location = new Point(100, 200);
    Rectangle bounds = new Rectangle(0, 0, 500, 300);
    Size dimensions = new Size(800, 600);

    Clipboard.SetData("Location", location);
    Clipboard.SetData("Bounds", bounds);
    Clipboard.SetData("Size", dimensions);

    // Color information
    Color backgroundColor = Color.FromArgb(255, 128, 64, 192);
    Clipboard.SetData("BackColor", backgroundColor);

    // Bitmap data (use with caution for large images)
    Bitmap smallIcon = new Bitmap(16, 16);
    Clipboard.SetData("Icon", smallIcon);
}
Public Shared Sub SystemDrawingTypesExample()
    ' Geometric types
    Dim location As New Point(100, 200)
    Dim bounds As New Rectangle(0, 0, 500, 300)
    Dim dimensions As New Size(800, 600)

    Clipboard.SetData("Location", location)
    Clipboard.SetData("Bounds", bounds)
    Clipboard.SetData("Size", dimensions)

    ' Color information
    Dim backgroundColor As Color = Color.FromArgb(255, 128, 64, 192)
    Clipboard.SetData("BackColor", backgroundColor)

    ' Bitmap data (use with caution for large images)
    Dim smallIcon As New Bitmap(16, 16)
    Clipboard.SetData("Icon", smallIcon)
End Sub

Praca z typami niestandardowymi

Gdy używasz SetDataAsJson<T>(String, T) i TryGetData z typami niestandardowymi, System.Text.Json automatycznie obsługuje serializację. Wiele typów działa bez żadnej specjalnej konfiguracji — rekordy, proste klasy i struktury z właściwościami publicznymi serializują się bezproblemowo.

Proste typy, które działają bez atrybutów

Najprostsze typy niestandardowe nie wymagają specjalnej konfiguracji:

// Records work without any attributes.
public record PersonInfo(string Name, int Age, string Email);

// Simple classes serialize all public properties automatically.
public class DocumentMetadata
{
    public string Title { get; set; }
    public DateTime Created { get; set; }
    public string Author { get; set; }
}

// Structs with public properties work seamlessly.
public struct Point3D
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }
}
' Simple classes serialize all public properties automatically.
Public Class DocumentMetadata
    Public Property Title As String
    Public Property Created As DateTime
    Public Property Author As String
End Class

' Structs with public properties work seamlessly.
Public Structure Point3D
    Public Property X As Double
    Public Property Y As Double
    Public Property Z As Double
End Structure

Używanie atrybutów JSON do zaawansowanej kontroli

Użyj System.Text.Json atrybutów tylko wtedy, gdy trzeba dostosować zachowanie serializacji. Aby uzyskać kompleksowe wskazówki dotyczące System.Text.Json serializacji, atrybutów i zaawansowanych opcji konfiguracji, zobacz serializacji i deserializacji JSON na platformie .NET.

W poniższym przykładzie pokazano, jak można użyć atrybutów JSON do kontrolowania serializacji:

public class ClipboardFriendlyType
{
    // Include a field that normally isn't serialized
    [JsonInclude]
    private int _privateData;

    // Public properties are always serialized
    public string Name { get; set; }
    
    // Exclude sensitive or non-essential data
    [JsonIgnore]
    public string InternalId { get; set; }
    
    // Handle property name differences for compatibility
    [JsonPropertyName("display_text")]
    public string DisplayText { get; set; }
    
    // Control null value handling
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string OptionalField { get; set; }
}
Public Class ClipboardFriendlyType
    ' Include a field that normally isn't serialized
    <JsonInclude>
    Private _privateData As Integer

    ' Public properties are always serialized
    Public Property Name As String

    ' Exclude sensitive or non-essential data
    <JsonIgnore>
    Public Property InternalId As String

    ' Handle property name differences for compatibility
    <JsonPropertyName("display_text")>
    Public Property DisplayText As String

    ' Control null value handling
    <JsonIgnore(Condition:=JsonIgnoreCondition.WhenWritingNull)>
    Public Property OptionalField As String
End Class

Przykład: Operacje na schowku z typami niestandardowymi

public static void CustomTypesClipboardOperationsExample()
{
    var data = new ClipboardFriendlyType 
    { 
        Name = "Sample", 
        DisplayText = "Sample Display Text",
        InternalId = "internal-123" // This property isn't serialized due to [JsonIgnore]
    };

    Clipboard.SetDataAsJson("MyAppData", data);

    if (Clipboard.TryGetData("MyAppData", out ClipboardFriendlyType retrieved))
    {
        Console.WriteLine($"Retrieved: {retrieved.Name}");
        // retrieved.InternalId is null because of [JsonIgnore]
    }
}
Public Shared Sub CustomTypesClipboardOperationsExample()
    Dim data As New ClipboardFriendlyType With {
        .Name = "Sample",
        .DisplayText = "Sample Display Text",
        .InternalId = "internal-123" ' This property isn't serialized due to <JsonIgnore>
    }

    Clipboard.SetDataAsJson("MyAppData", data)

    Dim retrieved As ClipboardFriendlyType = Nothing
    If Clipboard.TryGetData("MyAppData", retrieved) Then
        Console.WriteLine($"Retrieved: {retrieved.Name}")
        ' retrieved.InternalId is null because of <JsonIgnore>
    End If
End Sub

Ostrzeżenie

BinaryFormatter pomoc techniczna nie jest zalecana. Używaj go tylko jako tymczasowego pomostu migracyjnego dla aplikacji starszego typu, które nie mogą natychmiast migrować do nowych typów interfejsów API bezpiecznych.

Jeśli musisz nadal używać BinaryFormatter do operacji schowka w .NET 10, włącz ograniczoną obsługę za pomocą jawnej konfiguracji. Takie podejście wiąże się ze znacznym ryzykiem bezpieczeństwa i wymaga kilku kroków.

Aby uzyskać pełne instrukcje krok po kroku, odnieś się do Włącz obsługę schowka BinaryFormatter (niezalecane). Aby uzyskać ogólne wskazówki dotyczące migracji, zobacz Przewodnik migracji BinaryFormatter.

Ostrzeżenia i zagrożenia dotyczące zabezpieczeń

BinaryFormatter jest z natury niezabezpieczony i przestarzały z następujących powodów:

  • Luki w zabezpieczeniach dotyczące dowolnego wykonywania kodu: osoby atakujące mogą wykonywać złośliwy kod podczas deserializacji, ujawniając aplikację na ataki zdalne.
  • Ataki typu "odmowa usługi": złośliwe dane schowka mogą zużywać nadmiarną ilość pamięci lub zasobów CPU, powodując awarie lub niestabilność.
  • Ryzyko ujawnienia informacji: osoby atakujące mogą wyodrębniać poufne dane z pamięci.
  • Brak granic zabezpieczeń: format jest zasadniczo niebezpieczny, a ustawienia konfiguracji nie mogą go zabezpieczyć.

Włącz tę obsługę jedynie jako tymczasowe rozwiązanie pomostowe podczas aktualizowania aplikacji, aby korzystać z nowych interfejsów API bezpiecznych typów.

Aby uzyskać szczegółowe wskazówki dotyczące zabezpieczeń i kroki konfiguracji, zobacz Ostrzeżenia i zagrożenia zabezpieczeń w przewodniku z instrukcjami.

Implementowanie rozpoznawania typów skoncentrowanych na zabezpieczeniach

Nawet jeśli BinaryFormatter jest włączone, należy zaimplementować rozpoznawanie typów, aby ograniczyć deserializację do jawnie zatwierdzonych typów. Postępuj zgodnie z następującymi wytycznymi:

  • Użyj jawnych list dozwolonych. Odrzuć dowolny typ, który nie został jawnie zatwierdzony.
  • Zweryfikuj nazwy typów. Upewnij się, że nazwy typów są dokładnie zgodne z oczekiwaną wartością.
  • Ogranicz do podstawowych typów. Uwzględnij tylko typy wymagane dla funkcji schowka.
  • Zgłaszanie wyjątków dla nieznanych typów. Wyraźnie odrzucaj nieautoryzowane typy.
  • Regularnie przeglądaj. Przeprowadź inspekcję i zaktualizuj listę dozwolonych zgodnie z potrzebami.

Aby zapoznać się z kompletnymi przykładami implementacji i przykładami kodu, zobacz Implementowanie rozpoznawania typów skoncentrowanych na zabezpieczeniach w przewodniku z instrukcjami.

Migrowanie kodu schowka za pomocą sztucznej inteligencji

Migrowanie operacji schowka z platformy .NET 8 do platformy .NET 10 obejmuje systematyczne zmiany kodu w wielu plikach i klasach. Narzędzia sztucznej inteligencji, takie jak GitHub Copilot, mogą pomóc przyspieszyć migrację, identyfikując starsze wzorce, sugerując nowoczesne zamiany i tworząc scenariusze testowe. Zamiast ręcznie przeszukiwać bazę kodu i konwertować poszczególne operacje schowka pojedynczo, możesz użyć sztucznej inteligencji do obsługi powtarzających się zadań, koncentrując się na weryfikowaniu wyników i obsłudze przypadków brzegowych.

W poniższych sekcjach przedstawiono konkretne strategie monitowania dotyczące różnych aspektów migracji schowka, od znajdowania problematycznych wzorców kodu po tworzenie niezawodnych typów serializowalnych w formacie JSON i kompleksowych zestawów testów.

Zastosowanie sztucznej inteligencji do identyfikacji wzorców starzejących się schowków.

Użyj narzędzia Copilot, aby zeskanować bazę kodu i zlokalizować operacje związane ze schowkiem, które wymagają migracji. Pomaga to zrozumieć zakres zmian wymaganych przed rozpoczęciem rzeczywistej pracy związanej z migracją.

Find all clipboard operations in my codebase that use GetData(), SetData() with custom objects, DataObject.GetData(), or IDataObject.GetData(). Show me the file paths and line numbers where these patterns occur.

Copilot jest obsługiwany przez sztuczną inteligencję, więc możliwe są niespodzianki i błędy. Aby uzyskać więcej informacji, zobacz także Copilot często zadawane pytania dotyczące ogólnego użytkowania.

Konwertowanie funkcji GetData() na tryGetData<T>() przy użyciu sztucznej inteligencji

Użyj narzędzia Copilot, aby przekonwertować przestarzałe GetData() wywołania na nowy wzorzec bezpieczny TryGetData<T>() dla typów. Ta konwersja obejmuje prawidłową obsługę błędów i eliminuje niebezpieczne rzutowanie.

Convert this GetData() clipboard code to use the new TryGetData<T>() method with proper error handling:

[paste your existing GetData() code here]

Make sure to eliminate casting and add appropriate error handling for when the data isn't available.

Copilot jest obsługiwany przez sztuczną inteligencję, więc możliwe są niespodzianki i błędy. Aby uzyskać więcej informacji, zobacz także Copilot często zadawane pytania dotyczące ogólnego użytkowania.

Migrowanie funkcji SetData() do funkcji SetDataAsJson<T>() przy użyciu sztucznej inteligencji

Użyj narzędzia Copilot, aby przekonwertować magazyn obiektów niestandardowych z przestarzałej SetData() metody na nowe SetDataAsJson<T>() podejście. Dzięki temu obiekty niestandardowe są prawidłowo serializowane do schowka.

Take this SetData() clipboard code that stores custom objects:

[paste your existing SetData() code here]

Convert it to use SetDataAsJson<T>() and make the custom types JSON-serializable. Add any necessary System.Text.Json attributes if the types have complex properties.

Copilot jest obsługiwany przez sztuczną inteligencję, więc możliwe są niespodzianki i błędy. Aby uzyskać więcej informacji, zobacz także Copilot często zadawane pytania dotyczące ogólnego użytkowania.

Tworzenie modeli danych z możliwością serializacji JSON za pomocą sztucznej inteligencji

Użyj narzędzia Copilot, aby zaprojektować typy niestandardowe, które bezproblemowo współpracują z elementami SetDataAsJson<T>() i TryGetData<T>(). Obejmuje to dodawanie odpowiednich atrybutów dla właściwości wymagających specjalnej obsługi.

Create a JSON-serializable version of this class for clipboard operations:

[paste your existing class definition here]

Make it work with System.Text.Json, add JsonIgnore for sensitive properties, JsonInclude for private fields that should serialize, and JsonPropertyName for any properties that need different names in JSON.

Copilot jest obsługiwany przez sztuczną inteligencję, więc możliwe są niespodzianki i błędy. Aby uzyskać więcej informacji, zobacz także Copilot często zadawane pytania dotyczące ogólnego użytkowania.

Wykorzystywanie sztucznej inteligencji do generowania typowanych bezpiecznie metod opakowujących.

Użyj narzędzia Copilot, aby utworzyć metody obudowy, które enkapsulują nowe interfejsy API dla schowka i dostarczają przejrzyste interfejsy dla określonych typów danych aplikacji.

Create a type-safe clipboard wrapper class that provides methods for storing and retrieving these custom types:

[list your custom types here]

Use SetDataAsJson<T>() and TryGetData<T>() internally, include proper error handling, and add methods like SavePersonToClipboard() and TryGetPersonFromClipboard().

Copilot jest obsługiwany przez sztuczną inteligencję, więc możliwe są niespodzianki i błędy. Aby uzyskać więcej informacji, zobacz także Copilot często zadawane pytania dotyczące ogólnego użytkowania.

Tworzenie kompleksowych testów przy użyciu sztucznej inteligencji

Użyj narzędzia Copilot, aby wygenerować zestawy testów sprawdzające poprawność migracji schowka, w tym testy serializacji w obie strony i scenariusze obsługi błędów.

Generate comprehensive unit tests for this clipboard code:

[paste your migrated clipboard code here]

Include tests for successful round-trip serialization, handling of null values, error cases when data isn't available, and verification that the migrated code produces the same results as the original for valid scenarios.

Copilot jest obsługiwany przez sztuczną inteligencję, więc możliwe są niespodzianki i błędy. Aby uzyskać więcej informacji, zobacz także Copilot często zadawane pytania dotyczące ogólnego użytkowania.

Weryfikowanie wyników migracji za pomocą sztucznej inteligencji

Użyj narzędzia Copilot, aby przejrzeć zmigrowany kod i zidentyfikować potencjalne problemy lub obszary, w których migracja może nie zostać ukończona.

Review this migrated clipboard code for potential issues:

[paste your migrated code here]

Check for: missing error handling, types that might not serialize properly to JSON, performance concerns with large objects, security issues, and any remaining uses of obsolete methods.

Copilot jest obsługiwany przez sztuczną inteligencję, więc możliwe są niespodzianki i błędy. Aby uzyskać więcej informacji, zobacz także Copilot często zadawane pytania dotyczące ogólnego użytkowania.