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
+
+
+
+
+
+
+
+