Skip to content

Commit 2bfb292

Browse files
committed
Don't unwrap wrapped DataObject inner data store (#12800)
When creating a DataObject from another DataObject we need to keep the original DataObject as the "inner" data so that virtuals get called as they historically have. Also pass through to correct overload on TryGetDataCore. Fixes #12789
1 parent 69636da commit 2bfb292

File tree

3 files changed

+103
-5
lines changed

3 files changed

+103
-5
lines changed

src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,7 @@ public DataObject()
4545
/// </summary>
4646
public DataObject(object data)
4747
{
48-
if (data is DataObject dataObject)
49-
{
50-
_innerData = dataObject._innerData;
51-
}
52-
else if (data is IDataObject iDataObject)
48+
if (data is IDataObject iDataObject)
5349
{
5450
_innerData = Composition.CreateFromWinFormsDataObject(iDataObject);
5551
}

src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ClipboardTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,4 +699,33 @@ public void Clipboard_SetDataObject_Text()
699699
byte[] array = stream.ToArray();
700700
array.Should().BeEquivalentTo("Hello, World!\0"u8.ToArray());
701701
}
702+
703+
[WinFormsFact]
704+
public void Clipboard_DerivedDataObject_DataPresent()
705+
{
706+
// https://github.com/dotnet/winforms/issues/12789
707+
SomeDataObject data = new();
708+
709+
// This was provided as a workaround for the above and should not break, but should
710+
// also work without it.
711+
data.SetData(SomeDataObject.Format, data);
712+
713+
Clipboard.SetDataObject(data);
714+
Clipboard.ContainsData(SomeDataObject.Format).Should().BeTrue();
715+
Clipboard.GetDataObject()!.GetDataPresent(SomeDataObject.Format).Should().BeTrue();
716+
717+
data = new();
718+
Clipboard.SetDataObject(data);
719+
Clipboard.ContainsData(SomeDataObject.Format).Should().BeTrue();
720+
Clipboard.GetDataObject()!.GetDataPresent(SomeDataObject.Format).Should().BeTrue();
721+
}
722+
723+
public class SomeDataObject : DataObject
724+
{
725+
public static string Format => "SomeDataObjectId";
726+
public override string[] GetFormats() => [Format];
727+
728+
public override bool GetDataPresent(string format, bool autoConvert)
729+
=> format == Format || base.GetDataPresent(format, autoConvert);
730+
}
702731
}

src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DataObjectTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2657,4 +2657,77 @@ public unsafe void DataObject_ComTypesIDataObject_MockRoundTrip_IsWrapped()
26572657
IDataObject outData = dropTargetAccessor.CreateDelegate<CreateWinFormsDataObjectForOutgoingDropData>()(inDataPtr);
26582658
outData.Should().BeSameAs(inData);
26592659
}
2660+
2661+
[Fact]
2662+
public void DataObject_CreateFromDataObject_DoesNotUnwrapDataStore()
2663+
{
2664+
// The inner data should not have it's data store unwrapped.
2665+
DataObject dataObject = new();
2666+
DataObject wrapped = new(dataObject);
2667+
DataObject.Composition composition = wrapped.TestAccessor().Dynamic._innerData;
2668+
IDataObject original = composition.TestAccessor().Dynamic._winFormsDataObject;
2669+
original.Should().BeSameAs(dataObject);
2670+
}
2671+
2672+
[Fact]
2673+
public void DataObject_CreateFromDataObject_VirtualsAreCalled()
2674+
{
2675+
Mock<DataObject> mock = new(MockBehavior.Loose);
2676+
DataObject wrapped = new(mock.Object);
2677+
2678+
wrapped.GetData("Foo", false);
2679+
mock.Verify(o => o.GetData("Foo", false), Times.Once());
2680+
mock.VerifyNoOtherCalls();
2681+
mock.Reset();
2682+
2683+
wrapped.GetData("Foo");
2684+
mock.Verify(o => o.GetData("Foo", true), Times.Once());
2685+
mock.VerifyNoOtherCalls();
2686+
mock.Reset();
2687+
2688+
wrapped.GetData(typeof(string));
2689+
mock.Verify(o => o.GetData("System.String", true), Times.Once());
2690+
mock.VerifyNoOtherCalls();
2691+
mock.Reset();
2692+
2693+
wrapped.GetDataPresent("Foo", false);
2694+
mock.Verify(o => o.GetDataPresent("Foo", false), Times.Once());
2695+
mock.VerifyNoOtherCalls();
2696+
mock.Reset();
2697+
2698+
wrapped.GetDataPresent("Foo");
2699+
mock.Verify(o => o.GetDataPresent("Foo", true), Times.Once());
2700+
mock.VerifyNoOtherCalls();
2701+
mock.Reset();
2702+
2703+
wrapped.GetDataPresent(typeof(string));
2704+
mock.Verify(o => o.GetDataPresent("System.String", true), Times.Once());
2705+
mock.VerifyNoOtherCalls();
2706+
mock.Reset();
2707+
2708+
wrapped.GetFormats(false);
2709+
mock.Verify(o => o.GetFormats(false), Times.Once());
2710+
mock.VerifyNoOtherCalls();
2711+
mock.Reset();
2712+
2713+
wrapped.GetFormats();
2714+
mock.Verify(o => o.GetFormats(true), Times.Once());
2715+
mock.VerifyNoOtherCalls();
2716+
mock.Reset();
2717+
2718+
wrapped.SetData("Foo", "Bar");
2719+
mock.Verify(o => o.SetData("Foo", "Bar"), Times.Once());
2720+
mock.VerifyNoOtherCalls();
2721+
mock.Reset();
2722+
2723+
wrapped.SetData(typeof(string), "Bar");
2724+
mock.Verify(o => o.SetData(typeof(string), "Bar"), Times.Once());
2725+
mock.VerifyNoOtherCalls();
2726+
mock.Reset();
2727+
2728+
wrapped.SetData("Bar");
2729+
mock.Verify(o => o.SetData("Bar"), Times.Once());
2730+
mock.VerifyNoOtherCalls();
2731+
mock.Reset();
2732+
}
26602733
}

0 commit comments

Comments
 (0)