Skip to content

Commit

Permalink
The existing GetData methods now call the new ones. TryGetData method…
Browse files Browse the repository at this point in the history
…s will fall back to use BinaryFormatter if a binder is provided. The GetData methods will use a binder that allows BInaryFormater to resolve all types is the application sets EnableUnsafeBinaryFormatterDeserialization AppContext switch to true, by default this switch is false.
  • Loading branch information
Tanya-Solyanik committed Jun 22, 2024
1 parent e462a84 commit 816f4c9
Show file tree
Hide file tree
Showing 24 changed files with 1,257 additions and 158 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<Copyright>$(CopyrightNetFoundation)</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<LangVersion>preview</LangVersion>
<LangVersion Condition="'$(LangVersion)' == ''">preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>

Expand Down
9 changes: 8 additions & 1 deletion src/Common/tests/TestUtilities/AppContextSwitchNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ namespace System;

public static class AppContextSwitchNames
{
/// <summary>
/// The switch that controls whether or not the <see cref="BinaryFormatter"/> is enabled in the Clipboard.
/// </summary>
public const string ClipboardEnableUnsafeBinaryFormatterDeserializationSwitchName =
"System.Windows.Forms.Clipboard.EnableUnsafeBinaryFormatterDeserialization";

/// <summary>
/// The switch that controls whether or not the <see cref="BinaryFormatter"/> is enabled.
/// </summary>
public static string EnableUnsafeBinaryFormatterSerialization { get; }
= "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization";

/// <summary>
/// Switch that controls <see cref="AppContext"/> switch caching.
/// Switch that controls <see cref="AppContext"/> switch caching. This switch is set to
/// <see langword="true" /> in our test assemblies.
/// </summary>
public static string LocalAppContext_DisableCaching { get; }
= "TestSwitch.LocalAppContext.DisableCaching";
Expand Down
27 changes: 27 additions & 0 deletions src/Common/tests/TestUtilities/BinaryFormatterInClipboardScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System;

public readonly ref struct BinaryFormatterInClipboardScope
{
private readonly AppContextSwitchScope _switchScope;

public BinaryFormatterInClipboardScope(bool enable)
{
Monitor.Enter(typeof(BinaryFormatterInClipboardScope));
_switchScope = new(AppContextSwitchNames.ClipboardEnableUnsafeBinaryFormatterDeserializationSwitchName, enable);
}

public void Dispose()
{
try
{
_switchScope.Dispose();
}
finally
{
Monitor.Exit(typeof(BinaryFormatterInClipboardScope));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ Option Strict On

Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.Diagnostics.CodeAnalysis
Imports System.Drawing
Imports System.IO
Imports System.Reflection.Metadata
Imports System.Runtime.InteropServices
Imports System.Runtime.Serialization
Imports System.Windows.Forms

Namespace Microsoft.VisualBasic.MyServices
Expand Down Expand Up @@ -38,7 +42,7 @@ Namespace Microsoft.VisualBasic.MyServices
''' </summary>
''' <param name="format">The type of text to get</param>
''' <returns>The text as a String</returns>
Public Function GetText(format As System.Windows.Forms.TextDataFormat) As String
Public Function GetText(format As TextDataFormat) As String
Return Clipboard.GetText(format)
End Function

Expand All @@ -56,7 +60,7 @@ Namespace Microsoft.VisualBasic.MyServices
''' </summary>
''' <param name="format">The type of text being checked for</param>
''' <returns>True if text is available, otherwise False</returns>
Public Function ContainsText(format As System.Windows.Forms.TextDataFormat) As Boolean
Public Function ContainsText(format As TextDataFormat) As Boolean
Return Clipboard.ContainsText(format)
End Function

Expand All @@ -73,7 +77,7 @@ Namespace Microsoft.VisualBasic.MyServices
''' </summary>
''' <param name="text">The String to save</param>
''' <param name="format">The format in which to save the String</param>
Public Sub SetText(text As String, format As System.Windows.Forms.TextDataFormat)
Public Sub SetText(text As String, format As TextDataFormat)
Clipboard.SetText(text, format)
End Sub

Expand Down Expand Up @@ -166,6 +170,33 @@ Namespace Microsoft.VisualBasic.MyServices
Return Clipboard.GetData(format)
End Function

''' <summary>
''' Gets data of type <typeparamref name="T"/> from the clipboard that's been saved in the passed in format.
''' </summary>
''' <param name="format">The format in which to search for the data of type <typeparamref name="T"/>. </param>
''' <param name="data">The retrieved data, is successful.</param>
''' <param name="resolver"> used to control BinaryFormatter deserialization if needed.</param>
Public Function TryGetData(Of T)(format As String, resolver As Func(Of TypeName, Type), <Out> ByRef data As T) As Boolean
Return Clipboard.TryGetData(format, resolver, data)
End Function

''' <summary>
''' Gets data of type <typeparamref name="T"/> from the clipboard that's been saved in the passed in format.
''' </summary>
''' <param name="format">The format in which to search for the data of type <typeparamref name="T"/>. </param>
''' <param name="data">The retrieved data, is successful.</param>
Public Function TryGetData(Of T)(format As String, <Out> ByRef data As T) As Boolean
Return Clipboard.TryGetData(format, data)
End Function

''' <summary>
''' Gets data of type <typeparamref name="T"/> from the clipboard.
''' </summary>
''' <param name="data">The retrieved data, if successful.</param>
Public Function TryGetData(Of T)(<Out> ByRef data As T) As Boolean
Return Clipboard.TryGetData(data)
End Function

''' <summary>
''' Indicates whether or not there is data on the clipboard in the passed in format
''' </summary>
Expand All @@ -184,6 +215,14 @@ Namespace Microsoft.VisualBasic.MyServices
Clipboard.SetData(format, data)
End Sub

''' <summary>
''' Saves the passed in data to the clipboard as JSON that contains <typeparamref name="T"/>.
''' </summary>
''' <param name="data">The data to be saved.</param>
Public Sub SetDataAsJson(Of T)(data As T)
Clipboard.SetDataAsJson(data)
End Sub

''' <summary>
''' Removes everything from the clipboard
''' </summary>
Expand All @@ -197,7 +236,7 @@ Namespace Microsoft.VisualBasic.MyServices
''' <returns>The data object</returns>
''' <remarks>This gives the ability to save an object in multiple formats</remarks>
<EditorBrowsable(EditorBrowsableState.Advanced)>
Public Function GetDataObject() As System.Windows.Forms.IDataObject
Public Function GetDataObject() As IDataObject
Return Clipboard.GetDataObject()
End Function

Expand All @@ -207,7 +246,7 @@ Namespace Microsoft.VisualBasic.MyServices
''' <param name="data">The data object to be saved</param>
''' <remarks>This gives the ability to save an object in multiple formats</remarks>
<EditorBrowsable(EditorBrowsableState.Advanced)>
Public Sub SetDataObject(data As System.Windows.Forms.DataObject)
Public Sub SetDataObject(data As DataObject)
Clipboard.SetDataObject(data)
End Sub
End Class
Expand Down
4 changes: 4 additions & 0 deletions src/Microsoft.VisualBasic.Forms/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Microsoft.VisualBasic.MyServices.ClipboardProxy.SetDataAsJson(Of T)(data As T) -> Void
Microsoft.VisualBasic.MyServices.ClipboardProxy.TryGetData(Of T)(ByRef data As T) -> Boolean
Microsoft.VisualBasic.MyServices.ClipboardProxy.TryGetData(Of T)(format As String, ByRef data As T) -> Boolean
Microsoft.VisualBasic.MyServices.ClipboardProxy.TryGetData(Of T)(format As String, resolver As System.Func(Of System.Reflection.Metadata.TypeName, System.Type), ByRef data As T) -> Boolean
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System.Drawing;
using System.Reflection.Metadata;
using System.Windows.Forms;
using FluentAssertions;
using Microsoft.VisualBasic.Devices;
using DataFormats = System.Windows.Forms.DataFormats;
using TextDataFormat = System.Windows.Forms.TextDataFormat;
Expand All @@ -14,41 +19,41 @@ public class ClipboardProxyTests
[WinFormsFact]
public void Clear()
{
var clipboard = (new Computer()).Clipboard;
var clipboard = new Computer().Clipboard;
string text = GetUniqueText();
clipboard.SetText(text);
Assert.True(System.Windows.Forms.Clipboard.ContainsText());
Assert.True(Clipboard.ContainsText());
clipboard.Clear();
Assert.False(System.Windows.Forms.Clipboard.ContainsText());
Assert.False(Clipboard.ContainsText());
}

[WinFormsFact]
public void Text()
{
var clipboard = (new Computer()).Clipboard;
var clipboard = new Computer().Clipboard;
string text = GetUniqueText();
clipboard.SetText(text, TextDataFormat.UnicodeText);
Assert.Equal(System.Windows.Forms.Clipboard.ContainsText(), clipboard.ContainsText());
Assert.Equal(System.Windows.Forms.Clipboard.GetText(), clipboard.GetText());
Assert.Equal(System.Windows.Forms.Clipboard.GetText(TextDataFormat.UnicodeText), clipboard.GetText(TextDataFormat.UnicodeText));
Assert.Equal(Clipboard.ContainsText(), clipboard.ContainsText());
Assert.Equal(Clipboard.GetText(), clipboard.GetText());
Assert.Equal(Clipboard.GetText(TextDataFormat.UnicodeText), clipboard.GetText(TextDataFormat.UnicodeText));
Assert.Equal(text, clipboard.GetText(TextDataFormat.UnicodeText));
}

[WinFormsFact]
public void Image()
{
var clipboard = (new Computer()).Clipboard;
Bitmap image = new(2, 2);
Assert.Equal(System.Windows.Forms.Clipboard.ContainsImage(), clipboard.ContainsImage());
Assert.Equal(System.Windows.Forms.Clipboard.GetImage(), clipboard.GetImage());
var clipboard = new Computer().Clipboard;
using Bitmap image = new(2, 2);
Assert.Equal(Clipboard.ContainsImage(), clipboard.ContainsImage());
Assert.Equal(Clipboard.GetImage(), clipboard.GetImage());
clipboard.SetImage(image);
}

[WinFormsFact]
public void Audio()
{
var clipboard = (new Computer()).Clipboard;
Assert.Equal(System.Windows.Forms.Clipboard.ContainsAudio(), clipboard.ContainsAudio());
var clipboard = new Computer().Clipboard;
Assert.Equal(Clipboard.ContainsAudio(), clipboard.ContainsAudio());
// Not tested:
// Public Function GetAudioStream() As Stream
// Public Sub SetAudio(ByVal audioBytes As Byte())
Expand All @@ -58,8 +63,8 @@ public void Audio()
[WinFormsFact]
public void FileDropList()
{
var clipboard = (new Computer()).Clipboard;
Assert.Equal(System.Windows.Forms.Clipboard.ContainsFileDropList(), clipboard.ContainsFileDropList());
var clipboard = new Computer().Clipboard;
Assert.Equal(Clipboard.ContainsFileDropList(), clipboard.ContainsFileDropList());
// Not tested:
// Public Function GetFileDropList() As StringCollection
// Public Sub SetFileDropList(ByVal filePaths As StringCollection)
Expand All @@ -68,21 +73,89 @@ public void FileDropList()
[WinFormsFact]
public void Data()
{
var clipboard = (new Computer()).Clipboard;
object data = GetUniqueText();
Assert.Equal(System.Windows.Forms.Clipboard.ContainsData(DataFormats.UnicodeText), clipboard.ContainsData(DataFormats.UnicodeText));
Assert.Equal(System.Windows.Forms.Clipboard.GetData(DataFormats.UnicodeText), clipboard.GetData(DataFormats.UnicodeText));
var clipboard = new Computer().Clipboard;
string data = GetUniqueText();
clipboard.SetData(DataFormats.UnicodeText, data);

Clipboard.ContainsData(DataFormats.UnicodeText).Should().Be(clipboard.ContainsData(DataFormats.UnicodeText));
using BinaryFormatterInClipboardScope clipboardScope = new(enable: true);
Clipboard.GetData(DataFormats.UnicodeText).Should().Be(Clipboard.GetData(DataFormats.UnicodeText));
}

[WinFormsFact]
public void DataOfT_BinaryFormatterDisabled_Fail()
{
var clipboard = new Computer().Clipboard;
clipboard.SetDataAsJson(new Button());

clipboard.TryGetData(out Button? result).Should().BeFalse();
}

[WinFormsFact]
public void Data_SetOfT_NotSupported()
{
var clipboard = new Computer().Clipboard;
TestData data = new("thing1", "thing2");
// BinaryFormatter is off, thus we'll serialize an exception message to the Clipboard.
clipboard.SetDataAsJson(data);

clipboard.TryGetData(typeof(TestData).FullName!, out TestData? result).Should().BeFalse();
// TanyaSo what to do with "not supported" message, I'm eating it up
result.Should().BeNull();
}

[WinFormsFact]
public void Data_SetOfT_Success()
{
var clipboard = new Computer().Clipboard;
TestData data = new("thing1", "thing2");
using (BinaryFormatterScope scope = new(enable: true))
{
clipboard.SetDataAsJson(data);
}

clipboard.TryGetData(typeof(TestData).FullName!, TestDataResolver, out TestData? result).Should().BeTrue();
result.Should().BeEquivalentTo(data);
}

[WinFormsFact]
public void DataObject()
{
var clipboard = (new Computer()).Clipboard;
object data = GetUniqueText();
Assert.Equal(System.Windows.Forms.Clipboard.GetDataObject().GetData(DataFormats.UnicodeText), clipboard.GetDataObject().GetData(DataFormats.UnicodeText));
clipboard.SetDataObject(new System.Windows.Forms.DataObject(data));
var clipboard = new Computer().Clipboard;
string data = GetUniqueText();
clipboard.SetDataObject(new DataObject(data));

Clipboard.GetDataObject()!.GetData(DataFormats.UnicodeText).Should().Be(clipboard.GetDataObject().GetData(DataFormats.UnicodeText));
}

private static string GetUniqueText() => Guid.NewGuid().ToString("D");

[Serializable]
private class TestData
{
public TestData(string text1, string text2)
{
_text1 = text1;
_text2 = text2;
}

public string _text1;
public string _text2;
}

private static Type TestDataResolver(TypeName typeName)
{
Type type = typeof(TestData);
TypeName parsed = TypeName.Parse($"{type.FullName}, {type.Assembly.FullName}");

// Namespace-qualified type name.
if (typeName.FullName == parsed.FullName
// Ignore version, culture, and public key token in the assembly name.
&& typeName.AssemblyName?.Name == parsed.AssemblyName?.Name)
{
return type;
}

throw new NotSupportedException($"Unexpected type {typeName.AssemblyQualifiedName}.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ static bool Get(BinaryFormattedObject format, [NotNullWhen(true)] out object? va
}

/// <summary>
/// Trys to get this object as a primitive type or string.
/// Tries to get this object as a primitive type or string.
/// </summary>
/// <returns><see langword="true"/> if this represented a primitive type or string.</returns>
public static bool TryGetPrimitiveType(this BinaryFormattedObject format, [NotNullWhen(true)] out object? value)
Expand Down
Loading

0 comments on commit 816f4c9

Please sign in to comment.