|
// 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.Runtime.InteropServices;
using Windows.Win32.System.Com;
using Windows.Win32.System.Ole;
using DVASPECT = System.Runtime.InteropServices.ComTypes.DVASPECT;
using FORMATETC = System.Runtime.InteropServices.ComTypes.FORMATETC;
using IStream = Windows.Win32.System.Com.IStream;
using STGMEDIUM = System.Runtime.InteropServices.ComTypes.STGMEDIUM;
using TYMED = System.Runtime.InteropServices.ComTypes.TYMED;
using Com = Windows.Win32.System.Com;
namespace System.Windows.Forms.Tests;
[Collection("Sequential")] // Each registered Clipboard format is an OS singleton,
// and we should not run this test at the same time as other tests using the same format.
[UISettings(MaxAttempts = 3)] // Try up to 3 times before failing.
public unsafe class DragDropFormatTests
{
public static IEnumerable<object[]> DragDropFormat_TestData()
{
FORMATETC formatEtc = new()
{
cfFormat = (short)PInvoke.RegisterClipboardFormat("InShellDragLoop"),
dwAspect = DVASPECT.DVASPECT_CONTENT,
lindex = -1,
ptd = nint.Zero,
tymed = TYMED.TYMED_HGLOBAL
};
STGMEDIUM medium = new()
{
pUnkForRelease = null,
tymed = TYMED.TYMED_HGLOBAL,
unionmember = PInvokeCore.GlobalAlloc(
GLOBAL_ALLOC_FLAGS.GMEM_MOVEABLE | GLOBAL_ALLOC_FLAGS.GMEM_ZEROINIT,
BOOL.Size)
};
SaveInDragLoopToHandle((HGLOBAL)medium.unionmember, inDragLoop: true);
yield return new object[] { formatEtc, medium };
MemoryStream memoryStream = new();
IStream.Interface iStream = new ComManagedStream(memoryStream);
formatEtc = new()
{
cfFormat = (short)PInvoke.RegisterClipboardFormat("DragContext"),
dwAspect = DVASPECT.DVASPECT_CONTENT,
lindex = -1,
ptd = nint.Zero,
tymed = TYMED.TYMED_ISTREAM
};
medium = new()
{
pUnkForRelease = null,
tymed = TYMED.TYMED_ISTREAM,
unionmember = Marshal.GetIUnknownForObject(iStream)
};
yield return new object[] { formatEtc, medium };
}
[Theory]
[MemberData(nameof(DragDropFormat_TestData))]
public void DragDropFormat_Set_Dispose_ReturnsExpected(FORMATETC formatEtc, STGMEDIUM medium)
{
DragDropFormat? dragDropFormat = default;
try
{
dragDropFormat = new DragDropFormat((ushort)formatEtc.cfFormat, (Com.STGMEDIUM)medium, copyData: false);
dragDropFormat.Dispose();
int handleSize = (int)PInvokeCore.GlobalSize(dragDropFormat.Medium.hGlobal);
Assert.Equal(0, handleSize);
Assert.Equal(nint.Zero, (nint)dragDropFormat.Medium.pUnkForRelease);
Assert.Equal(Com.TYMED.TYMED_NULL, dragDropFormat.Medium.tymed);
Assert.True(dragDropFormat.Medium.hGlobal.IsNull);
}
finally
{
dragDropFormat?.Dispose();
}
}
[Theory]
[MemberData(nameof(DragDropFormat_TestData))]
public void DragDropFormat_Set_GetData_ReturnsExpected(FORMATETC formatEtc, STGMEDIUM medium)
{
DragDropFormat? dragDropFormat = default;
try
{
dragDropFormat = new DragDropFormat((ushort)formatEtc.cfFormat, (Com.STGMEDIUM)medium, copyData: false);
Com.STGMEDIUM data = dragDropFormat.GetData();
Assert.Equal(medium.pUnkForRelease ?? nint.Zero, (nint)data.pUnkForRelease);
Assert.Equal((uint)medium.tymed, (uint)data.tymed);
switch (data.tymed)
{
case Com.TYMED.TYMED_HGLOBAL:
case Com.TYMED.TYMED_FILE:
case Com.TYMED.TYMED_ENHMF:
case Com.TYMED.TYMED_GDI:
case Com.TYMED.TYMED_MFPICT:
Assert.NotEqual(medium.unionmember, data.hGlobal);
break;
case Com.TYMED.TYMED_ISTORAGE:
case Com.TYMED.TYMED_ISTREAM:
case Com.TYMED.TYMED_NULL:
default:
Assert.Equal(medium.unionmember, (nint)data.hGlobal);
break;
}
}
finally
{
dragDropFormat?.Dispose();
}
}
[Theory]
[MemberData(nameof(DragDropFormat_TestData))]
public void DragDropFormat_Set_RefreshData_ReturnsExpected(FORMATETC formatEtc, STGMEDIUM medium)
{
DragDropFormat? dragDropFormat = default;
try
{
dragDropFormat = new DragDropFormat((ushort)formatEtc.cfFormat, (Com.STGMEDIUM)medium, copyData: false);
Com.STGMEDIUM dataRefresh = new()
{
pUnkForRelease = dragDropFormat.Medium.pUnkForRelease,
tymed = dragDropFormat.Medium.tymed,
u = new()
{
hGlobal = dragDropFormat.Medium.tymed switch
{
Com.TYMED.TYMED_HGLOBAL or Com.TYMED.TYMED_FILE or Com.TYMED.TYMED_ENHMF or Com.TYMED.TYMED_GDI or Com.TYMED.TYMED_MFPICT
=> (HGLOBAL)(nint)PInvoke.OleDuplicateData(
(HANDLE)(nint)dragDropFormat.Medium.hGlobal,
(CLIPBOARD_FORMAT)formatEtc.cfFormat,
GLOBAL_ALLOC_FLAGS.GMEM_MOVEABLE | GLOBAL_ALLOC_FLAGS.GMEM_ZEROINIT),
_ => dragDropFormat.Medium.hGlobal,
}
}
};
dragDropFormat.RefreshData((ushort)formatEtc.cfFormat, dataRefresh, copyData: false);
Com.STGMEDIUM data = dragDropFormat.GetData();
switch (dragDropFormat.Medium.tymed)
{
case Com.TYMED.TYMED_HGLOBAL:
case Com.TYMED.TYMED_FILE:
case Com.TYMED.TYMED_ENHMF:
case Com.TYMED.TYMED_GDI:
case Com.TYMED.TYMED_MFPICT:
Assert.NotEqual(dragDropFormat.Medium.u, data.u);
break;
case Com.TYMED.TYMED_ISTORAGE:
case Com.TYMED.TYMED_ISTREAM:
case Com.TYMED.TYMED_NULL:
default:
Assert.Equal(dragDropFormat.Medium.u, data.u);
break;
}
}
finally
{
dragDropFormat?.Dispose();
}
}
private static unsafe void SaveInDragLoopToHandle(HGLOBAL handle, bool inDragLoop)
{
try
{
void* basePtr = PInvokeCore.GlobalLock(handle);
*(BOOL*)basePtr = (BOOL)inDragLoop;
}
finally
{
PInvokeCore.GlobalUnlock(handle);
}
}
}
|