本文介绍如何将 Windows 窗体剪贴板和拖放操作升级到 .NET 10 中的新类型安全 API。 你将了解如何使用新的 Clipboard.TryGetData 和 Clipboard.SetDataAsJson<T>(String, T) 方法,了解哪些内置类型在没有更改的情况下工作,并在删除 BinaryFormatter后发现用于处理自定义类型和旧数据的策略。
BinaryFormatter 由于安全漏洞,已从 .NET 9 中的运行时中删除。 此更改导致剪贴板与自定义对象的拖放操作失效。 .NET 10 引入了新的 API,这些 API 使用 JSON 序列化和类型安全方法还原此功能、提高安全性,并提供更好的错误处理和跨进程兼容性。
Clipboard.SetData(String, Object) 不再适用于需要 BinaryFormatter 序列化的自定义类型。 此方法执行复制操作(默认情况下),该操作会立即序列化数据,但由于需要 BinaryFormatter 的类型已被移除,现在该序列化失败。
Clipboard.GetData(String) 在 .NET 10 中已过时。 当反序列化需要BinaryFormatter但未启用BinaryFormatter时,GetData() 返回一个NotSupportedException实例。 使用新的 Clipboard.TryGetData 方法和 Clipboard.SetDataAsJson<T>(String, T) 方法来对自定义对象进行类型安全操作和 JSON 序列化。
以下部分提供了详细的迁移指南,介绍了哪些类型无需更改即可工作,并演示如何处理新的开发和旧数据方案。
先决条件
在继续之前,请查看以下概念:
- 在 .NET 9 之前,应用程序在剪贴板和拖放方案中如何使用
BinaryFormatter。 - 导致删除的
BinaryFormatter安全漏洞。 - 如何使用
System.Text.Json序列化模式及其限制。
有关详细信息,请参阅以下文章:
因删除 BinaryFormatter 而导致的重大变更
在 .NET 9 中移除BinaryFormatter从根本上改变了 Windows 窗体对剪贴板和自定义类型拖放操作的处理方式。 这些更改会影响现有代码模式,需要仔细迁移才能维护功能。
自定义类型不再自动序列化
在 .NET 8 及更早版本中,可以通过调用 SetData()在剪贴板上放置任何可序列化的自定义对象。
BinaryFormatter 自动处理序列化。 从 .NET 9 开始,SetData() 仍执行立即序列化数据的复制作业,但对于需要 BinaryFormatter 的类型,由于其已被移除,序列化将失败。
以下代码不再有效:
[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
你可能会看到的内容
- 该方法
SetData()无需引发异常即可完成。 - 数据被放置在剪贴板上,但对于需要
BinaryFormatter进行序列化的类型,序列化会失败。 - 稍后尝试使用
GetData()检索数据时,返回的 NotSupportedException 实例指示BinaryFormatter是必需的,但未启用。
迁移指南
使用新 SetDataAsJson<T>() 方法或手动序列化为 string 或 byte[]。 有关详细信息,请参阅“ 使用自定义类型 ”部分。
GetData() 已过时 - 请改用 TryGetData<T>()
旧 GetData() 方法在 .NET 10 中已过时。 此方法在大多数情况下可以成功返回数据,但当反序列化需要 BinaryFormatter 并且未启用时,GetData() 将返回一个指示需要 BinaryFormatter 的 NotSupportedException 实例。 应迁移到新的类型安全 TryGetData<T>() 方法,以便更好地处理错误和类型安全性。
以下示例显示了应避免的过时模式:
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
请改用新式类型安全方法: 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
TryGetData<T>() 的优点
- 类型安全性:无需强制转换,该方法将返回请求的确切类型。
- 清除错误处理:返回布尔成功指示器,而不是使用 null 模式或异常模式。
- 面向未来:旨在兼容新序列化方法和支持旧数据。
如何识别受影响的代码
查找:
- 任何
GetData()调用,因为无论数据类型如何,整个方法都已过时。 -
DataObject.GetData()和IDataObject.GetData()在拖放操作中的用法。
迁移指南
将所有 GetData() 用法替换为类型安全 TryGetData<T>() 的方法。 有关所有重载的综合示例,请参阅 “新建类型安全 API” 部分。
新型类型安全的 API
.NET 10 引入了三个新的 API 系列,这些系列为剪贴板和拖放作提供类型安全性、更好的错误处理和 JSON 序列化支持:
- TryGetData 用于检索数据的方法
- SetDataAsJson<T>(String, T) 用于存储数据的方法
- ITypedDataObject 拖放操作的接口
TryGetData<T>() 方法
该 TryGetData<T>() 系列取代了过时 GetData() 的方法。 它为剪贴板操作提供类型安全的检索,并明确显示操作的成功或失败。
基本类型安全检索
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
自定义 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
SetDataAsJson<T>() 方法
这些方法使用System.Text.Json,通过类型安全存储提供自动的 JSON 序列化。
指定自定义格式
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
自动格式推理
通过将类型名称指定为数据格式, TryGetData<T> 可以自动推断格式:
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
ITypedDataObject 接口
该ITypedDataObject 接口通过扩展IDataObject并使用类型化方法,使其能够进行类型安全的拖放操作。
从 .NET 10 开始,DataObject(拖放场景中的常用类型)实现 ITypedDataObject。
在拖放方案中使用 ITypedDataObject
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
不需要 JSON 序列化的类型
许多内置的 .NET 类型可用于剪贴板操作,而无需 JSON 序列化或 BinaryFormatter 支持。 这些类型会自动序列化为 .NET 远程处理二进制格式(NRBF),该格式提供高效的存储和维护类型安全性。
这些类型使用 NRBF,这是旧版 BinaryFormatter使用的相同高效二进制格式。 NRBF 序列化提供以下关键优势:
- 压缩二进制表示形式:实现高效的存储和传输。
- 内置类型信息:在往返操作期间保留确切的 .NET 类型。
- 跨进程兼容性:适用于不同的 .NET 应用程序。
- 自动序列化:类型在没有自定义代码的情况下序列化。
注释
如 SetData() 的方法执行复制操作以立即序列化数据,确保即使当前进程退出,剪贴板数据仍然保留。 如果直接使用类型 DataObject 和 SetDataObject(dataObject, copy),则可以控制序列化何时发生。 将 copy 参数设置为 true (默认值)会强制立即序列化,同时 false 延迟序列化,直到需要。 如果copy设置为false,并且数据在相同的进程中检索,则可能不需要进行序列化,但在进程退出后数据不会持久保存。
有关技术详细信息,请参阅 .NET 远程处理二进制格式规范。
在System.Formats.Nrbf命名空间中实现了支持 NRBF 编码数据的类。
类型安全保证
Windows 窗体为这些内置类型提供了多种安全机制:
- 精确类型匹配。 TryGetData 仅返回请求的类型。
- 自动验证。 Windows 窗体在反序列化期间验证类型兼容性。
- 没有任意代码执行。 与使用 BinaryFormatter 的自定义类型不同,这些类型无法执行恶意代码。
- 需要内容验证。 仍必须验证应用程序逻辑的数据内容和范围。
- 没有大小约束。 大型数组或位图不会自动受到限制。 监视内存使用情况。
支持的基元类型
以下基元类型可与剪贴板和 DataObject 作无缝配合工作。 不需要自定义序列化或配置。 剪贴板系统会自动处理这些内置 .NET 类型:
-
bool、byte、char、decimal、double、short、int和long。 -
sbyte、ushort、uint、ulong、float和string。 -
TimeSpan和DateTime。
以下示例演示了这些基元类型如何直接使用 SetData() 和 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
基元类型的集合
支持的基元类型的数组和泛型列表无需额外配置即可工作。 但是,请记住以下限制:
- 所有数组和列表元素都必须支持基元类型。
- 避免
string[]和List<string>,因为 NRBF 格式在处理字符串集合中的 null 值时具有复杂性。 - 单独存储字符串,或使用字符串集合的 JSON 序列化。
以下示例演示如何在剪贴板上设置数组和列表:
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
System.Drawing 类型
命名空间中的 System.Drawing 常见图形类型可与剪贴板和 DataObject 作无缝配合工作。 这些类型对于处理视觉元素且需要在组件或应用程序之间传输与绘图相关的数据的应用程序非常有用。 请注意,序列化 a Bitmap 可能会占用大量内存,尤其是对于大型图像。 支持以下类型:
-
Point、PointF、Rectangle、RectangleF。 -
Size,SizeF.Color -
Bitmap(序列化时可能会消耗大量内存)。
以下示例演示如何在剪贴板操作中使用这些图形类型:
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
使用自定义类型
使用 SetDataAsJson<T>(String, T) 和 TryGetData 自定义类型时,System.Text.Json 会自动处理序列化。 许多类型无需任何特殊配置即可工作, 记录、简单类和具有公共属性的结构无缝序列化。
不带属性的简单类型
最直接的自定义类型不需要特殊配置:
// 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
使用 JSON 属性进行高级控制
仅当需要自定义序列化行为时,才使用 System.Text.Json 属性。 有关序列化、属性和高级配置选项的综合指南 System.Text.Json ,请参阅 .NET 中的 JSON 序列化和反序列化。
以下示例演示如何使用 JSON 属性来控制序列化:
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
示例:使用自定义类型的剪贴板操作
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
启用 BinaryFormatter 支持(不建议)
注意
BinaryFormatter
不建议支持。 仅将其用作无法立即迁移到新类型安全 API 的旧应用程序的临时迁移桥。
如果必须继续在 .NET 10 中使用 BinaryFormatter 进行剪贴板操作,请通过显式配置启用有限的支持。 此方法具有重大安全风险,需要执行几个步骤。
有关完整的分步说明,请参阅 “启用 BinaryFormatter 剪贴板支持”(不建议这样做)。 有关常规迁移指南,请参阅 BinaryFormatter 迁移指南。
安全警告和风险
BinaryFormatter 出于以下原因,本质上不安全且已弃用:
- 任意代码执行漏洞:攻击者可以在反序列化期间执行恶意代码,向远程攻击公开应用程序。
- 拒绝服务攻击:恶意剪贴板数据可能会消耗过多的内存或 CPU 资源,从而导致崩溃或不稳定。
- 信息泄露风险:攻击者可能会从内存中提取敏感数据。
- 无安全边界:格式从根本上不安全,配置设置无法保护它。
仅在更新应用程序以使用新类型安全 API 时将此支持作为临时网桥启用。
有关详细的安全指南和配置步骤,请参阅作指南中的 安全警告和风险 。
实现以安全为中心的类型解析程序
即使启用了BinaryFormatter,您也必须实现类型解析程序,以将反序列化限制为显式批准的类型。 请遵循这些指导:
- 使用显式允许列表。 拒绝任何未经明确批准的类型。
- 验证类型名称。 确保类型名称与预期值完全匹配。
- 限制为必要类型。 仅包括剪贴板功能所需的类型。
- 对未知类型抛出异常。 明确拒绝未经授权的类型。
- 定期审阅。 根据需要审核和更新允许的列表。
有关完整的实现示例和代码示例,请参阅作指南中的 “实现以安全为中心的类型解析程序 ”。
使用 AI 迁移剪贴板代码
将剪贴板操作从 .NET 8 迁移到 .NET 10 包括跨多个文件和类的系统代码更改。 GitHub Copilot 等 AI 工具可以通过识别旧模式、建议新式替换和创建测试方案来帮助加速迁移。 与其手动在代码库中搜索并逐个处理每个剪贴板操作,不如使用 AI 来处理重复任务,同时您可以专注于验证结果和处理边缘情况。
以下部分显示了剪贴板迁移的不同方面的具体提示策略,从查找有问题的代码模式到创建可靠的 JSON 可序列化类型和全面的测试套件。
使用 AI 识别旧式剪贴板模式
使用 Copilot 扫描代码库并查找需要迁移的剪贴板操作。 这有助于了解开始实际迁移工作之前所需的更改范围。
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 由 AI 提供支持,因此可能会带来意外和错误。 有关详细信息,请参阅 Copilot 常规使用常见问题解答。
使用 AI 将 GetData() 转换为 TryGetData<T>()
使用 Copilot 将过时的 GetData() 调用转换为新的类型安全的 TryGetData<T>() 模式。 此转换包括正确的错误处理和消除不安全的强制转换。
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 由 AI 提供支持,因此可能会带来意外和错误。 有关详细信息,请参阅 Copilot 常规使用常见问题解答。
使用 AI 将 SetData() 迁移到 SetDataAsJson<T>()
使用 Copilot 将自定义对象存储从过时 SetData() 的方法转换为新 SetDataAsJson<T>() 方法。 这可确保自定义对象正确序列化到剪贴板。
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 由 AI 提供支持,因此可能会带来意外和错误。 有关详细信息,请参阅 Copilot 常规使用常见问题解答。
使用 AI 创建 JSON 可序列化的数据模型
使用 Copilot 设计可与SetDataAsJson<T>()和TryGetData<T>()无缝配合的自定义类型。 这包括为需要特殊处理的属性添加适当的属性。
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 由 AI 提供支持,因此可能会带来意外和错误。 有关详细信息,请参阅 Copilot 常规使用常见问题解答。
使用 AI 生成类型安全的包装方法
使用 Copilot 创建包装器方法,用于封装新的剪贴板 API 并为应用程序的特定数据类型提供干净的接口。
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 由 AI 提供支持,因此可能会带来意外和错误。 有关详细信息,请参阅 Copilot 常规使用常见问题解答。
使用 AI 创建全面的测试
使用 Copilot 生成测试套件,用于验证剪贴板迁移是否正常工作,包括往返序列化测试和错误处理方案。
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 由 AI 提供支持,因此可能会带来意外和错误。 有关详细信息,请参阅 Copilot 常规使用常见问题解答。
使用 AI 验证迁移结果
使用 Copilot 查看迁移的代码,并确定迁移可能未完成的潜在问题或区域。
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 由 AI 提供支持,因此可能会带来意外和错误。 有关详细信息,请参阅 Copilot 常规使用常见问题解答。