Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ private WpfOleServices() { }

public static void EnsureThreadState() => OleServicesContext.EnsureThreadState();

// WPF also provides enhanced metafiles, which are handled in GetDataHere.
public static TYMED AllowedTymeds =>
TYMED.TYMED_HGLOBAL | TYMED.TYMED_ISTREAM | TYMED.TYMED_GDI | TYMED.TYMED_ENHMF;

public static HRESULT GetDataHere(string format, object data, FORMATETC* pformatetc, STGMEDIUM* pmedium)
{
TYMED mediumType = (TYMED)pformatetc->tymed;
Expand All @@ -40,20 +44,29 @@ public static HRESULT GetDataHere(string format, object data, FORMATETC* pformat
// Handle enhanced metafiles.
if (mediumType.HasFlag(TYMED.TYMED_ENHMF) && format.Equals(DataFormatNames.Emf))
{
HENHMETAFILE hemf = HENHMETAFILE.Null;

if (SystemDrawingHelper.IsMetafile(data))
{
pmedium->u.hEnhMetaFile = SystemDrawingHelper.GetHandleFromMetafile(data);
hemf = SystemDrawingHelper.GetHandleFromMetafile(data);
}
else if (data is MemoryStream memoryStream && memoryStream.GetBuffer() is { } buffer && buffer.Length != 0)
{
HENHMETAFILE hemf = PInvoke.SetEnhMetaFileBits(buffer);
hemf = PInvoke.SetEnhMetaFileBits(buffer);

if (hemf.IsNull)
{
throw new Win32Exception();
}
}

if (hemf.IsNull)
{
return HRESULT.E_FAIL;
}

pmedium->tymed = TYMED.TYMED_ENHMF;
pmedium->u.hEnhMetaFile = hemf;
return HRESULT.S_OK;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
Expand Down Expand Up @@ -270,4 +271,75 @@ public void SetData_Text_Format_AllUpper()
Clipboard.GetData("System.String").Should().BeNull();
Clipboard.GetData("TEXT").Should().BeNull();
}

[WpfTheory(Skip = "Requires IOleServices.AllowedTymeds from dotnet/winforms")]
[BoolData]
public void SetDataObject_EnhancedMetafileStream_NativeConsumerGetsHandle(bool copy)
{
MemoryStream stream = new();
stream.Write(CreateEmfBytes());
Clipboard.SetDataObject(new DataObject(DataFormats.EnhancedMetafile, stream), copy);

// Read the data back the way another application would.
Native.OpenClipboard(0).Should().BeTrue();
try
{
Native.IsClipboardFormatAvailable(Native.CF_ENHMETAFILE).Should().BeTrue();
nint hemf = Native.GetClipboardData(Native.CF_ENHMETAFILE);
hemf.Should().NotBe(0);
Native.GetEnhMetaFileBits(hemf, 0, null).Should().BeGreaterThan(0u);
}
finally
{
Native.CloseClipboard();
}

static byte[] CreateEmfBytes()
{
nint hdc = Native.CreateEnhMetaFileW(0, null, 0, null);
Native.Rectangle(hdc, 0, 0, 200, 100);
nint hemf = Native.CloseEnhMetaFile(hdc);
byte[] bytes = new byte[Native.GetEnhMetaFileBits(hemf, 0, null)];
Native.GetEnhMetaFileBits(hemf, (uint)bytes.Length, bytes);
Native.DeleteEnhMetaFile(hemf);
return bytes;
}
}

private static class Native
{
public const uint CF_ENHMETAFILE = 14;

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenClipboard(nint hWndNewOwner);

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseClipboard();

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsClipboardFormatAvailable(uint format);

[DllImport("user32.dll", SetLastError = true)]
public static extern nint GetClipboardData(uint uFormat);

[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
public static extern nint CreateEnhMetaFileW(nint hdc, string? lpFilename, nint lprc, string? lpDesc);

[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Rectangle(nint hdc, int left, int top, int right, int bottom);

[DllImport("gdi32.dll")]
public static extern nint CloseEnhMetaFile(nint hdc);

[DllImport("gdi32.dll")]
public static extern uint GetEnhMetaFileBits(nint hEMF, uint nSize, byte[]? lpData);

[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteEnhMetaFile(nint hemf);
}
}