diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index 7f8ccaba794415..38c6d68f901ab4 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -8,7 +8,7 @@ {WasmAppBuilderTasksAssemblyPath} {MicrosoftNetCoreAppRuntimePackRidDir} {RepositoryEngineeringDir} - {NetCoreAppCurrent} + {TestTargetFramework} {RuntimeIdentifier} {UseMonoRuntime} {TargetingPackDir} diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets index f43dbee3b4e1f2..6f084b6ddb41ef 100644 --- a/eng/testing/linker/trimmingTests.targets +++ b/eng/testing/linker/trimmingTests.targets @@ -25,14 +25,16 @@ $([MSBuild]::NormalizeDirectory('$(TrimmingTestProjectsDir)', '$(MSBuildProjectName)', '%(Filename)', '$(PackageRID)')) $(PackageRID) + $(NetCoreAppCurrent) + $(NetCoreAppCurrent)-%(TestConsoleAppSourceFiles.TargetOS) - + %(ProjectDir)project.csproj - $([MSBuild]::NormalizePath('%(ProjectDir)', 'bin', '$(Configuration)', '$(NetCoreAppCurrent)', '%(TestRuntimeIdentifier)', 'publish', 'project')) - $([MSBuild]::NormalizePath('%(ProjectDir)', 'bin', '$(Configuration)', '$(NetCoreAppCurrent)', '%(TestRuntimeIdentifier)', 'AppBundle', 'run-v8.sh')) - $([MSBuild]::NormalizeDirectory('%(ProjectDir)', 'bin', '$(Configuration)', '$(NetCoreAppCurrent)', '%(TestRuntimeIdentifier)', 'publish')) - $([MSBuild]::NormalizeDirectory('%(ProjectDir)', 'bin', '$(Configuration)', '$(NetCoreAppCurrent)', '%(TestRuntimeIdentifier)', 'AppBundle')) + $([MSBuild]::NormalizePath('%(ProjectDir)', 'bin', '$(Configuration)', '%(TestTargetFramework)', '%(TestRuntimeIdentifier)', 'publish', 'project')) + $([MSBuild]::NormalizePath('%(ProjectDir)', 'bin', '$(Configuration)', '%(TestTargetFramework)', '%(TestRuntimeIdentifier)', 'AppBundle', 'run-v8.sh')) + $([MSBuild]::NormalizeDirectory('%(ProjectDir)', 'bin', '$(Configuration)', '%(TestTargetFramework)', '%(TestRuntimeIdentifier)', 'publish')) + $([MSBuild]::NormalizeDirectory('%(ProjectDir)', 'bin', '$(Configuration)', '%(TestTargetFramework)', '%(TestRuntimeIdentifier)', 'AppBundle')) @@ -70,7 +72,7 @@ . + /// Stat flags for . /// /// internal enum STATFLAG : uint diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml index 3f367cccd24969..a0b7401fb0ac23 100644 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml @@ -7,12 +7,6 @@ member M:System.Drawing.Bitmap.#ctor(System.IO.Stream,System.Boolean) - - ILLink - IL2050 - member - M:System.Drawing.Icon.Save(System.IO.Stream) - ILLink IL2050 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 092ea6af165e91..b02d55a46c6a2b 100644 --- a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -317,6 +317,13 @@ + + + + + + + diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs new file mode 100644 index 00000000000000..7895381853c4a4 --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs @@ -0,0 +1,314 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + /// + /// The ComWrappers implementation for System.Drawing.Common's COM interop usages. + /// + /// Supports IStream and IPicture COM interfaces. + /// + internal unsafe class DrawingComWrappers : 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(); + + private DrawingComWrappers() { } + + private static ComInterfaceEntry* InitializeComInterfaceEntry() + { + GetIUnknownImpl(out IntPtr fpQueryInteface, out IntPtr fpAddRef, out IntPtr fpRelease); + + IntPtr iStreamVtbl = IStreamVtbl.Create(fpQueryInteface, fpAddRef, fpRelease); + + ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DrawingComWrappers), sizeof(ComInterfaceEntry)); + wrapperEntry->IID = IID_IStream; + wrapperEntry->Vtable = iStreamVtbl; + return wrapperEntry; + } + + protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + Debug.Assert(obj is Interop.Ole32.IStream); + Debug.Assert(s_wrapperEntry != null); + + // Always return the same table mappings. + count = 1; + return s_wrapperEntry; + } + + protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) + { + Debug.Assert(flags == CreateObjectFlags.UniqueInstance); + + Guid pictureIID = IPicture.IID; + int hr = Marshal.QueryInterface(externalComObject, ref pictureIID, out IntPtr comObject); + if (hr == S_OK) + { + return new PictureWrapper(comObject); + } + + throw new NotImplementedException(); + } + + protected override void ReleaseObjects(IEnumerable objects) + { + throw new NotImplementedException(); + } + + internal static class IStreamVtbl + { + public static IntPtr Create(IntPtr fpQueryInteface, IntPtr fpAddRef, IntPtr fpRelease) + { + IntPtr* vtblRaw = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IStreamVtbl), IntPtr.Size * 14); + vtblRaw[0] = fpQueryInteface; + vtblRaw[1] = fpAddRef; + vtblRaw[2] = fpRelease; + vtblRaw[3] = (IntPtr)(delegate* unmanaged)&Read; + vtblRaw[4] = (IntPtr)(delegate* unmanaged)&Write; + vtblRaw[5] = (IntPtr)(delegate* unmanaged)&Seek; + vtblRaw[6] = (IntPtr)(delegate* unmanaged)&SetSize; + vtblRaw[7] = (IntPtr)(delegate* unmanaged)&CopyTo; + vtblRaw[8] = (IntPtr)(delegate* unmanaged)&Commit; + vtblRaw[9] = (IntPtr)(delegate* unmanaged)&Revert; + vtblRaw[10] = (IntPtr)(delegate* unmanaged)&LockRegion; + vtblRaw[11] = (IntPtr)(delegate* unmanaged)&UnlockRegion; + vtblRaw[12] = (IntPtr)(delegate* unmanaged)&Stat; + vtblRaw[13] = (IntPtr)(delegate* unmanaged)&Clone; + + return (IntPtr)vtblRaw; + } + + [UnmanagedCallersOnly] + private static int Read(IntPtr thisPtr, byte* pv, uint cb, uint* pcbRead) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Read(pv, cb, pcbRead); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Write(IntPtr thisPtr, byte* pv, uint cb, uint* pcbWritten) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Write(pv, cb, pcbWritten); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Seek(IntPtr thisPtr, long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Seek(dlibMove, dwOrigin, plibNewPosition); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int SetSize(IntPtr thisPtr, ulong libNewSize) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.SetSize(libNewSize); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int CopyTo(IntPtr thisPtr, IntPtr pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + Interop.Ole32.IStream pstmStream = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)pstm); + + inst.CopyTo(pstmStream, cb, pcbRead, pcbWritten); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Commit(IntPtr thisPtr, uint grfCommitFlags) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Commit(grfCommitFlags); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Revert(IntPtr thisPtr) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Revert(); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int LockRegion(IntPtr thisPtr, ulong libOffset, ulong cb, uint dwLockType) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + return (int)inst.LockRegion(libOffset, cb, dwLockType); + } + catch (Exception e) + { + return e.HResult; + } + } + + [UnmanagedCallersOnly] + private static int UnlockRegion(IntPtr thisPtr, ulong libOffset, ulong cb, uint dwLockType) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + return (int)inst.UnlockRegion(libOffset, cb, dwLockType); + } + catch (Exception e) + { + return e.HResult; + } + } + + [UnmanagedCallersOnly] + private static int Stat(IntPtr thisPtr, Interop.Ole32.STATSTG* pstatstg, Interop.Ole32.STATFLAG grfStatFlag) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Stat(pstatstg, grfStatFlag); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [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); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + } + + internal interface IPicture : IDisposable + { + static readonly Guid IID = new Guid(0x7BF80980, 0xBF32, 0x101A, 0x8B, 0xBB, 0, 0xAA, 0x00, 0x30, 0x0C, 0xAB); + + // NOTE: Only SaveAsFile is invoked. The other methods on IPicture are not necessary + + int SaveAsFile(IntPtr pstm, int fSaveMemCopy, int* pcbSize); + } + + private class PictureWrapper : IPicture + { + private readonly IntPtr _wrappedInstance; + + public PictureWrapper(IntPtr wrappedInstance) + { + _wrappedInstance = wrappedInstance; + } + + public void Dispose() + { + Marshal.Release(_wrappedInstance); + } + + 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)); + + try + { + return ((delegate* unmanaged)(*(*(void***)_wrappedInstance + 15 /* IPicture.SaveAsFile slot */))) + (_wrappedInstance, pstmImpl, fSaveMemCopy, pcbSize); + } + finally + { + Marshal.Release(pstmImpl); + } + } + } + } +} 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 new file mode 100644 index 00000000000000..a03ad5ed133bbe --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Drawing.Internal; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System.Drawing +{ + public sealed partial class Icon : MarshalByRefObject, ICloneable, IDisposable, ISerializable + { + public unsafe void Save(Stream outputStream) + { + if (_iconData != null) + { + outputStream.Write(_iconData, 0, _iconData.Length); + } + else + { + if (outputStream == null) + throw new ArgumentNullException(nameof(outputStream)); + + // Ideally, we would pick apart the icon using + // GetIconInfo, and then pull the individual bitmaps out, + // converting them to DIBS and saving them into the file. + // 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; + IntPtr lpPicture; + Marshal.ThrowExceptionForHR(OleCreatePictureIndirect(&pictdesc, &iid, fOwn: 0, &lpPicture)); + + IntPtr streamPtr = IntPtr.Zero; + 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 + .GetOrCreateObjectForComInstance(lpPicture, CreateObjectFlags.UniqueInstance); + + var gpStream = new GPStream(outputStream, makeSeekable: false); + streamPtr = DrawingComWrappers.Instance.GetOrCreateComInterfaceForObject(gpStream, CreateComInterfaceFlags.None); + + CheckSaveAsFileResult(picture.SaveAsFile(streamPtr, -1, null)); + } + finally + { + if (streamPtr != IntPtr.Zero) + { + int count = Marshal.Release(streamPtr); + Debug.Assert(count == 0); + } + + if (lpPicture != IntPtr.Zero) + { + int count = Marshal.Release(lpPicture); + Debug.Assert(count == 0); + } + } + } + } + + 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); + + [StructLayout(LayoutKind.Sequential)] + private readonly struct PICTDESC + { + public readonly int SizeOfStruct; + public readonly int PicType; + public readonly IntPtr Icon; + + private unsafe PICTDESC(int picType, IntPtr hicon) + { + SizeOfStruct = sizeof(PICTDESC); + PicType = picType; + Icon = hicon; + } + + public static PICTDESC CreateIconPICTDESC(IntPtr hicon) => + new PICTDESC(Ole.PICTYPE_ICON, hicon); + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.NoCOMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.NoCOMWrappers.cs new file mode 100644 index 00000000000000..34a5d19a0d9c83 --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.NoCOMWrappers.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Drawing.Internal; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System.Drawing +{ + public sealed partial class Icon : MarshalByRefObject, ICloneable, IDisposable, ISerializable + { + public void Save(Stream outputStream) + { + if (_iconData != null) + { + outputStream.Write(_iconData, 0, _iconData.Length); + } + else + { + // Ideally, we would pick apart the icon using + // GetIconInfo, and then pull the individual bitmaps out, + // converting them to DIBS and saving them into the file. + // But, in the interest of simplicity, we just call to + // OLE to do it for us. + PICTDESC pictdesc = PICTDESC.CreateIconPICTDESC(Handle); + Guid g = typeof(IPicture).GUID; + IPicture picture = OleCreatePictureIndirect(pictdesc, ref g, false); + + if (picture != null) + { + try + { + if (outputStream == null) + throw new ArgumentNullException(nameof(outputStream)); + + picture.SaveAsFile(new GPStream(outputStream, makeSeekable: false), -1, out int temp); + } + finally + { + Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + Marshal.ReleaseComObject(picture); + } + } + } + } + + [DllImport(Interop.Libraries.Oleaut32, PreserveSig = false)] + internal static extern IPicture OleCreatePictureIndirect(PICTDESC pictdesc, [In]ref Guid refiid, bool fOwn); + + [ComImport] + [Guid("7BF80980-BF32-101A-8BBB-00AA00300CAB")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IPicture + { + IntPtr GetHandle(); + + IntPtr GetHPal(); + + [return: MarshalAs(UnmanagedType.I2)] + short GetPictureType(); + + int GetWidth(); + + int GetHeight(); + + void Render(); + + void SetHPal([In] IntPtr phpal); + + IntPtr GetCurDC(); + + void SelectPicture([In] IntPtr hdcIn, + [Out, MarshalAs(UnmanagedType.LPArray)] int[] phdcOut, + [Out, MarshalAs(UnmanagedType.LPArray)] int[] phbmpOut); + + [return: MarshalAs(UnmanagedType.Bool)] + bool GetKeepOriginalFormat(); + + void SetKeepOriginalFormat([In, MarshalAs(UnmanagedType.Bool)] bool pfkeep); + + void PictureChanged(); + + [PreserveSig] + int SaveAsFile([In, MarshalAs(UnmanagedType.Interface)] Interop.Ole32.IStream pstm, + [In] int fSaveMemCopy, + [Out] out int pcbSize); + + int GetAttributes(); + + void SetHdc([In] IntPtr hdc); + } + + [StructLayout(LayoutKind.Sequential)] + internal sealed class PICTDESC + { + internal int cbSizeOfStruct; + public int picType; + internal IntPtr union1; + internal int union2; + internal int union3; + + public static PICTDESC CreateIconPICTDESC(IntPtr hicon) + { + return new PICTDESC() + { + cbSizeOfStruct = 12, + picType = Ole.PICTYPE_ICON, + union1 = hicon + }; + } + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs index 6bd26a336c1bff..4acd6376e4c437 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs @@ -637,41 +637,6 @@ private unsafe void Initialize(int width, int height) } } - public void Save(Stream outputStream) - { - if (_iconData != null) - { - outputStream.Write(_iconData, 0, _iconData.Length); - } - else - { - // Ideally, we would pick apart the icon using - // GetIconInfo, and then pull the individual bitmaps out, - // converting them to DIBS and saving them into the file. - // But, in the interest of simplicity, we just call to - // OLE to do it for us. - PICTDESC pictdesc = PICTDESC.CreateIconPICTDESC(Handle); - Guid g = typeof(IPicture).GUID; - IPicture picture = OleCreatePictureIndirect(pictdesc, ref g, false); - - if (picture != null) - { - try - { - if (outputStream == null) - throw new ArgumentNullException(nameof(outputStream)); - - picture.SaveAsFile(new GPStream(outputStream, makeSeekable: false), -1, out int temp); - } - finally - { - Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); - Marshal.ReleaseComObject(picture); - } - } - } - } - private unsafe void CopyBitmapData(BitmapData sourceData, BitmapData targetData) { byte* srcPtr = (byte*)sourceData.Scan0; @@ -905,75 +870,9 @@ private bool HasPngSignature() public override string ToString() => SR.toStringIcon; - [DllImport(Interop.Libraries.Oleaut32, PreserveSig = false)] - internal static extern IPicture OleCreatePictureIndirect(PICTDESC pictdesc, [In]ref Guid refiid, bool fOwn); - - [ComImport] - [Guid("7BF80980-BF32-101A-8BBB-00AA00300CAB")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IPicture - { - IntPtr GetHandle(); - - IntPtr GetHPal(); - - [return: MarshalAs(UnmanagedType.I2)] - short GetPictureType(); - - int GetWidth(); - - int GetHeight(); - - void Render(); - - void SetHPal([In] IntPtr phpal); - - IntPtr GetCurDC(); - - void SelectPicture([In] IntPtr hdcIn, - [Out, MarshalAs(UnmanagedType.LPArray)] int[] phdcOut, - [Out, MarshalAs(UnmanagedType.LPArray)] int[] phbmpOut); - - [return: MarshalAs(UnmanagedType.Bool)] - bool GetKeepOriginalFormat(); - - void SetKeepOriginalFormat([In, MarshalAs(UnmanagedType.Bool)] bool pfkeep); - - void PictureChanged(); - - [PreserveSig] - int SaveAsFile([In, MarshalAs(UnmanagedType.Interface)] Interop.Ole32.IStream pstm, - [In] int fSaveMemCopy, - [Out] out int pcbSize); - - int GetAttributes(); - - void SetHdc([In] IntPtr hdc); - } - internal static class Ole { public const int PICTYPE_ICON = 3; } - - [StructLayout(LayoutKind.Sequential)] - internal sealed class PICTDESC - { - internal int cbSizeOfStruct; - public int picType; - internal IntPtr union1; - internal int union2; - internal int union3; - - public static PICTDESC CreateIconPICTDESC(IntPtr hicon) - { - return new PICTDESC() - { - cbSizeOfStruct = 12, - picType = Ole.PICTYPE_ICON, - union1 = hicon - }; - } - } } } 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 6040cd85c982c6..dd6a8a16350ff0 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 @@ -3,7 +3,6 @@ using System.Buffers; using System.IO; -using System.Runtime.InteropServices; namespace System.Drawing.Internal { @@ -177,9 +176,14 @@ public void SetSize(ulong value) _dataStream.SetLength(checked((long)value)); } - public void Stat(out Interop.Ole32.STATSTG pstatstg, Interop.Ole32.STATFLAG grfStatFlag) + public unsafe void Stat(Interop.Ole32.STATSTG* pstatstg, Interop.Ole32.STATFLAG grfStatFlag) { - pstatstg = new Interop.Ole32.STATSTG + if (pstatstg == null) + { + throw new ArgumentNullException(nameof(pstatstg)); + } + + *pstatstg = new Interop.Ole32.STATSTG { cbSize = (ulong)_dataStream.Length, type = Interop.Ole32.STGTY.STGTY_STREAM, @@ -195,7 +199,7 @@ public void Stat(out Interop.Ole32.STATSTG pstatstg, Interop.Ole32.STATFLAG grfS if (grfStatFlag == Interop.Ole32.STATFLAG.STATFLAG_DEFAULT) { // Caller wants a name - pstatstg.AllocName(_dataStream is FileStream fs ? fs.Name : _dataStream.ToString()); + pstatstg->AllocName(_dataStream is FileStream fs ? fs.Name : _dataStream.ToString()); } } diff --git a/src/libraries/System.Drawing.Common/tests/IconTests.cs b/src/libraries/System.Drawing.Common/tests/IconTests.cs index 2ad23d5f18d5fd..108e277aee25bd 100644 --- a/src/libraries/System.Drawing.Common/tests/IconTests.cs +++ b/src/libraries/System.Drawing.Common/tests/IconTests.cs @@ -528,7 +528,7 @@ public void Save_ClosedOutputStreamIconData_ThrowsException() [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] [ActiveIssue("https://github.com/dotnet/runtime/issues/47759", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ConditionalFact(Helpers.IsDrawingSupported)] - public void Save_ClosedOutputStreamNoIconData_DoesNothing() + public void Save_ClosedOutputStreamNoIconData() { using (var source = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"))) using (var icon = Icon.FromHandle(source.Handle)) @@ -536,7 +536,16 @@ public void Save_ClosedOutputStreamNoIconData_DoesNothing() var stream = new MemoryStream(); stream.Close(); - icon.Save(stream); + if (PlatformDetection.IsNetFramework) + { + // The ObjectDisposedException is ignored in previous .NET versions, + // so the following does nothing. + icon.Save(stream); + } + else + { + Assert.Throws(() => icon.Save(stream)); + } } } diff --git a/src/libraries/System.Drawing.Common/tests/TrimmingTests/IconSave.cs b/src/libraries/System.Drawing.Common/tests/TrimmingTests/IconSave.cs new file mode 100644 index 00000000000000..0e15a645c5135e --- /dev/null +++ b/src/libraries/System.Drawing.Common/tests/TrimmingTests/IconSave.cs @@ -0,0 +1,28 @@ +// 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; +using System.IO; + +/// +/// Tests that Icon.Save works when the Icon is loaded from an IntPtr. +/// This causes COM to be used to save the Icon. +/// +class Program +{ + static int Main(string[] args) + { + Icon i = SystemIcons.WinLogo; + using MemoryStream stream = new(); + + i.Save(stream); + + // ensure something was written to the stream + if (stream.Position == 0) + { + return -1; + } + + return 100; + } +} diff --git a/src/libraries/System.Drawing.Common/tests/TrimmingTests/System.Drawing.Common.TrimmingTests.proj b/src/libraries/System.Drawing.Common/tests/TrimmingTests/System.Drawing.Common.TrimmingTests.proj new file mode 100644 index 00000000000000..71883b846cbb1b --- /dev/null +++ b/src/libraries/System.Drawing.Common/tests/TrimmingTests/System.Drawing.Common.TrimmingTests.proj @@ -0,0 +1,15 @@ + + + + + System.Drawing.Common + + + + + + + +