Skip to content

Commit f6fbbdf

Browse files
authored
Add a few more EMF tests (#12969)
Move a few interop definitions down and add some more EMF tests to ensure we have #12951 fully covered.
1 parent d2625ca commit f6fbbdf

File tree

9 files changed

+126
-14
lines changed

9 files changed

+126
-14
lines changed

src/System.Drawing.Common/tests/System/Drawing/ImageTests.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
using System.Runtime.InteropServices;
66
using System.Text;
77
using Microsoft.DotNet.XUnitExtensions;
8+
using Windows.Win32;
9+
using Windows.Win32.Foundation;
10+
using Windows.Win32.Graphics.Gdi;
811
using Encoder = System.Drawing.Imaging.Encoder;
912

1013
namespace System.Drawing.Tests;
@@ -663,4 +666,71 @@ public void Save_InvalidDirectory_ThrowsDirectoryNotFoundException()
663666
string badTarget = Path.Join("NoSuchDirectory", "NoSuchFile");
664667
AssertExtensions.Throws<DirectoryNotFoundException>(() => bitmap.Save(badTarget), $"The directory NoSuchDirectory of the filename {badTarget} does not exist.");
665668
}
669+
670+
[Fact]
671+
public unsafe void FromStream_NativeMetafile()
672+
{
673+
// Create a memory metafile from the screen DC
674+
HDC hdc = PInvokeCore.CreateEnhMetaFile(HDC.Null, default, null, default(PCWSTR));
675+
using CreatePenScope pen = new(Color.Red);
676+
using SelectObjectScope penScope = new(hdc, pen);
677+
PInvokeCore.Rectangle(hdc, 10, 10, 100, 100);
678+
HENHMETAFILE hemf = PInvokeCore.CloseEnhMetaFile(hdc);
679+
680+
uint length = PInvokeCore.GetEnhMetaFileBits(hemf, 0, null);
681+
byte[] buffer = new byte[length];
682+
length = PInvokeCore.GetEnhMetaFileBits(hemf, buffer);
683+
PInvokeCore.DeleteEnhMetaFile(hemf);
684+
685+
MemoryStream stream = new(buffer);
686+
using Image image = Image.FromStream(stream);
687+
image.Size.Should().Be(new Size(90, 90));
688+
689+
// The stream must be at the beginning for WMF/EMF
690+
stream.Position = 10;
691+
Action action = () => Image.FromStream(stream);
692+
action.Should().Throw<ArgumentException>();
693+
694+
// https://github.com/dotnet/winforms/issues/12951
695+
stream.Position = 0;
696+
using Image image2 = Image.FromStream(new NonSeekableStreamWrapper(stream));
697+
}
698+
699+
public class NonSeekableStreamWrapper : Stream
700+
{
701+
private readonly Stream _innerStream;
702+
703+
public NonSeekableStreamWrapper(Stream innerStream) =>
704+
_innerStream = innerStream ?? throw new ArgumentNullException(nameof(innerStream));
705+
706+
public override bool CanRead => _innerStream.CanRead;
707+
public override bool CanSeek => false;
708+
public override bool CanWrite => _innerStream.CanWrite;
709+
public override long Length => throw new NotSupportedException("This stream does not support seeking.");
710+
public override long Position
711+
{
712+
get => throw new NotSupportedException("This stream does not support seeking.");
713+
set => throw new NotSupportedException("This stream does not support seeking.");
714+
}
715+
716+
public override void Flush() => _innerStream.Flush();
717+
718+
public override int Read(byte[] buffer, int offset, int count) => _innerStream.Read(buffer, offset, count);
719+
720+
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException("This stream does not support seeking.");
721+
722+
public override void SetLength(long value) => _innerStream.SetLength(value);
723+
724+
public override void Write(byte[] buffer, int offset, int count) => _innerStream.Write(buffer, offset, count);
725+
726+
protected override void Dispose(bool disposing)
727+
{
728+
if (disposing)
729+
{
730+
_innerStream.Dispose();
731+
}
732+
733+
base.Dispose(disposing);
734+
}
735+
}
666736
}

src/System.Drawing.Common/tests/System/Drawing/Imaging/MetafileTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
//
2525

2626
using System.Runtime.InteropServices;
27+
using Windows.Win32;
28+
using Windows.Win32.Foundation;
29+
using Windows.Win32.Graphics.Gdi;
2730

2831
namespace System.Drawing.Imaging.Tests;
2932

@@ -1014,4 +1017,42 @@ private static void AssertMetafileHeader(MetafileHeader header)
10141017
Assert.Equal((int)MetafileType.Wmf, header.WmfHeader.Type);
10151018
Assert.Equal(0x300, header.WmfHeader.Version);
10161019
}
1020+
1021+
[Fact]
1022+
public unsafe void CreateFromNativeHandle_Success()
1023+
{
1024+
// Create a memory metafile from the screen DC
1025+
HDC hdc = PInvokeCore.CreateEnhMetaFile(HDC.Null, default, null, default(PCWSTR));
1026+
using CreatePenScope pen = new(Color.Blue);
1027+
using CreateBrushScope brush = new(Color.Green);
1028+
using SelectObjectScope penScope = new(hdc, pen);
1029+
using SelectObjectScope brushScope = new(hdc, brush);
1030+
PInvokeCore.Rectangle(hdc, 10, 10, 100, 100);
1031+
HENHMETAFILE hemf = PInvokeCore.CloseEnhMetaFile(hdc);
1032+
1033+
Metafile metafile = new(henhmetafile: (nint)hemf.Value, deleteEmf: true);
1034+
metafile.Size.Should().Be(new Size(90, 90));
1035+
1036+
List<EmfPlusRecordType> recordTypes = [];
1037+
using Graphics graphics = Graphics.FromHwnd(0);
1038+
graphics.EnumerateMetafile(
1039+
metafile,
1040+
default(Point),
1041+
(recordType, flags, dataSize, data, _) =>
1042+
{
1043+
recordTypes.Add(recordType);
1044+
return true;
1045+
});
1046+
1047+
recordTypes.Should().BeEquivalentTo(
1048+
[
1049+
EmfPlusRecordType.EmfHeader,
1050+
EmfPlusRecordType.EmfCreatePen,
1051+
EmfPlusRecordType.EmfSelectObject,
1052+
EmfPlusRecordType.EmfCreateBrushIndirect,
1053+
EmfPlusRecordType.EmfSelectObject,
1054+
EmfPlusRecordType.EmfRectangle,
1055+
EmfPlusRecordType.EmfEof
1056+
]);
1057+
}
10171058
}

src/System.Private.Windows.Core/src/NativeMethods.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CFSTR_FILENAMEA
99
CFSTR_INDRAGLOOP
1010
CLIPBOARD_FORMAT
1111
CLIPBRD_E_BAD_DATA
12+
CloseEnhMetaFile
1213
CLR_*
1314
CoCreateInstance
1415
CombineRgn
@@ -19,6 +20,7 @@ CreateCompatibleBitmap
1920
CreateCompatibleDC
2021
CreateDC
2122
CreateDIBSection
23+
CreateEnhMetaFileW
2224
CreateFontIndirect
2325
CreateICW
2426
CreatePen
@@ -27,6 +29,7 @@ CreateSolidBrush
2729
DATA_S_SAMEFORMATETC
2830
DefWindowProc
2931
DeleteDC
32+
DeleteEnhMetaFile
3033
DeleteObject
3134
DestroyIcon
3235
DEVMODEW
@@ -80,6 +83,7 @@ EM_*
8083
EndPaint
8184
EnumChildWindows
8285
EnumDisplayMonitors
86+
EnumEnhMetaFile
8387
EnumThreadWindows
8488
EnumWindows
8589
FACILITY_CODE
@@ -95,6 +99,7 @@ GetDC
9599
GetDCEx
96100
GetDesktopWindow
97101
GetDeviceCaps
102+
GetEnhMetaFileBits
98103
GetForegroundWindow
99104
GetGuiResources
100105
GetIconInfo
@@ -203,6 +208,7 @@ PropVariantClear
203208
PWSTR
204209
RealizePalette
205210
RECT
211+
Rectangle
206212
REGDB_E_CLASSNOTREG
207213
RegisterClipboardFormat
208214
RegisterDragDrop

src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DesignerUtils.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ public static void DrawNoResizeHandle(Graphics graphics, Rectangle bounds, bool
267267
using SelectObjectScope penSelection = new(hDC, s_grabHandlePenPrimary);
268268

269269
// Draw our rect no-resize handle
270-
PInvoke.Rectangle(hDC, bounds.Left, bounds.Top, bounds.Right, bounds.Bottom);
270+
PInvokeCore.Rectangle(hDC, bounds.Left, bounds.Top, bounds.Right, bounds.Bottom);
271271
}
272272

273273
/// <summary>
@@ -291,7 +291,7 @@ public static void DrawLockedHandle(Graphics graphics, Rectangle bounds, bool is
291291

292292
// Lower rect - its fillbrush depends on the primary selection
293293
PInvokeCore.SelectObject(hDC, isPrimary ? s_grabHandleFillBrushPrimary : s_grabHandleFillBrush);
294-
PInvoke.Rectangle(hDC, bounds.Left, bounds.Top + s_lockedHandleLowerOffset, bounds.Right, bounds.Bottom);
294+
PInvokeCore.Rectangle(hDC, bounds.Left, bounds.Top + s_lockedHandleLowerOffset, bounds.Right, bounds.Bottom);
295295
}
296296

297297
/// <summary>

src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/OleDragDropHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ private static void DrawReversibleFrame(HWND handle, Rectangle rectangle, Color
442442
using SelectObjectScope penSelection = new(dc, pen);
443443

444444
PInvokeCore.SetBkColor(dc, (COLORREF)(uint)ColorTranslator.ToWin32(graphicsColor));
445-
PInvoke.Rectangle(dc, rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom);
445+
PInvokeCore.Rectangle(dc, rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom);
446446
// ------ Duplicate code----------------------------------------------------------
447447
}
448448

src/System.Windows.Forms.Primitives/src/NativeMethods.txt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ ClientToScreen
3434
CLIPBRD_E_BAD_DATA
3535
ClipCursor
3636
CloseDesktop
37-
CloseEnhMetaFile
3837
CloseHandle
3938
CloseThemeData
4039
CLSCTX
@@ -49,7 +48,6 @@ CoRegisterMessageFilter
4948
CreateAcceleratorTableW
5049
CreateActCtx
5150
CreateBrushIndirect
52-
CreateEnhMetaFileW
5351
CreateHalftonePalette
5452
CreateILockBytesOnHGlobal
5553
CreateMenu
@@ -64,7 +62,6 @@ DATETIMEPICK_CLASS
6462
DeactivateActCtx
6563
DefFrameProc
6664
DefMDIChildProc
67-
DeleteEnhMetaFile
6865
DESKTOP_ACCESS_FLAGS
6966
DestroyAcceleratorTable
7067
DestroyCursor
@@ -108,7 +105,6 @@ EnableWindow
108105
EndDialog
109106
ENM_*
110107
EnumDisplaySettings
111-
EnumEnhMetaFile
112108
ES_*
113109
EVENTMSG
114110
ExpandCollapseState
@@ -519,7 +515,6 @@ PROPERTYKEY
519515
ProviderOptions
520516
ReadClassStg
521517
READYSTATE
522-
Rectangle
523518
RedrawWindow
524519
REGDB_E_CLASSNOTREG
525520
RegisterClass

src/System.Windows.Forms.Primitives/src/System/Windows/Forms/DeviceContextExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ internal static void DrawRectangle(
3434
using SetRop2Scope ropScope = new(hdc, R2_MODE.R2_COPYPEN);
3535
using SelectObjectScope brushScope = new(hdc, PInvokeCore.GetStockObject(GET_STOCK_OBJECT_FLAGS.NULL_BRUSH));
3636

37-
PInvoke.Rectangle(hdc, left, top, right, bottom);
37+
PInvokeCore.Rectangle(hdc, left, top, right, bottom);
3838
}
3939

4040
internal static void FillRectangle(this DeviceContextHdcScope hdc, Rectangle rectangle, HBRUSH hbrush) =>

src/System.Windows.Forms.Primitives/tests/TestUtilities/Metafiles/EmfScope.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ private static unsafe HDC CreateEnhMetaFile(
4444
fixed (char* pFileName = lpFilename)
4545
fixed (char* pDesc = lpDesc)
4646
{
47-
HDC metafileHdc = PInvoke.CreateEnhMetaFile(hdc, pFileName, lprc, pDesc);
47+
HDC metafileHdc = PInvokeCore.CreateEnhMetaFile(hdc, pFileName, lprc, pDesc);
4848
if (metafileHdc.IsNull)
4949
{
5050
throw new Win32Exception("Could not create metafile");
@@ -67,7 +67,7 @@ public HENHMETAFILE HENHMETAFILE
6767
return default;
6868
}
6969

70-
_hemf = PInvoke.CloseEnhMetaFile(HDC);
70+
_hemf = PInvokeCore.CloseEnhMetaFile(HDC);
7171
}
7272

7373
return _hemf;
@@ -80,7 +80,7 @@ public unsafe void Enumerate(ProcessRecordDelegate enumerator)
8080
try
8181
{
8282
IntPtr callback = Marshal.GetFunctionPointerForDelegate(CallBack);
83-
PInvoke.EnumEnhMetaFile(
83+
PInvokeCore.EnumEnhMetaFile(
8484
default,
8585
HENHMETAFILE,
8686
(delegate* unmanaged[Stdcall]<HDC, HANDLETABLE*, ENHMETARECORD*, int, LPARAM, int>)callback,
@@ -250,7 +250,7 @@ public void Dispose()
250250
{
251251
if (!HDC.IsNull)
252252
{
253-
PInvoke.DeleteEnhMetaFile(HENHMETAFILE);
253+
PInvokeCore.DeleteEnhMetaFile(HENHMETAFILE);
254254
}
255255

256256
GC.SuppressFinalize(this);

src/System.Windows.Forms/src/System/Windows/Forms/Rendering/ControlPaint.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1815,7 +1815,7 @@ public static void DrawReversibleFrame(Rectangle rectangle, Color backColor, Fra
18151815
using SelectObjectScope penSelection = new(desktopDC, pen);
18161816

18171817
PInvokeCore.SetBkColor(desktopDC, (COLORREF)(uint)ColorTranslator.ToWin32(graphicsColor));
1818-
PInvoke.Rectangle(desktopDC, rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom);
1818+
PInvokeCore.Rectangle(desktopDC, rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom);
18191819
}
18201820

18211821
/// <summary>

0 commit comments

Comments
 (0)