diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs new file mode 100644 index 0000000000000..a3ff4e14fb7a7 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; + +internal static partial class Interop +{ + internal static partial class Ole32 + { + /// + /// IStream interface. + /// + /// + /// This interface explicitly doesn't use the built-in COM support, but instead is only used with ComWrappers. + /// + internal interface IStream + { + // pcbRead is optional + unsafe void Read(byte* pv, uint cb, uint* pcbRead); + + // pcbWritten is optional + unsafe void Write(byte* pv, uint cb, uint* pcbWritten); + + // SeekOrgin matches the native values, plibNewPosition is optional + unsafe void Seek(long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition); + + void SetSize(ulong libNewSize); + + // pcbRead and pcbWritten are optional + unsafe HRESULT CopyTo( + IntPtr pstm, + ulong cb, + ulong* pcbRead, + ulong* pcbWritten); + + void Commit(uint grfCommitFlags); + + void Revert(); + + HRESULT LockRegion( + ulong libOffset, + ulong cb, + uint dwLockType); + + HRESULT UnlockRegion( + ulong libOffset, + ulong cb, + uint dwLockType); + + unsafe void Stat( + STATSTG* pstatstg, + STATFLAG grfStatFlag); + + unsafe HRESULT Clone(IntPtr* ppstm); + } + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs similarity index 97% rename from src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.cs rename to src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs index 6fdf6718f050a..b8907e5a33a9f 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class Ole32 { /// - /// COM IStream interface. + /// COM IStream interface. /// /// /// The definition in does not lend diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index a0b7401fb0ac2..0000000000000 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - ILLink - IL2050 - member - M:System.Drawing.Bitmap.#ctor(System.IO.Stream,System.Boolean) - - - ILLink - IL2050 - member - M:System.Drawing.Image.FromStream(System.IO.Stream,System.Boolean,System.Boolean) - - - ILLink - IL2050 - member - M:System.Drawing.Image.InitializeFromStream(System.IO.Stream) - - - ILLink - IL2050 - member - M:System.Drawing.Image.Save(System.IO.Stream,System.Drawing.Imaging.ImageCodecInfo,System.Drawing.Imaging.EncoderParameters) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Imaging.EmfType,System.String) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Rectangle,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.RectangleF,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.GetMetafileHeader(System.IO.Stream) - - - diff --git a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj index b02d55a46c6a2..ff707877c2d11 100644 --- a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -192,6 +192,7 @@ + @@ -294,8 +295,6 @@ Link="Common\Interop\Windows\Kernel32\Interop.GlobalLock.cs" /> - + + - + + + + diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs index 4fe1b54ae1b83..84f959f94c281 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs @@ -9,25 +9,24 @@ namespace System.Drawing { public sealed partial class Bitmap { - public Bitmap(Stream stream, bool useIcm) + public unsafe Bitmap(Stream stream, bool useIcm) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } - IntPtr bitmap = IntPtr.Zero; - int status; + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + IntPtr bitmap = IntPtr.Zero; if (useIcm) { - status = Gdip.GdipCreateBitmapFromStreamICM(new GPStream(stream), out bitmap); + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromStreamICM(streamWrapper.Ptr, &bitmap)); } else { - status = Gdip.GdipCreateBitmapFromStream(new GPStream(stream), out bitmap); + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromStream(streamWrapper.Ptr, &bitmap)); } - Gdip.CheckStatus(status); ValidateImage(bitmap); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.COMWrappers.cs similarity index 89% rename from src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs rename to src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.COMWrappers.cs index 236ae1be2aa03..5889a4ac704b4 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.COMWrappers.cs @@ -14,16 +14,16 @@ namespace System.Drawing /// /// Supports IStream and IPicture COM interfaces. /// - internal unsafe class DrawingComWrappers : ComWrappers + internal unsafe partial class DrawingCom : ComWrappers { private const int S_OK = (int)Interop.HRESULT.S_OK; private static readonly Guid IID_IStream = new Guid(0x0000000C, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); private static readonly ComInterfaceEntry* s_wrapperEntry = InitializeComInterfaceEntry(); - internal static DrawingComWrappers Instance { get; } = new DrawingComWrappers(); + internal static DrawingCom Instance { get; } = new DrawingCom(); - private DrawingComWrappers() { } + private DrawingCom() { } private static ComInterfaceEntry* InitializeComInterfaceEntry() { @@ -31,7 +31,7 @@ private DrawingComWrappers() { } IntPtr iStreamVtbl = IStreamVtbl.Create(fpQueryInterface, fpAddRef, fpRelease); - ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DrawingComWrappers), sizeof(ComInterfaceEntry)); + ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DrawingCom), sizeof(ComInterfaceEntry)); wrapperEntry->IID = IID_IStream; wrapperEntry->Vtable = iStreamVtbl; return wrapperEntry; @@ -66,6 +66,27 @@ protected override void ReleaseObjects(IEnumerable objects) throw new NotImplementedException(); } + internal static IStreamWrapper GetComWrapper(Interop.Ole32.IStream stream) + { + IntPtr streamWrapperPtr = Instance.GetOrCreateComInterfaceForObject(stream, CreateComInterfaceFlags.None); + + Guid streamIID = IID_IStream; + int result = Marshal.QueryInterface(streamWrapperPtr, ref streamIID, out IntPtr streamPtr); + + Marshal.Release(streamWrapperPtr); + + ThrowExceptionForHR(result); + + return new IStreamWrapper(streamPtr); + } + + internal static void ThrowExceptionForHR(int errorCode) + { + // Pass -1 for errorInfo to indicate that Windows' GetErrorInfo shouldn't be called, and only + // throw the Exception corresponding to the specified errorCode. + Marshal.ThrowExceptionForHR(errorCode, errorInfo: new IntPtr(-1)); + } + internal static class IStreamVtbl { public static IntPtr Create(IntPtr fpQueryInterface, IntPtr fpAddRef, IntPtr fpRelease) @@ -159,16 +180,13 @@ private static int CopyTo(IntPtr thisPtr, IntPtr pstm, ulong cb, ulong* pcbRead, try { Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); - Interop.Ole32.IStream pstmStream = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)pstm); - inst.CopyTo(pstmStream, cb, pcbRead, pcbWritten); + return (int)inst.CopyTo(pstm, cb, pcbRead, pcbWritten); } catch (Exception e) { return e.HResult; } - - return S_OK; } [UnmanagedCallersOnly] @@ -250,23 +268,16 @@ private static int Stat(IntPtr thisPtr, Interop.Ole32.STATSTG* pstatstg, Interop [UnmanagedCallersOnly] private static int Clone(IntPtr thisPtr, IntPtr* ppstm) { - if (ppstm == null) - { - return (int)Interop.HRESULT.STG_E_INVALIDPOINTER; - } - try { Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); - *ppstm = Instance.GetOrCreateComInterfaceForObject(inst.Clone(), CreateComInterfaceFlags.None); + return (int)inst.Clone(ppstm); } catch (Exception e) { return e.HResult; } - - return S_OK; } } @@ -297,7 +308,7 @@ public unsafe int SaveAsFile(IntPtr pstm, int fSaveMemCopy, int* pcbSize) { // Get the IStream implementation, since the ComWrappers runtime returns a pointer to the IUnknown interface implementation Guid streamIID = IID_IStream; - Marshal.ThrowExceptionForHR(Marshal.QueryInterface(pstm, ref streamIID, out IntPtr pstmImpl)); + ThrowExceptionForHR(Marshal.QueryInterface(pstm, ref streamIID, out IntPtr pstmImpl)); try { diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.NoCOMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.NoCOMWrappers.cs new file mode 100644 index 0000000000000..d3fdd2115bffb --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.NoCOMWrappers.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Internal; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + internal static partial class DrawingCom + { + internal static IStreamWrapper GetComWrapper(GPStream stream) + { + return new IStreamWrapper(Marshal.GetComInterfaceForObject(stream)); + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.cs new file mode 100644 index 0000000000000..a5b53e65ff589 --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + internal partial class DrawingCom + { + internal readonly struct IStreamWrapper : IDisposable + { + public readonly IntPtr Ptr; + + public IStreamWrapper(IntPtr ptr) + { + Ptr = ptr; + } + + public void Dispose() + { + Marshal.Release(Ptr); + } + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs index 47aeb4f9397c3..3edf4a0e7b67e 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs @@ -210,10 +210,10 @@ private static void PlatformInitialize() internal static extern int GdipDeleteBrush(HandleRef brush); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipLoadImageFromStream(Interop.Ole32.IStream stream, out IntPtr image); + internal static extern int GdipLoadImageFromStream(IntPtr stream, IntPtr* image); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipLoadImageFromStreamICM(Interop.Ole32.IStream stream, out IntPtr image); + internal static extern int GdipLoadImageFromStreamICM(IntPtr stream, IntPtr* image); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipCloneImage(HandleRef image, out IntPtr cloneimage); @@ -222,7 +222,7 @@ private static void PlatformInitialize() internal static extern int GdipSaveImageToFile(HandleRef image, string filename, ref Guid classId, HandleRef encoderParams); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSaveImageToStream(HandleRef image, Interop.Ole32.IStream stream, ref Guid classId, HandleRef encoderParams); + internal static extern int GdipSaveImageToStream(HandleRef image, IntPtr stream, Guid* classId, HandleRef encoderParams); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipSaveAdd(HandleRef image, HandleRef encoderParams); @@ -327,7 +327,7 @@ private static void PlatformInitialize() internal static extern int GdipGetMetafileHeaderFromFile(string filename, IntPtr header); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetMetafileHeaderFromStream(Interop.Ole32.IStream stream, IntPtr header); + internal static extern int GdipGetMetafileHeaderFromStream(IntPtr stream, IntPtr header); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipGetMetafileHeaderFromMetafile(HandleRef metafile, IntPtr header); @@ -336,16 +336,16 @@ private static void PlatformInitialize() internal static extern int GdipGetHemfFromMetafile(HandleRef metafile, out IntPtr hEnhMetafile); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateMetafileFromStream(Interop.Ole32.IStream stream, out IntPtr metafile); + internal static extern int GdipCreateMetafileFromStream(IntPtr stream, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStream(Interop.Ole32.IStream stream, IntPtr referenceHdc, EmfType emfType, ref RectangleF frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + internal static extern int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, RectangleF* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStream(Interop.Ole32.IStream stream, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + internal static extern int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStreamI(Interop.Ole32.IStream stream, IntPtr referenceHdc, EmfType emfType, ref Rectangle frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + internal static extern int GdipRecordMetafileStreamI(IntPtr stream, IntPtr referenceHdc, EmfType emfType, Rectangle* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipComment(HandleRef graphics, int sizeData, byte[] data); @@ -354,10 +354,10 @@ private static void PlatformInitialize() internal static extern int GdipCreateFontFromLogfontW(IntPtr hdc, ref Interop.User32.LOGFONT lf, out IntPtr font); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromStream(Interop.Ole32.IStream stream, out IntPtr bitmap); + internal static extern int GdipCreateBitmapFromStream(IntPtr stream, IntPtr* bitmap); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromStreamICM(Interop.Ole32.IStream stream, out IntPtr bitmap); + internal static extern int GdipCreateBitmapFromStreamICM(IntPtr stream, IntPtr* bitmap); } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs index a03ad5ed133bb..3d9ad1a356676 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs @@ -28,7 +28,7 @@ public unsafe void Save(Stream outputStream) // But, in the interest of simplicity, we just call to // OLE to do it for us. PICTDESC pictdesc = PICTDESC.CreateIconPICTDESC(Handle); - Guid iid = DrawingComWrappers.IPicture.IID; + Guid iid = DrawingCom.IPicture.IID; IntPtr lpPicture; Marshal.ThrowExceptionForHR(OleCreatePictureIndirect(&pictdesc, &iid, fOwn: 0, &lpPicture)); @@ -36,13 +36,13 @@ public unsafe void Save(Stream outputStream) try { // Use UniqueInstance here because we never want to cache the wrapper. It only gets used once and then disposed. - using DrawingComWrappers.IPicture picture = (DrawingComWrappers.IPicture)DrawingComWrappers.Instance + using DrawingCom.IPicture picture = (DrawingCom.IPicture)DrawingCom.Instance .GetOrCreateObjectForComInstance(lpPicture, CreateObjectFlags.UniqueInstance); var gpStream = new GPStream(outputStream, makeSeekable: false); - streamPtr = DrawingComWrappers.Instance.GetOrCreateComInterfaceForObject(gpStream, CreateComInterfaceFlags.None); + streamPtr = DrawingCom.Instance.GetOrCreateComInterfaceForObject(gpStream, CreateComInterfaceFlags.None); - CheckSaveAsFileResult(picture.SaveAsFile(streamPtr, -1, null)); + DrawingCom.ThrowExceptionForHR(picture.SaveAsFile(streamPtr, -1, null)); } finally { @@ -61,13 +61,6 @@ public unsafe void Save(Stream outputStream) } } - private static void CheckSaveAsFileResult(int errorCode) - { - // Pass -1 for errorInfo to indicate that Windows' GetErrorInfo shouldn't be called, and only - // throw the Exception corresponding to the specified errorCode. - Marshal.ThrowExceptionForHR(errorCode, errorInfo: new IntPtr(-1)); - } - [DllImport(Interop.Libraries.Oleaut32)] private static unsafe extern int OleCreatePictureIndirect(PICTDESC* pictdesc, Guid* refiid, int fOwn, IntPtr* lplpvObj); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs index a6dd53744a974..ccea5ff42d52e 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs @@ -23,16 +23,7 @@ public static Image FromStream(Stream stream, bool useEmbeddedColorManagement, b if (stream == null) throw new ArgumentNullException(nameof(stream)); - IntPtr image = IntPtr.Zero; - - if (useEmbeddedColorManagement) - { - Gdip.CheckStatus(Gdip.GdipLoadImageFromStreamICM(new GPStream(stream), out image)); - } - else - { - Gdip.CheckStatus(Gdip.GdipLoadImageFromStream(new GPStream(stream), out image)); - } + IntPtr image = LoadGdipImageFromStream(new GPStream(stream), useEmbeddedColorManagement); if (validateImageData) ValidateImage(image); @@ -45,9 +36,7 @@ public static Image FromStream(Stream stream, bool useEmbeddedColorManagement, b // Used for serialization private IntPtr InitializeFromStream(Stream stream) { - IntPtr image = IntPtr.Zero; - - Gdip.CheckStatus(Gdip.GdipLoadImageFromStream(new GPStream(stream), out image)); + IntPtr image = LoadGdipImageFromStream(new GPStream(stream), useEmbeddedColorManagement: false); ValidateImage(image); nativeImage = image; @@ -59,6 +48,22 @@ private IntPtr InitializeFromStream(Stream stream) return image; } + private static unsafe IntPtr LoadGdipImageFromStream(GPStream stream, bool useEmbeddedColorManagement) + { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(stream); + + IntPtr image = IntPtr.Zero; + if (useEmbeddedColorManagement) + { + Gdip.CheckStatus(Gdip.GdipLoadImageFromStreamICM(streamWrapper.Ptr, &image)); + } + else + { + Gdip.CheckStatus(Gdip.GdipLoadImageFromStream(streamWrapper.Ptr, &image)); + } + return image; + } + internal Image(IntPtr nativeImage) => SetNativeImage(nativeImage); /// @@ -240,11 +245,15 @@ public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters? encod if (!saved) { - Gdip.CheckStatus(Gdip.GdipSaveImageToStream( - new HandleRef(this, nativeImage), - new GPStream(stream, makeSeekable: false), - ref g, - new HandleRef(encoderParams, encoderParamsMemory))); + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream, makeSeekable: false)); + unsafe + { + Gdip.CheckStatus(Gdip.GdipSaveImageToStream( + new HandleRef(this, nativeImage), + streamWrapper.Ptr, + &g, + new HandleRef(encoderParams, encoderParamsMemory))); + } } } finally diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs index 447799333febf..4d8dccb01e71f 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs @@ -1,11 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Runtime.InteropServices; -using System.IO; using System.Drawing.Internal; +using System.IO; +using System.Runtime.InteropServices; using Gdip = System.Drawing.SafeNativeMethods.Gdip; -using System.Runtime.Serialization; namespace System.Drawing.Imaging { @@ -27,14 +26,18 @@ public Metafile(IntPtr hmetafile, WmfPlaceableFileHeader wmfHeader) : /// /// Initializes a new instance of the class from the specified stream. /// - public Metafile(Stream stream) + public unsafe Metafile(Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } - Gdip.CheckStatus(Gdip.GdipCreateMetafileFromStream(new GPStream(stream), out IntPtr metafile)); + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; + Gdip.CheckStatus(Gdip.GdipCreateMetafileFromStream(streamWrapper.Ptr, &metafile)); + SetNativeImage(metafile); } @@ -145,16 +148,19 @@ public Metafile(string fileName, IntPtr referenceHdc, Rectangle frameRect, Metaf /// /// Initializes a new instance of the class from the specified data stream. /// - public Metafile(Stream stream, IntPtr referenceHdc, EmfType type, string? description) + public unsafe Metafile(Stream stream, IntPtr referenceHdc, EmfType type, string? description) { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, IntPtr.Zero, MetafileFrameUnit.GdiCompatible, description, - out IntPtr metafile)); + &metafile)); SetNativeImage(metafile); } @@ -162,16 +168,19 @@ public Metafile(Stream stream, IntPtr referenceHdc, EmfType type, string? descri /// /// Initializes a new instance of the class with the specified filename. /// - public Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + public unsafe Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, - ref frameRect, + &frameRect, frameUnit, description, - out IntPtr metafile)); + &metafile)); SetNativeImage(metafile); } @@ -179,31 +188,32 @@ public Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, Metafi /// /// Initializes a new instance of the class with the specified filename. /// - public Metafile(Stream stream, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + public unsafe Metafile(Stream stream, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) { - IntPtr metafile = IntPtr.Zero; + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + IntPtr metafile = IntPtr.Zero; if (frameRect.IsEmpty) { Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, IntPtr.Zero, frameUnit, description, - out metafile)); + &metafile)); } else { Gdip.CheckStatus(Gdip.GdipRecordMetafileStreamI( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, - ref frameRect, + &frameRect, frameUnit, description, - out metafile)); + &metafile)); } SetNativeImage(metafile); @@ -292,7 +302,8 @@ public static MetafileHeader GetMetafileHeader(Stream stream) try { - Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromStream(new GPStream(stream), memory)); + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromStream(streamWrapper.Ptr, memory)); int[] type = new int[] { 0 }; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.COMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.COMWrappers.cs new file mode 100644 index 0000000000000..65079d5698a4b --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.COMWrappers.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Runtime.InteropServices; + +namespace System.Drawing.Internal +{ + internal sealed partial class GPStream : Interop.Ole32.IStream + { + public unsafe Interop.HRESULT Clone(IntPtr* ppstm) + { + if (ppstm == null) + { + return Interop.HRESULT.STG_E_INVALIDPOINTER; + } + + // The cloned object should have the same current "position" + var clone = new GPStream(_dataStream) + { + _virtualPosition = _virtualPosition + }; + + *ppstm = DrawingCom.Instance.GetOrCreateComInterfaceForObject(clone, CreateComInterfaceFlags.None); + + return Interop.HRESULT.S_OK; + } + + public unsafe Interop.HRESULT CopyTo(IntPtr pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) + { + byte[] buffer = ArrayPool.Shared.Rent(4096); + + ulong remaining = cb; + ulong totalWritten = 0; + ulong totalRead = 0; + + fixed (byte* b = buffer) + { + while (remaining > 0) + { + uint read = remaining < (ulong)buffer.Length ? (uint)remaining : (uint)buffer.Length; + Read(b, read, &read); + remaining -= read; + totalRead += read; + + if (read == 0) + { + break; + } + + uint written; + Interop.HRESULT hr = (Interop.HRESULT)WriteToStream(pstm, b, read, &written); + if (hr != Interop.HRESULT.S_OK) + { + return hr; + } + totalWritten += written; + } + } + + ArrayPool.Shared.Return(buffer); + + if (pcbRead != null) + { + *pcbRead = totalRead; + } + + if (pcbWritten != null) + { + *pcbWritten = totalWritten; + } + + return Interop.HRESULT.S_OK; + } + + private static unsafe int WriteToStream(IntPtr pstm, byte* pv, uint cb, uint* pcbWritten) + { + return ((delegate* unmanaged)(*(*(void***)pstm + 4 /* IStream.Write slot */))) + (pstm, pv, cb, pcbWritten); + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.NoCOMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.NoCOMWrappers.cs new file mode 100644 index 0000000000000..0c4e56ef8d685 --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.NoCOMWrappers.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; + +namespace System.Drawing.Internal +{ + internal sealed partial class GPStream : Interop.Ole32.IStream + { + public Interop.Ole32.IStream Clone() + { + // The cloned object should have the same current "position" + return new GPStream(_dataStream) + { + _virtualPosition = _virtualPosition + }; + } + + public unsafe void CopyTo(Interop.Ole32.IStream pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) + { + byte[] buffer = ArrayPool.Shared.Rent(4096); + + ulong remaining = cb; + ulong totalWritten = 0; + ulong totalRead = 0; + + fixed (byte* b = buffer) + { + while (remaining > 0) + { + uint read = remaining < (ulong)buffer.Length ? (uint)remaining : (uint)buffer.Length; + Read(b, read, &read); + remaining -= read; + totalRead += read; + + if (read == 0) + { + break; + } + + uint written; + pstm.Write(b, read, &written); + totalWritten += written; + } + } + + ArrayPool.Shared.Return(buffer); + + if (pcbRead != null) + *pcbRead = totalRead; + + if (pcbWritten != null) + *pcbWritten = totalWritten; + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs index dd6a8a16350ff..524be173b52cf 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs @@ -1,12 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; using System.IO; namespace System.Drawing.Internal { - internal sealed class GPStream : Interop.Ole32.IStream + internal sealed partial class GPStream : Interop.Ole32.IStream { private readonly Stream _dataStream; @@ -41,15 +40,6 @@ private void ActualizeVirtualPosition() _virtualPosition = -1; } - public Interop.Ole32.IStream Clone() - { - // The cloned object should have the same current "position" - return new GPStream(_dataStream) - { - _virtualPosition = _virtualPosition - }; - } - public void Commit(uint grfCommitFlags) { _dataStream.Flush(); @@ -58,43 +48,6 @@ public void Commit(uint grfCommitFlags) ActualizeVirtualPosition(); } - public unsafe void CopyTo(Interop.Ole32.IStream pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) - { - byte[] buffer = ArrayPool.Shared.Rent(4096); - - ulong remaining = cb; - ulong totalWritten = 0; - ulong totalRead = 0; - - fixed (byte* b = buffer) - { - while (remaining > 0) - { - uint read = remaining < (ulong)buffer.Length ? (uint)remaining : (uint)buffer.Length; - Read(b, read, &read); - remaining -= read; - totalRead += read; - - if (read == 0) - { - break; - } - - uint written; - pstm.Write(b, read, &written); - totalWritten += written; - } - } - - ArrayPool.Shared.Return(buffer); - - if (pcbRead != null) - *pcbRead = totalRead; - - if (pcbWritten != null) - *pcbWritten = totalWritten; - } - public unsafe void Read(byte* pv, uint cb, uint* pcbRead) { ActualizeVirtualPosition();