-
Notifications
You must be signed in to change notification settings - Fork 967
/
Icon.ComWrappers.cs
93 lines (81 loc) · 3.17 KB
/
Icon.ComWrappers.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// 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.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using static Interop;
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 = DrawingCom.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 DrawingCom.IPicture picture = (DrawingCom.IPicture)DrawingCom.Instance
.GetOrCreateObjectForComInstance(lpPicture, CreateObjectFlags.UniqueInstance);
var gpStream = new GPStream(outputStream, makeSeekable: false);
streamPtr = DrawingCom.Instance.GetOrCreateComInterfaceForObject(gpStream, CreateComInterfaceFlags.None);
DrawingCom.ThrowExceptionForHR(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);
}
}
}
}
#if NET7_0_OR_GREATER
[LibraryImport(Libraries.Oleaut32)]
private static unsafe partial int OleCreatePictureIndirect(
#else
[DllImport(Libraries.Oleaut32)]
private static extern unsafe int OleCreatePictureIndirect(
#endif
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(Ole.PICTYPE_ICON, hicon);
}
}