From f4087574c0547630895f7fa9b5e18fcbbb6976ba Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Sat, 18 Apr 2020 23:22:39 +0800 Subject: [PATCH] fixed a bug of Screenshot on win7 --- .../Controls/Image/GifImage.cs | 87 ++--- .../Controls/Screenshot/ScreenshotWindow.cs | 62 ++- .../Controls/Window/BlurWindow.cs | 13 +- .../Data/Enum/ImageCodecFlags.cs | 18 + .../HandyControl_Shared/Data/Gif/GPStream.cs | 16 +- .../Data/Gif/GifPropertyItemInternal.cs | 4 +- .../Data/ImageCodecInfo.cs | 158 ++++++++ .../HandyControl_Shared.projitems | 3 +- .../Tools/Helper/ExternDllHelper.cs | 362 ------------------ .../Tools/Interop/InteropMethods.cs | 303 +++++++++++++-- .../Tools/Interop/InteropValues.cs | 274 +++++++++++++ 11 files changed, 850 insertions(+), 450 deletions(-) create mode 100644 src/Shared/HandyControl_Shared/Data/Enum/ImageCodecFlags.cs create mode 100644 src/Shared/HandyControl_Shared/Data/ImageCodecInfo.cs delete mode 100644 src/Shared/HandyControl_Shared/Tools/Helper/ExternDllHelper.cs diff --git a/src/Shared/HandyControl_Shared/Controls/Image/GifImage.cs b/src/Shared/HandyControl_Shared/Controls/Image/GifImage.cs index 114e273b..78e52ee0 100644 --- a/src/Shared/HandyControl_Shared/Controls/Image/GifImage.cs +++ b/src/Shared/HandyControl_Shared/Controls/Image/GifImage.cs @@ -13,6 +13,7 @@ using System.Windows.Media.Imaging; using System.Windows.Resources; using HandyControl.Data; using HandyControl.Tools; +using HandyControl.Tools.Interop; namespace HandyControl.Controls { @@ -123,7 +124,7 @@ namespace HandyControl.Controls try { StopAnimate(); - ExternDllHelper.Gdip.GdipDisposeImage(new HandleRef(this, NativeImage)); + InteropMethods.Gdip.GdipDisposeImage(new HandleRef(this, NativeImage)); } catch { @@ -139,17 +140,17 @@ namespace HandyControl.Controls { filename = Path.GetFullPath(filename); - var status = ExternDllHelper.Gdip.GdipCreateBitmapFromFile(filename, out var bitmap); + var status = InteropMethods.Gdip.GdipCreateBitmapFromFile(filename, out var bitmap); - if (status != ExternDllHelper.Gdip.Ok) - throw ExternDllHelper.Gdip.StatusException(status); + if (status != InteropMethods.Gdip.Ok) + throw InteropMethods.Gdip.StatusException(status); - status = ExternDllHelper.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap)); + status = InteropMethods.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap)); - if (status != ExternDllHelper.Gdip.Ok) + if (status != InteropMethods.Gdip.Ok) { - ExternDllHelper.Gdip.GdipDisposeImage(new HandleRef(null, bitmap)); - throw ExternDllHelper.Gdip.StatusException(status); + InteropMethods.Gdip.GdipDisposeImage(new HandleRef(null, bitmap)); + throw InteropMethods.Gdip.StatusException(status); } SetNativeImage(bitmap); @@ -162,17 +163,17 @@ namespace HandyControl.Controls if (stream == null) throw new ArgumentException("stream null"); - var status = ExternDllHelper.Gdip.GdipCreateBitmapFromStream(new GPStream(stream), out var bitmap); + var status = InteropMethods.Gdip.GdipCreateBitmapFromStream(new GPStream(stream), out var bitmap); - if (status != ExternDllHelper.Gdip.Ok) - throw ExternDllHelper.Gdip.StatusException(status); + if (status != InteropMethods.Gdip.Ok) + throw InteropMethods.Gdip.StatusException(status); - status = ExternDllHelper.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap)); + status = InteropMethods.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap)); - if (status != ExternDllHelper.Gdip.Ok) + if (status != InteropMethods.Gdip.Ok) { - ExternDllHelper.Gdip.GdipDisposeImage(new HandleRef(null, bitmap)); - throw ExternDllHelper.Gdip.StatusException(status); + InteropMethods.Gdip.GdipDisposeImage(new HandleRef(null, bitmap)); + throw InteropMethods.Gdip.StatusException(status); } SetNativeImage(bitmap); @@ -296,10 +297,10 @@ namespace HandyControl.Controls { var guid = new Guid(); - var status = ExternDllHelper.Gdip.GdipGetImageRawFormat(new HandleRef(this, NativeImage), ref guid); + var status = InteropMethods.Gdip.GdipGetImageRawFormat(new HandleRef(this, NativeImage), ref guid); - if (status != ExternDllHelper.Gdip.Ok) - throw ExternDllHelper.Gdip.StatusException(status); + if (status != InteropMethods.Gdip.Ok) + throw InteropMethods.Gdip.StatusException(status); return guid; } @@ -321,11 +322,11 @@ namespace HandyControl.Controls { get { - var status = ExternDllHelper.Gdip.GdipImageGetFrameDimensionsCount(new HandleRef(this, NativeImage), out var count); + var status = InteropMethods.Gdip.GdipImageGetFrameDimensionsCount(new HandleRef(this, NativeImage), out var count); - if (status != ExternDllHelper.Gdip.Ok) + if (status != InteropMethods.Gdip.Ok) { - throw ExternDllHelper.Gdip.StatusException(status); + throw InteropMethods.Gdip.StatusException(status); } if (count <= 0) @@ -338,15 +339,15 @@ namespace HandyControl.Controls var buffer = Marshal.AllocHGlobal(checked(size * count)); if (buffer == IntPtr.Zero) { - throw ExternDllHelper.Gdip.StatusException(ExternDllHelper.Gdip.OutOfMemory); + throw InteropMethods.Gdip.StatusException(InteropMethods.Gdip.OutOfMemory); } - status = ExternDllHelper.Gdip.GdipImageGetFrameDimensionsList(new HandleRef(this, NativeImage), buffer, count); + status = InteropMethods.Gdip.GdipImageGetFrameDimensionsList(new HandleRef(this, NativeImage), buffer, count); - if (status != ExternDllHelper.Gdip.Ok) + if (status != InteropMethods.Gdip.Ok) { Marshal.FreeHGlobal(buffer); - throw ExternDllHelper.Gdip.StatusException(status); + throw InteropMethods.Gdip.StatusException(status); } var guids = new Guid[count]; @@ -355,7 +356,7 @@ namespace HandyControl.Controls { for (var i = 0; i < count; i++) { - guids[i] = (Guid)ExternDllHelper.PtrToStructure((IntPtr)((long)buffer + size * i), typeof(Guid)); + guids[i] = (Guid)InteropMethods.PtrToStructure((IntPtr)((long)buffer + size * i), typeof(Guid)); } } finally @@ -372,10 +373,10 @@ namespace HandyControl.Controls var count = new[] { 0 }; var dimensionId = dimension.Guid; - var status = ExternDllHelper.Gdip.GdipImageGetFrameCount(new HandleRef(this, NativeImage), ref dimensionId, count); + var status = InteropMethods.Gdip.GdipImageGetFrameCount(new HandleRef(this, NativeImage), ref dimensionId, count); - if (status != ExternDllHelper.Gdip.Ok) - throw ExternDllHelper.Gdip.StatusException(status); + if (status != InteropMethods.Gdip.Ok) + throw InteropMethods.Gdip.StatusException(status); return count[0]; } @@ -384,10 +385,10 @@ namespace HandyControl.Controls { GifPropertyItem propitem; - var status = ExternDllHelper.Gdip.GdipGetPropertyItemSize(new HandleRef(this, NativeImage), propid, out var size); + var status = InteropMethods.Gdip.GdipGetPropertyItemSize(new HandleRef(this, NativeImage), propid, out var size); - if (status != ExternDllHelper.Gdip.Ok) - throw ExternDllHelper.Gdip.StatusException(status); + if (status != InteropMethods.Gdip.Ok) + throw InteropMethods.Gdip.StatusException(status); if (size == 0) return null; @@ -395,15 +396,15 @@ namespace HandyControl.Controls var propdata = Marshal.AllocHGlobal(size); if (propdata == IntPtr.Zero) - throw ExternDllHelper.Gdip.StatusException(ExternDllHelper.Gdip.OutOfMemory); + throw InteropMethods.Gdip.StatusException(InteropMethods.Gdip.OutOfMemory); - status = ExternDllHelper.Gdip.GdipGetPropertyItem(new HandleRef(this, NativeImage), propid, size, propdata); + status = InteropMethods.Gdip.GdipGetPropertyItem(new HandleRef(this, NativeImage), propid, size, propdata); try { - if (status != ExternDllHelper.Gdip.Ok) + if (status != InteropMethods.Gdip.Ok) { - throw ExternDllHelper.Gdip.StatusException(status); + throw InteropMethods.Gdip.StatusException(status); } propitem = GifPropertyItemInternal.ConvertFromMemory(propdata, 1)[0]; @@ -421,10 +422,10 @@ namespace HandyControl.Controls var count = new[] { 0 }; var dimensionId = dimension.Guid; - var status = ExternDllHelper.Gdip.GdipImageSelectActiveFrame(new HandleRef(this, NativeImage), ref dimensionId, frameIndex); + var status = InteropMethods.Gdip.GdipImageSelectActiveFrame(new HandleRef(this, NativeImage), ref dimensionId, frameIndex); - if (status != ExternDllHelper.Gdip.Ok) - throw ExternDllHelper.Gdip.StatusException(status); + if (status != InteropMethods.Gdip.Ok) + throw InteropMethods.Gdip.StatusException(status); return count[0]; } @@ -441,16 +442,16 @@ namespace HandyControl.Controls [ResourceConsumption(ResourceScope.Machine)] internal IntPtr GetHbitmap(Color background) { - var status = ExternDllHelper.Gdip.GdipCreateHBITMAPFromBitmap(new HandleRef(this, NativeImage), out var hBitmap, + var status = InteropMethods.Gdip.GdipCreateHBITMAPFromBitmap(new HandleRef(this, NativeImage), out var hBitmap, ColorHelper.ToWin32(background)); if (status == 2 && (Width >= short.MaxValue || Height >= short.MaxValue)) { throw new ArgumentException("GdiplusInvalidSize"); } - if (status != ExternDllHelper.Gdip.Ok && NativeImage != IntPtr.Zero) + if (status != InteropMethods.Gdip.Ok && NativeImage != IntPtr.Zero) { - throw ExternDllHelper.Gdip.StatusException(status); + throw InteropMethods.Gdip.StatusException(status); } return hBitmap; @@ -474,7 +475,7 @@ namespace HandyControl.Controls { if (handle != IntPtr.Zero) { - ExternDllHelper.DeleteObject(handle); + InteropMethods.DeleteObject(handle); } } } diff --git a/src/Shared/HandyControl_Shared/Controls/Screenshot/ScreenshotWindow.cs b/src/Shared/HandyControl_Shared/Controls/Screenshot/ScreenshotWindow.cs index 7b73fd0e..34e800d8 100644 --- a/src/Shared/HandyControl_Shared/Controls/Screenshot/ScreenshotWindow.cs +++ b/src/Shared/HandyControl_Shared/Controls/Screenshot/ScreenshotWindow.cs @@ -1,8 +1,10 @@ using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; using System.Windows.Input; -using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using HandyControl.Data; @@ -31,6 +33,8 @@ namespace HandyControl.Controls private BitmapSource _imageSource; + private static readonly Guid BmpGuid = new Guid("{b96b3cab-0728-11d3-9d7b-0000f81ef32e}"); + #region const private const int IntervalLength = 1; @@ -554,13 +558,63 @@ namespace HandyControl.Controls var desktopWindowWidth = _desktopWindowRect.Right - _desktopWindowRect.Left; var desktopWindowHeight = _desktopWindowRect.Bottom - _desktopWindowRect.Top; - var bitmap = InteropMethods.CreateCompatibleBitmap(hdcSrc, desktopWindowWidth, desktopWindowHeight); - var hOld = InteropMethods.SelectObject(hdcDest, bitmap); + var hbitmap = InteropMethods.CreateCompatibleBitmap(hdcSrc, desktopWindowWidth, desktopWindowHeight); + var hOld = InteropMethods.SelectObject(hdcDest, hbitmap); InteropMethods.BitBlt(hdcDest, 0, 0, desktopWindowWidth, desktopWindowHeight, hdcSrc, 0, 0, InteropValues.SRCCOPY); InteropMethods.SelectObject(hdcDest, hOld); InteropMethods.DeleteDC(hdcDest); InteropMethods.ReleaseDC(_desktopWindowHandle, hdcSrc); - return Imaging.CreateBitmapSourceFromHBitmap(bitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); + + var status = InteropMethods.Gdip.GdipCreateBitmapFromHBITMAP(new HandleRef(null, hbitmap), new HandleRef(null, IntPtr.Zero), out var bitmap); + if (status != InteropMethods.Gdip.Ok) throw InteropMethods.Gdip.StatusException(status); + + using var ms = new MemoryStream(); + status = InteropMethods.Gdip.GdipGetImageEncodersSize(out var numEncoders, out var size); + if (status != InteropMethods.Gdip.Ok) throw InteropMethods.Gdip.StatusException(status); + + var memory = Marshal.AllocHGlobal(size); + try + { + status = InteropMethods.Gdip.GdipGetImageEncoders(numEncoders, size, memory); + if (status != InteropMethods.Gdip.Ok) throw InteropMethods.Gdip.StatusException(status); + + var codecInfo = ImageCodecInfo.ConvertFromMemory(memory, numEncoders).FirstOrDefault(item => item.FormatID.Equals(BmpGuid)); + if (codecInfo == null) throw new Exception("ImageCodecInfo is null"); + + var encoderParamsMemory = IntPtr.Zero; + + try + { + var g = codecInfo.Clsid; + status = InteropMethods.Gdip.GdipSaveImageToStream(new HandleRef(this, bitmap), + new InteropValues.ComStreamFromDataStream(ms), ref g, + new HandleRef(null, encoderParamsMemory)); + } + finally + { + if (encoderParamsMemory != IntPtr.Zero) + { + Marshal.FreeHGlobal(encoderParamsMemory); + } + } + + if (status != InteropMethods.Gdip.Ok) + { + throw InteropMethods.Gdip.StatusException(status); + } + } + finally + { + Marshal.FreeHGlobal(memory); + } + + var bitmapImage = new BitmapImage(); + bitmapImage.BeginInit(); + bitmapImage.CacheOption = BitmapCacheOption.OnLoad; + bitmapImage.StreamSource = ms; + bitmapImage.EndInit(); + bitmapImage.Freeze(); + return bitmapImage; } private void MoveElement(InteropValues.POINT point) diff --git a/src/Shared/HandyControl_Shared/Controls/Window/BlurWindow.cs b/src/Shared/HandyControl_Shared/Controls/Window/BlurWindow.cs index 9defae36..f070ce45 100644 --- a/src/Shared/HandyControl_Shared/Controls/Window/BlurWindow.cs +++ b/src/Shared/HandyControl_Shared/Controls/Window/BlurWindow.cs @@ -3,6 +3,7 @@ using System.Windows; using System.Windows.Media; using HandyControl.Data; using HandyControl.Tools; +using HandyControl.Tools.Interop; namespace HandyControl.Controls { @@ -33,12 +34,12 @@ namespace HandyControl.Controls return; } - var accentPolicy = new ExternDllHelper.ACCENTPOLICY(); + var accentPolicy = new InteropValues.ACCENTPOLICY(); var accentPolicySize = Marshal.SizeOf(accentPolicy); accentPolicy.AccentState = SystemVersionInfo < SystemVersionInfo.Windows10_1809 - ? ExternDllHelper.ACCENTSTATE.ACCENT_ENABLE_BLURBEHIND - : ExternDllHelper.ACCENTSTATE.ACCENT_ENABLE_ACRYLICBLURBEHIND; + ? InteropValues.ACCENTSTATE.ACCENT_ENABLE_BLURBEHIND + : InteropValues.ACCENTSTATE.ACCENT_ENABLE_ACRYLICBLURBEHIND; accentPolicy.AccentFlags = 2; accentPolicy.GradientColor = ResourceHelper.GetResource(ResourceToken.BlurGradientValue); @@ -46,14 +47,14 @@ namespace HandyControl.Controls var accentPtr = Marshal.AllocHGlobal(accentPolicySize); Marshal.StructureToPtr(accentPolicy, accentPtr, false); - var data = new ExternDllHelper.WINCOMPATTRDATA + var data = new InteropValues.WINCOMPATTRDATA { - Attribute = ExternDllHelper.WINDOWCOMPOSITIONATTRIB.WCA_ACCENT_POLICY, + Attribute = InteropValues.WINDOWCOMPOSITIONATTRIB.WCA_ACCENT_POLICY, DataSize = accentPolicySize, Data = accentPtr }; - ExternDllHelper.SetWindowCompositionAttribute(window.GetHandle(), ref data); + InteropMethods.Gdip.SetWindowCompositionAttribute(window.GetHandle(), ref data); Marshal.FreeHGlobal(accentPtr); } diff --git a/src/Shared/HandyControl_Shared/Data/Enum/ImageCodecFlags.cs b/src/Shared/HandyControl_Shared/Data/Enum/ImageCodecFlags.cs new file mode 100644 index 00000000..c5fb0558 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Data/Enum/ImageCodecFlags.cs @@ -0,0 +1,18 @@ +using System; + +namespace HandyControl.Data +{ + [Flags] + internal enum ImageCodecFlags + { + Encoder = 0x00000001, + Decoder = 0x00000002, + SupportBitmap = 0x00000004, + SupportVector = 0x00000008, + SeekableEncode = 0x00000010, + BlockingDecode = 0x00000020, + Builtin = 0x00010000, + System = 0x00020000, + User = 0x00040000 + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Data/Gif/GPStream.cs b/src/Shared/HandyControl_Shared/Data/Gif/GPStream.cs index 4374b4f6..64bfaa9e 100644 --- a/src/Shared/HandyControl_Shared/Data/Gif/GPStream.cs +++ b/src/Shared/HandyControl_Shared/Data/Gif/GPStream.cs @@ -3,12 +3,12 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.InteropServices; using System.Security.Permissions; -using HandyControl.Tools; +using HandyControl.Tools.Interop; namespace HandyControl.Data { // ReSharper disable once InconsistentNaming - internal class GPStream : ExternDllHelper.IStream + internal class GPStream : InteropValues.IStream { protected Stream DataStream; @@ -55,7 +55,7 @@ namespace HandyControl.Data _virtualPosition = -1; } - public virtual ExternDllHelper.IStream Clone() + public virtual InteropValues.IStream Clone() { NotImplemented(); return null; @@ -72,7 +72,7 @@ namespace HandyControl.Data UIPermission(SecurityAction.Demand, Window = UIPermissionWindow.AllWindows), SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode) ] - public virtual long CopyTo(ExternDllHelper.IStream pstm, long cb, long[] pcbRead) + public virtual long CopyTo(InteropValues.IStream pstm, long cb, long[] pcbRead) { const int bufsize = 4096; @@ -117,7 +117,7 @@ namespace HandyControl.Data protected static ExternalException EFail(string msg) { - throw new ExternalException(msg, ExternDllHelper.E_FAIL); + throw new ExternalException(msg, InteropMethods.E_FAIL); } protected static void NotImplemented() @@ -154,7 +154,7 @@ namespace HandyControl.Data var len = DataStream.Length; switch (origin) { - case ExternDllHelper.StreamConsts.STREAM_SEEK_SET: + case InteropValues.StreamConsts.STREAM_SEEK_SET: if (offset <= len) { DataStream.Position = offset; @@ -165,7 +165,7 @@ namespace HandyControl.Data _virtualPosition = offset; } break; - case ExternDllHelper.StreamConsts.STREAM_SEEK_END: + case InteropValues.StreamConsts.STREAM_SEEK_END: if (offset <= 0) { DataStream.Position = len + offset; @@ -176,7 +176,7 @@ namespace HandyControl.Data _virtualPosition = len + offset; } break; - case ExternDllHelper.StreamConsts.STREAM_SEEK_CUR: + case InteropValues.StreamConsts.STREAM_SEEK_CUR: if (offset + pos <= len) { DataStream.Position = pos + offset; diff --git a/src/Shared/HandyControl_Shared/Data/Gif/GifPropertyItemInternal.cs b/src/Shared/HandyControl_Shared/Data/Gif/GifPropertyItemInternal.cs index c2643091..58e11aae 100644 --- a/src/Shared/HandyControl_Shared/Data/Gif/GifPropertyItemInternal.cs +++ b/src/Shared/HandyControl_Shared/Data/Gif/GifPropertyItemInternal.cs @@ -1,6 +1,6 @@ using System; using System.Runtime.InteropServices; -using HandyControl.Tools; +using HandyControl.Tools.Interop; namespace HandyControl.Data { @@ -85,7 +85,7 @@ namespace HandyControl.Data GifPropertyItemInternal propcopy = null; try { - propcopy = (GifPropertyItemInternal) ExternDllHelper.PtrToStructure(propdata, + propcopy = (GifPropertyItemInternal) InteropMethods.PtrToStructure(propdata, typeof(GifPropertyItemInternal)); props[i] = new GifPropertyItem diff --git a/src/Shared/HandyControl_Shared/Data/ImageCodecInfo.cs b/src/Shared/HandyControl_Shared/Data/ImageCodecInfo.cs new file mode 100644 index 00000000..0b97ab5b --- /dev/null +++ b/src/Shared/HandyControl_Shared/Data/ImageCodecInfo.cs @@ -0,0 +1,158 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using HandyControl.Tools.Interop; + +namespace HandyControl.Data +{ + internal sealed class ImageCodecInfo + { + private string _dllName; + + public Guid Clsid { get; set; } + + public Guid FormatID { get; set; } + + public string CodecName { get; set; } + + public string DllName + { + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + get + { + if (_dllName != null) + { + new System.Security.Permissions.FileIOPermission(System.Security.Permissions.FileIOPermissionAccess.PathDiscovery, _dllName).Demand(); + } + return _dllName; + } + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + set + { + if (value != null) + { + new System.Security.Permissions.FileIOPermission(System.Security.Permissions.FileIOPermissionAccess.PathDiscovery, value).Demand(); + } + _dllName = value; + } + } + + public string FormatDescription { get; set; } + + public string FilenameExtension { get; set; } + + public string MimeType { get; set; } + + public ImageCodecFlags Flags { get; set; } + + public int Version { get; set; } + + public byte[][] SignaturePatterns { get; set; } + + public byte[][] SignatureMasks { get; set; } + + public static ImageCodecInfo[] GetImageDecoders() + { + ImageCodecInfo[] imageCodecs; + + var status = InteropMethods.Gdip.GdipGetImageDecodersSize(out var numDecoders, out var size); + + if (status != InteropMethods.Gdip.Ok) + { + throw InteropMethods.Gdip.StatusException(status); + } + + var memory = Marshal.AllocHGlobal(size); + + try + { + status = InteropMethods.Gdip.GdipGetImageDecoders(numDecoders, size, memory); + + if (status != InteropMethods.Gdip.Ok) + { + throw InteropMethods.Gdip.StatusException(status); + } + + imageCodecs = ConvertFromMemory(memory, numDecoders); + } + finally + { + Marshal.FreeHGlobal(memory); + } + + return imageCodecs; + } + + public static ImageCodecInfo[] GetImageEncoders() + { + ImageCodecInfo[] imageCodecs; + + var status = InteropMethods.Gdip.GdipGetImageEncodersSize(out var numEncoders, out var size); + + if (status != InteropMethods.Gdip.Ok) + { + throw InteropMethods.Gdip.StatusException(status); + } + + var memory = Marshal.AllocHGlobal(size); + + try + { + status = InteropMethods.Gdip.GdipGetImageEncoders(numEncoders, size, memory); + + if (status != InteropMethods.Gdip.Ok) + { + throw InteropMethods.Gdip.StatusException(status); + } + + imageCodecs = ConvertFromMemory(memory, numEncoders); + } + finally + { + Marshal.FreeHGlobal(memory); + } + + return imageCodecs; + } + + public static ImageCodecInfo[] ConvertFromMemory(IntPtr memoryStart, int numCodecs) + { + var codecs = new ImageCodecInfo[numCodecs]; + + int index; + + for (index = 0; index < numCodecs; index++) + { + var curcodec = (IntPtr)((long)memoryStart + Marshal.SizeOf(typeof(InteropValues.ImageCodecInfoPrivate)) * index); + var codecp = new InteropValues.ImageCodecInfoPrivate(); + InteropMethods.PtrToStructure(curcodec, codecp); + + codecs[index] = new ImageCodecInfo + { + Clsid = codecp.Clsid, + FormatID = codecp.FormatID, + CodecName = Marshal.PtrToStringUni(codecp.CodecName), + DllName = Marshal.PtrToStringUni(codecp.DllName), + FormatDescription = Marshal.PtrToStringUni(codecp.FormatDescription), + FilenameExtension = Marshal.PtrToStringUni(codecp.FilenameExtension), + MimeType = Marshal.PtrToStringUni(codecp.MimeType), + Flags = (ImageCodecFlags) codecp.Flags, + Version = codecp.Version, + SignaturePatterns = new byte[codecp.SigCount][], + SignatureMasks = new byte[codecp.SigCount][] + }; + + for (var j = 0; j < codecp.SigCount; j++) + { + codecs[index].SignaturePatterns[j] = new byte[codecp.SigSize]; + codecs[index].SignatureMasks[j] = new byte[codecp.SigSize]; + + Marshal.Copy((IntPtr)((long)codecp.SigMask + j * codecp.SigSize), codecs[index].SignatureMasks[j], 0, codecp.SigSize); + Marshal.Copy((IntPtr)((long)codecp.SigPattern + j * codecp.SigSize), codecs[index].SignaturePatterns[j], 0, codecp.SigSize); + } + } + + return codecs; + } + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index 659ab9fd..9e017186 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -51,6 +51,7 @@ + @@ -61,6 +62,7 @@ + @@ -317,7 +319,6 @@ - diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/ExternDllHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/ExternDllHelper.cs deleted file mode 100644 index 58264b24..00000000 --- a/src/Shared/HandyControl_Shared/Tools/Helper/ExternDllHelper.cs +++ /dev/null @@ -1,362 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Security.Permissions; -using System.Threading; - -namespace HandyControl.Tools -{ - [SuppressMessage("ReSharper", "InconsistentNaming")] - internal class ExternDllHelper - { - private const string Gdi32 = "gdi32.dll"; - - private const string User32 = "user32.dll"; - - public const int E_FAIL = unchecked((int) 0x80004005); - - [StructLayout(LayoutKind.Sequential)] - internal struct WINCOMPATTRDATA - { - public WINDOWCOMPOSITIONATTRIB Attribute; - public IntPtr Data; - public int DataSize; - } - - internal enum WINDOWCOMPOSITIONATTRIB - { - WCA_ACCENT_POLICY = 19 - } - - internal enum ACCENTSTATE - { - ACCENT_DISABLED = 0, - ACCENT_ENABLE_GRADIENT = 1, - ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, - ACCENT_ENABLE_BLURBEHIND = 3, - ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, - ACCENT_INVALID_STATE = 5 - } - - [StructLayout(LayoutKind.Sequential)] - internal struct ACCENTPOLICY - { - public ACCENTSTATE AccentState; - public int AccentFlags; - public uint GradientColor; - public int AnimationId; - } - - [DllImport(User32)] - internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WINCOMPATTRDATA data); - - [ReflectionPermission(SecurityAction.Assert, Unrestricted = true), SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] - public static object PtrToStructure(IntPtr lparam, Type cls) => Marshal.PtrToStructure(lparam, cls); - - [DllImport(Gdi32, CharSet = CharSet.Auto, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool DeleteObject(IntPtr hObject); - - [ComImport, Guid("0000000C-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IStream - { - int Read( - [In] - IntPtr buf, - [In] - int len); - - int Write( - [In] - IntPtr buf, - [In] - int len); - - [return: MarshalAs(UnmanagedType.I8)] - long Seek( - [In, MarshalAs(UnmanagedType.I8)] - long dlibMove, - [In] - int dwOrigin); - - void SetSize( - [In, MarshalAs(UnmanagedType.I8)] - long libNewSize); - - [return: MarshalAs(UnmanagedType.I8)] - long CopyTo( - [In, MarshalAs(UnmanagedType.Interface)] - IStream pstm, - [In, MarshalAs(UnmanagedType.I8)] - long cb, - [Out, MarshalAs(UnmanagedType.LPArray)] - long[] pcbRead); - - void Commit( - [In] - int grfCommitFlags); - - void Revert(); - - void LockRegion( - [In, MarshalAs(UnmanagedType.I8)] - long libOffset, - [In, MarshalAs(UnmanagedType.I8)] - long cb, - [In] - int dwLockType); - - void UnlockRegion( - [In, MarshalAs(UnmanagedType.I8)] - long libOffset, - [In, MarshalAs(UnmanagedType.I8)] - long cb, - [In] - int dwLockType); - - void Stat( - [In] - IntPtr pStatstg, - [In] - int grfStatFlag); - - [return: MarshalAs(UnmanagedType.Interface)] - IStream Clone(); - } - - [SuppressMessage("ReSharper", "InconsistentNaming")] - public class StreamConsts - { - public const int LOCK_WRITE = 0x1; - public const int LOCK_EXCLUSIVE = 0x2; - public const int LOCK_ONLYONCE = 0x4; - public const int STATFLAG_DEFAULT = 0x0; - public const int STATFLAG_NONAME = 0x1; - public const int STATFLAG_NOOPEN = 0x2; - public const int STGC_DEFAULT = 0x0; - public const int STGC_OVERWRITE = 0x1; - public const int STGC_ONLYIFCURRENT = 0x2; - public const int STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 0x4; - public const int STREAM_SEEK_SET = 0x0; - public const int STREAM_SEEK_CUR = 0x1; - public const int STREAM_SEEK_END = 0x2; - } - - internal class Gdip - { - private const string Gdiplus = "gdiplus.dll"; - - private const string ThreadDataSlotName = "system.drawing.threaddata"; - - private static IntPtr InitToken; - - private static bool Initialized => InitToken != IntPtr.Zero; - - static Gdip() - { - Initialize(); - } - - [StructLayout(LayoutKind.Sequential)] - private struct StartupInput - { - private int GdiplusVersion; - - private readonly IntPtr DebugEventCallback; - - private bool SuppressBackgroundThread; - - private bool SuppressExternalCodecs; - - public static StartupInput GetDefault() - { - var result = new StartupInput - { - GdiplusVersion = 1, - SuppressBackgroundThread = false, - SuppressExternalCodecs = false - }; - return result; - } - } - - [StructLayout(LayoutKind.Sequential)] - private struct StartupOutput - { - private readonly IntPtr hook; - - private readonly IntPtr unhook; - } - - [ResourceExposure(ResourceScope.None)] - [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)] - [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")] - private static void Initialize() - { - var input = StartupInput.GetDefault(); - - var status = GdiplusStartup(out InitToken, ref input, out _); - - if (status != Ok) - { - throw StatusException(status); - } - - var currentDomain = AppDomain.CurrentDomain; - currentDomain.ProcessExit += OnProcessExit; - - if (!currentDomain.IsDefaultAppDomain()) - { - currentDomain.DomainUnload += OnProcessExit; - } - } - - [PrePrepareMethod] - [ResourceExposure(ResourceScope.AppDomain)] - [ResourceConsumption(ResourceScope.AppDomain)] - private static void OnProcessExit(object sender, EventArgs e) => Shutdown(); - - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] - [ResourceExposure(ResourceScope.AppDomain)] - [ResourceConsumption(ResourceScope.AppDomain)] - private static void Shutdown() - { - if (Initialized) - { - ClearThreadData(); - // unhook our shutdown handlers as we do not need to shut down more than once - var currentDomain = AppDomain.CurrentDomain; - currentDomain.ProcessExit -= OnProcessExit; - if (!currentDomain.IsDefaultAppDomain()) - { - currentDomain.DomainUnload -= OnProcessExit; - } - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void ClearThreadData() - { - var slot = Thread.GetNamedDataSlot(ThreadDataSlotName); - Thread.SetData(slot, null); - } - - //---------------------------------------------------------------------------------------- - // Status codes - //---------------------------------------------------------------------------------------- - internal const int Ok = 0; - internal const int GenericError = 1; - internal const int InvalidParameter = 2; - internal const int OutOfMemory = 3; - internal const int ObjectBusy = 4; - internal const int InsufficientBuffer = 5; - internal const int NotImplemented = 6; - internal const int Win32Error = 7; - internal const int WrongState = 8; - internal const int Aborted = 9; - internal const int FileNotFound = 10; - internal const int ValueOverflow = 11; - internal const int AccessDenied = 12; - internal const int UnknownImageFormat = 13; - internal const int FontFamilyNotFound = 14; - internal const int FontStyleNotFound = 15; - internal const int NotTrueTypeFont = 16; - internal const int UnsupportedGdiplusVersion = 17; - internal const int GdiplusNotInitialized = 18; - internal const int PropertyNotFound = 19; - internal const int PropertyNotSupported = 20; - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] - internal static extern int GdipImageGetFrameDimensionsCount(HandleRef image, out int count); - - internal static Exception StatusException(int status) - { - switch (status) - { - case GenericError: return new ExternalException("GdiplusGenericError"); - case InvalidParameter: return new ArgumentException("GdiplusInvalidParameter"); - case OutOfMemory: return new OutOfMemoryException("GdiplusOutOfMemory"); - case ObjectBusy: return new InvalidOperationException("GdiplusObjectBusy"); - case InsufficientBuffer: return new OutOfMemoryException("GdiplusInsufficientBuffer"); - case NotImplemented: return new NotImplementedException("GdiplusNotImplemented"); - case Win32Error: return new ExternalException("GdiplusGenericError"); - case WrongState: return new InvalidOperationException("GdiplusWrongState"); - case Aborted: return new ExternalException("GdiplusAborted"); - case FileNotFound: return new FileNotFoundException("GdiplusFileNotFound"); - case ValueOverflow: return new OverflowException("GdiplusOverflow"); - case AccessDenied: return new ExternalException("GdiplusAccessDenied"); - case UnknownImageFormat: return new ArgumentException("GdiplusUnknownImageFormat"); - case PropertyNotFound: return new ArgumentException("GdiplusPropertyNotFoundError"); - case PropertyNotSupported: return new ArgumentException("GdiplusPropertyNotSupportedError"); - case FontFamilyNotFound: return new ArgumentException("GdiplusFontFamilyNotFound"); - case FontStyleNotFound: return new ArgumentException("GdiplusFontStyleNotFound"); - case NotTrueTypeFont: return new ArgumentException("GdiplusNotTrueTypeFont_NoName"); - case UnsupportedGdiplusVersion: return new ExternalException("GdiplusUnsupportedGdiplusVersion"); - case GdiplusNotInitialized: return new ExternalException("GdiplusNotInitialized"); - } - - return new ExternalException("GdiplusUnknown"); - } - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] - internal static extern int GdipImageGetFrameDimensionsList(HandleRef image, IntPtr buffer, int count); - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] - internal static extern int GdipImageGetFrameCount(HandleRef image, ref Guid dimensionId, int[] count); - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] - internal static extern int GdipGetPropertyItemSize(HandleRef image, int propid, out int size); - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] - internal static extern int GdipGetPropertyItem(HandleRef image, int propid, int size, IntPtr buffer); - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.Machine)] - internal static extern int GdipCreateHBITMAPFromBitmap(HandleRef nativeBitmap, out IntPtr hbitmap, int argbBackground); - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] - internal static extern int GdipImageSelectActiveFrame(HandleRef image, ref Guid dimensionId, int frameIndex); - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.Machine)] - internal static extern int GdipCreateBitmapFromFile(string filename, out IntPtr bitmap); - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] - internal static extern int GdipImageForceValidation(HandleRef image); - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, EntryPoint = "GdipDisposeImage", CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] - private static extern int IntGdipDisposeImage(HandleRef image); - - internal static int GdipDisposeImage(HandleRef image) - { - if (!Initialized) return Ok; - var result = IntGdipDisposeImage(image); - return result; - } - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.Process)] - private static extern int GdiplusStartup(out IntPtr token, ref StartupInput input, out StartupOutput output); - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] - internal static extern int GdipGetImageRawFormat(HandleRef image, ref Guid format); - - [DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.Machine)] - internal static extern int GdipCreateBitmapFromStream(IStream stream, out IntPtr bitmap); - } - } -} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Tools/Interop/InteropMethods.cs b/src/Shared/HandyControl_Shared/Tools/Interop/InteropMethods.cs index 0ec02507..fa6b8d37 100644 --- a/src/Shared/HandyControl_Shared/Tools/Interop/InteropMethods.cs +++ b/src/Shared/HandyControl_Shared/Tools/Interop/InteropMethods.cs @@ -1,15 +1,24 @@ using System; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; +using System.Security.Permissions; using System.Text; +using System.Threading; namespace HandyControl.Tools.Interop { internal class InteropMethods { + #region common + + public const int E_FAIL = unchecked((int)0x80004005); + internal static readonly IntPtr HRGN_NONE = new IntPtr(-1); [DllImport(InteropValues.ExternDll.User32, CharSet = CharSet.Auto)] @@ -101,7 +110,7 @@ namespace HandyControl.Tools.Interop [SecurityCritical] internal static int ReleaseDC(HandleRef hWnd, HandleRef hDC) { - HandleCollector.Remove((IntPtr) hDC, CommonHandles.HDC); + HandleCollector.Remove((IntPtr)hDC, CommonHandles.HDC); return IntReleaseDC(hWnd, hDC); } @@ -280,7 +289,7 @@ namespace HandyControl.Tools.Interop [DllImport(InteropValues.ExternDll.User32, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowPlacement(IntPtr hwnd, InteropValues.WINDOWPLACEMENT lpwndpl); - + internal static InteropValues.WINDOWPLACEMENT GetWindowPlacement(IntPtr hwnd) { InteropValues.WINDOWPLACEMENT wINDOWPLACEMENT = new InteropValues.WINDOWPLACEMENT(); @@ -298,18 +307,18 @@ namespace HandyControl.Tools.Interop internal static int HiWord(int value) => (short)(value >> 16); internal static int LoWord(int value) => (short)(value & 65535); - + [DllImport(InteropValues.ExternDll.User32)] internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags); - + [DllImport(InteropValues.ExternDll.User32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool EnumThreadWindows(uint dwThreadId, InteropValues.EnumWindowsProc lpfn, IntPtr lParam); - + [DllImport(InteropValues.ExternDll.Gdi32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool DeleteDC(IntPtr hdc); - + [DllImport(InteropValues.ExternDll.Gdi32, SetLastError = true)] internal static extern IntPtr CreateCompatibleDC(IntPtr hdc); @@ -321,26 +330,26 @@ namespace HandyControl.Tools.Interop [DllImport(InteropValues.ExternDll.User32)] internal static extern IntPtr MonitorFromPoint(InteropValues.POINT pt, int flags); - + [DllImport(InteropValues.ExternDll.User32)] internal static extern IntPtr GetWindow(IntPtr hwnd, int nCmd); [DllImport(InteropValues.ExternDll.User32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool IsWindowVisible(IntPtr hwnd); - + [DllImport(InteropValues.ExternDll.User32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool IsIconic(IntPtr hwnd); - + [DllImport(InteropValues.ExternDll.User32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool IsZoomed(IntPtr hwnd); - + [DllImport(InteropValues.ExternDll.User32, CharSet = CharSet.Auto, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int flags); - + internal static System.Windows.Point GetCursorPos() { var result = default(System.Windows.Point); @@ -365,19 +374,19 @@ namespace HandyControl.Tools.Interop } return SetWindowLongPtr64(hWnd, nIndex, dwNewLong); } - + [DllImport(InteropValues.ExternDll.User32, CharSet = CharSet.Auto, EntryPoint = "SetWindowLong")] internal static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, int nIndex, IntPtr dwNewLong); - + [DllImport(InteropValues.ExternDll.User32, CharSet = CharSet.Auto, EntryPoint = "SetWindowLongPtr")] internal static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong); [DllImport(InteropValues.ExternDll.User32, CharSet = CharSet.Unicode)] private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); - + [DllImport(InteropValues.ExternDll.User32, CharSet = CharSet.Unicode)] private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong); - + internal static IntPtr SetWindowLongPtr(IntPtr hWnd, InteropValues.GWLP nIndex, IntPtr dwNewLong) { if (IntPtr.Size == 8) @@ -386,7 +395,7 @@ namespace HandyControl.Tools.Interop } return new IntPtr(SetWindowLong(hWnd, (int)nIndex, dwNewLong.ToInt32())); } - + internal static int SetWindowLong(IntPtr hWnd, InteropValues.GWL nIndex, int dwNewLong) => SetWindowLong(hWnd, (int)nIndex, dwNewLong); [DllImport(InteropValues.ExternDll.User32, CharSet = CharSet.Unicode)] @@ -401,37 +410,37 @@ namespace HandyControl.Tools.Interop [DllImport(InteropValues.ExternDll.User32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool DestroyWindow(IntPtr hwnd); - + [DllImport(InteropValues.ExternDll.User32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool UnregisterClass(IntPtr classAtom, IntPtr hInstance); - + [DllImport(InteropValues.ExternDll.User32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDest, ref InteropValues.POINT pptDest, ref InteropValues.SIZE psize, IntPtr hdcSrc, ref InteropValues.POINT pptSrc, uint crKey, [In] ref InteropValues.BLENDFUNCTION pblend, uint dwFlags); - + [DllImport(InteropValues.ExternDll.User32)] internal static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, InteropValues.RedrawWindowFlags flags); [DllImport(InteropValues.ExternDll.User32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, InteropValues.EnumMonitorsDelegate lpfnEnum, IntPtr dwData); - + [DllImport(InteropValues.ExternDll.User32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool IntersectRect(out InteropValues.RECT lprcDst, [In] ref InteropValues.RECT lprcSrc1, [In] ref InteropValues.RECT lprcSrc2); - + [DllImport(InteropValues.ExternDll.User32)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool GetMonitorInfo(IntPtr hMonitor, ref InteropValues.MONITORINFO monitorInfo); - + [DllImport(InteropValues.ExternDll.Gdi32, SetLastError = true)] internal static extern IntPtr CreateDIBSection(IntPtr hdc, ref InteropValues.BITMAPINFO pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset); - + [DllImport(InteropValues.ExternDll.MsImg)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool AlphaBlend(IntPtr hdcDest, int xoriginDest, int yoriginDest, int wDest, int hDest, IntPtr hdcSrc, int xoriginSrc, int yoriginSrc, int wSrc, int hSrc, InteropValues.BLENDFUNCTION pfn); - + internal static int GET_SC_WPARAM(IntPtr wParam) => (int)wParam & 65520; [DllImport(InteropValues.ExternDll.User32)] @@ -446,5 +455,251 @@ namespace HandyControl.Tools.Interop [DllImport(InteropValues.ExternDll.User32)] [ResourceExposure(ResourceScope.None)] internal static extern bool EnableWindow(IntPtr hWnd, bool enable); + + [ReflectionPermission(SecurityAction.Assert, Unrestricted = true), SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal static object PtrToStructure(IntPtr lparam, Type cls) => Marshal.PtrToStructure(lparam, cls); + + [ReflectionPermission(SecurityAction.Assert, Unrestricted = true), + SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + public static void PtrToStructure(IntPtr lparam, object data) => Marshal.PtrToStructure(lparam, data); + + #endregion + + internal class Gdip + { + private const string ThreadDataSlotName = "system.drawing.threaddata"; + + private static IntPtr InitToken; + + private static bool Initialized => InitToken != IntPtr.Zero; + + internal const int + Ok = 0, + GenericError = 1, + InvalidParameter = 2, + OutOfMemory = 3, + ObjectBusy = 4, + InsufficientBuffer = 5, + NotImplemented = 6, + Win32Error = 7, + WrongState = 8, + Aborted = 9, + FileNotFound = 10, + ValueOverflow = 11, + AccessDenied = 12, + UnknownImageFormat = 13, + FontFamilyNotFound = 14, + FontStyleNotFound = 15, + NotTrueTypeFont = 16, + UnsupportedGdiplusVersion = 17, + GdiplusNotInitialized = 18, + PropertyNotFound = 19, + PropertyNotSupported = 20, + E_UNEXPECTED = unchecked((int)0x8000FFFF); + + static Gdip() + { + Initialize(); + } + + [StructLayout(LayoutKind.Sequential)] + private struct StartupInput + { + private int GdiplusVersion; + + private readonly IntPtr DebugEventCallback; + + private bool SuppressBackgroundThread; + + private bool SuppressExternalCodecs; + + public static StartupInput GetDefault() + { + var result = new StartupInput + { + GdiplusVersion = 1, + SuppressBackgroundThread = false, + SuppressExternalCodecs = false + }; + return result; + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct StartupOutput + { + private readonly IntPtr hook; + + private readonly IntPtr unhook; + } + + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)] + [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")] + private static void Initialize() + { + var input = StartupInput.GetDefault(); + + var status = GdiplusStartup(out InitToken, ref input, out _); + + if (status != Ok) + { + throw StatusException(status); + } + + var currentDomain = AppDomain.CurrentDomain; + currentDomain.ProcessExit += OnProcessExit; + + if (!currentDomain.IsDefaultAppDomain()) + { + currentDomain.DomainUnload += OnProcessExit; + } + } + + [PrePrepareMethod] + [ResourceExposure(ResourceScope.AppDomain)] + [ResourceConsumption(ResourceScope.AppDomain)] + private static void OnProcessExit(object sender, EventArgs e) => Shutdown(); + + [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] + [ResourceExposure(ResourceScope.AppDomain)] + [ResourceConsumption(ResourceScope.AppDomain)] + private static void Shutdown() + { + if (Initialized) + { + ClearThreadData(); + // unhook our shutdown handlers as we do not need to shut down more than once + var currentDomain = AppDomain.CurrentDomain; + currentDomain.ProcessExit -= OnProcessExit; + if (!currentDomain.IsDefaultAppDomain()) + { + currentDomain.DomainUnload -= OnProcessExit; + } + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ClearThreadData() + { + var slot = Thread.GetNamedDataSlot(ThreadDataSlotName); + Thread.SetData(slot, null); + } + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipImageGetFrameDimensionsCount(HandleRef image, out int count); + + internal static Exception StatusException(int status) + { + switch (status) + { + case GenericError: return new ExternalException("GdiplusGenericError"); + case InvalidParameter: return new ArgumentException("GdiplusInvalidParameter"); + case OutOfMemory: return new OutOfMemoryException("GdiplusOutOfMemory"); + case ObjectBusy: return new InvalidOperationException("GdiplusObjectBusy"); + case InsufficientBuffer: return new OutOfMemoryException("GdiplusInsufficientBuffer"); + case NotImplemented: return new NotImplementedException("GdiplusNotImplemented"); + case Win32Error: return new ExternalException("GdiplusGenericError"); + case WrongState: return new InvalidOperationException("GdiplusWrongState"); + case Aborted: return new ExternalException("GdiplusAborted"); + case FileNotFound: return new FileNotFoundException("GdiplusFileNotFound"); + case ValueOverflow: return new OverflowException("GdiplusOverflow"); + case AccessDenied: return new ExternalException("GdiplusAccessDenied"); + case UnknownImageFormat: return new ArgumentException("GdiplusUnknownImageFormat"); + case PropertyNotFound: return new ArgumentException("GdiplusPropertyNotFoundError"); + case PropertyNotSupported: return new ArgumentException("GdiplusPropertyNotSupportedError"); + case FontFamilyNotFound: return new ArgumentException("GdiplusFontFamilyNotFound"); + case FontStyleNotFound: return new ArgumentException("GdiplusFontStyleNotFound"); + case NotTrueTypeFont: return new ArgumentException("GdiplusNotTrueTypeFont_NoName"); + case UnsupportedGdiplusVersion: return new ExternalException("GdiplusUnsupportedGdiplusVersion"); + case GdiplusNotInitialized: return new ExternalException("GdiplusNotInitialized"); + } + + return new ExternalException("GdiplusUnknown"); + } + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipImageGetFrameDimensionsList(HandleRef image, IntPtr buffer, int count); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipImageGetFrameCount(HandleRef image, ref Guid dimensionId, int[] count); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipGetPropertyItemSize(HandleRef image, int propid, out int size); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipGetPropertyItem(HandleRef image, int propid, int size, IntPtr buffer); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.Machine)] + internal static extern int GdipCreateHBITMAPFromBitmap(HandleRef nativeBitmap, out IntPtr hbitmap, int argbBackground); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipImageSelectActiveFrame(HandleRef image, ref Guid dimensionId, int frameIndex); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.Machine)] + internal static extern int GdipCreateBitmapFromFile(string filename, out IntPtr bitmap); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipImageForceValidation(HandleRef image); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, EntryPoint = "GdipDisposeImage", CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + private static extern int IntGdipDisposeImage(HandleRef image); + + internal static int GdipDisposeImage(HandleRef image) + { + if (!Initialized) return Ok; + var result = IntGdipDisposeImage(image); + return result; + } + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.Process)] + private static extern int GdiplusStartup(out IntPtr token, ref StartupInput input, out StartupOutput output); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipGetImageRawFormat(HandleRef image, ref Guid format); + + [DllImport(InteropValues.ExternDll.User32)] + internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref InteropValues.WINCOMPATTRDATA data); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.Machine)] + internal static extern int GdipCreateBitmapFromStream(InteropValues.IStream stream, out IntPtr bitmap); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.Machine)] + internal static extern int GdipCreateBitmapFromHBITMAP(HandleRef hbitmap, HandleRef hpalette, out IntPtr bitmap); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipGetImageEncodersSize(out int numEncoders, out int size); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipGetImageDecodersSize(out int numDecoders, out int size); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipGetImageDecoders(int numDecoders, int size, IntPtr decoders); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipGetImageEncoders(int numEncoders, int size, IntPtr encoders); + + [DllImport(InteropValues.ExternDll.GdiPlus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + internal static extern int GdipSaveImageToStream(HandleRef image, InteropValues.IStream stream, ref Guid classId, HandleRef encoderParams); + } } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Tools/Interop/InteropValues.cs b/src/Shared/HandyControl_Shared/Tools/Interop/InteropValues.cs index 9a08d865..dfc65f65 100644 --- a/src/Shared/HandyControl_Shared/Tools/Interop/InteropValues.cs +++ b/src/Shared/HandyControl_Shared/Tools/Interop/InteropValues.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Runtime.InteropServices; using System.Security; using System.Windows; @@ -12,6 +14,7 @@ namespace HandyControl.Tools.Interop public const string User32 = "user32.dll", Gdi32 = "gdi32.dll", + GdiPlus = "gdiplus.dll", Kernel32 = "kernel32.dll", Shell32 = "shell32.dll", MsImg = "msimg32.dll"; @@ -31,6 +34,7 @@ namespace HandyControl.Tools.Interop PLANES = 14, BI_RGB = 0, DIB_RGB_COLORS = 0, + E_FAIL = unchecked((int)0x80004005), NIF_MESSAGE = 0x00000001, NIF_ICON = 0x00000002, NIF_TIP = 0x00000004, @@ -659,5 +663,275 @@ namespace HandyControl.Tools.Interop public BitmapHandle hbmMask = null; public BitmapHandle hbmColor = null; } + + internal enum WINDOWCOMPOSITIONATTRIB + { + WCA_ACCENT_POLICY = 19 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct WINCOMPATTRDATA + { + public WINDOWCOMPOSITIONATTRIB Attribute; + public IntPtr Data; + public int DataSize; + } + + internal enum ACCENTSTATE + { + ACCENT_DISABLED = 0, + ACCENT_ENABLE_GRADIENT = 1, + ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, + ACCENT_ENABLE_BLURBEHIND = 3, + ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, + ACCENT_INVALID_STATE = 5 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ACCENTPOLICY + { + public ACCENTSTATE AccentState; + public int AccentFlags; + public uint GradientColor; + public int AnimationId; + } + + [ComImport, Guid("0000000C-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IStream + { + int Read([In] IntPtr buf, [In] int len); + + int Write([In] IntPtr buf, [In] int len); + + [return: MarshalAs(UnmanagedType.I8)] + long Seek([In, MarshalAs(UnmanagedType.I8)] long dlibMove, [In] int dwOrigin); + + void SetSize([In, MarshalAs(UnmanagedType.I8)] long libNewSize); + + [return: MarshalAs(UnmanagedType.I8)] + long CopyTo([In, MarshalAs(UnmanagedType.Interface)] IStream pstm, [In, MarshalAs(UnmanagedType.I8)] long cb, [Out, MarshalAs(UnmanagedType.LPArray)] long[] pcbRead); + + void Commit([In]int grfCommitFlags); + + void Revert(); + + void LockRegion([In, MarshalAs(UnmanagedType.I8)]long libOffset, [In, MarshalAs(UnmanagedType.I8)] long cb, [In] int dwLockType); + + void UnlockRegion([In, MarshalAs(UnmanagedType.I8)] long libOffset, [In, MarshalAs(UnmanagedType.I8)] long cb, [In] int dwLockType); + + void Stat([In] IntPtr pStatstg, [In] int grfStatFlag); + + [return: MarshalAs(UnmanagedType.Interface)] + IStream Clone(); + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + internal class StreamConsts + { + public const int LOCK_WRITE = 0x1; + public const int LOCK_EXCLUSIVE = 0x2; + public const int LOCK_ONLYONCE = 0x4; + public const int STATFLAG_DEFAULT = 0x0; + public const int STATFLAG_NONAME = 0x1; + public const int STATFLAG_NOOPEN = 0x2; + public const int STGC_DEFAULT = 0x0; + public const int STGC_OVERWRITE = 0x1; + public const int STGC_ONLYIFCURRENT = 0x2; + public const int STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 0x4; + public const int STREAM_SEEK_SET = 0x0; + public const int STREAM_SEEK_CUR = 0x1; + public const int STREAM_SEEK_END = 0x2; + } + + [StructLayout(LayoutKind.Sequential, Pack = 8)] + internal class ImageCodecInfoPrivate + { + [MarshalAs(UnmanagedType.Struct)] + public Guid Clsid; + [MarshalAs(UnmanagedType.Struct)] + public Guid FormatID; + + public IntPtr CodecName = IntPtr.Zero; + public IntPtr DllName = IntPtr.Zero; + public IntPtr FormatDescription = IntPtr.Zero; + public IntPtr FilenameExtension = IntPtr.Zero; + public IntPtr MimeType = IntPtr.Zero; + + public int Flags; + public int Version; + public int SigCount; + public int SigSize; + + public IntPtr SigPattern = IntPtr.Zero; + public IntPtr SigMask = IntPtr.Zero; + } + + internal class ComStreamFromDataStream : IStream + { + protected Stream DataStream; + + // to support seeking ahead of the stream length... + private long _virtualPosition = -1; + + internal ComStreamFromDataStream(Stream dataStream) + { + this.DataStream = dataStream ?? throw new ArgumentNullException(nameof(dataStream)); + } + + private void ActualizeVirtualPosition() + { + if (_virtualPosition == -1) return; + + if (_virtualPosition > DataStream.Length) + DataStream.SetLength(_virtualPosition); + + DataStream.Position = _virtualPosition; + + _virtualPosition = -1; + } + + public virtual IStream Clone() + { + NotImplemented(); + return null; + } + + public virtual void Commit(int grfCommitFlags) + { + DataStream.Flush(); + ActualizeVirtualPosition(); + } + + public virtual long CopyTo(IStream pstm, long cb, long[] pcbRead) + { + const int bufsize = 4096; // one page + var buffer = Marshal.AllocHGlobal(bufsize); + if (buffer == IntPtr.Zero) throw new OutOfMemoryException(); + long written = 0; + + try + { + while (written < cb) + { + var toRead = bufsize; + if (written + toRead > cb) toRead = (int)(cb - written); + var read = Read(buffer, toRead); + if (read == 0) break; + if (pstm.Write(buffer, read) != read) + { + throw EFail("Wrote an incorrect number of bytes"); + } + written += read; + } + } + finally + { + Marshal.FreeHGlobal(buffer); + } + if (pcbRead != null && pcbRead.Length > 0) + { + pcbRead[0] = written; + } + + return written; + } + + public virtual Stream GetDataStream() => DataStream; + + public virtual void LockRegion(long libOffset, long cb, int dwLockType) + { + } + + protected static ExternalException EFail(string msg) => throw new ExternalException(msg, E_FAIL); + + protected static void NotImplemented() => throw new NotImplementedException(); + + public virtual int Read(IntPtr buf, int length) + { + var buffer = new byte[length]; + var count = Read(buffer, length); + Marshal.Copy(buffer, 0, buf, length); + return count; + } + + public virtual int Read(byte[] buffer, int length) + { + ActualizeVirtualPosition(); + return DataStream.Read(buffer, 0, length); + } + + public virtual void Revert() => NotImplemented(); + + public virtual long Seek(long offset, int origin) + { + var pos = _virtualPosition; + if (_virtualPosition == -1) + { + pos = DataStream.Position; + } + var len = DataStream.Length; + + switch (origin) + { + case StreamConsts.STREAM_SEEK_SET: + if (offset <= len) + { + DataStream.Position = offset; + _virtualPosition = -1; + } + else + { + _virtualPosition = offset; + } + break; + case StreamConsts.STREAM_SEEK_END: + if (offset <= 0) + { + DataStream.Position = len + offset; + _virtualPosition = -1; + } + else + { + _virtualPosition = len + offset; + } + break; + case StreamConsts.STREAM_SEEK_CUR: + if (offset + pos <= len) + { + DataStream.Position = pos + offset; + _virtualPosition = -1; + } + else + { + _virtualPosition = offset + pos; + } + break; + } + + return _virtualPosition != -1 ? _virtualPosition : DataStream.Position; + } + + public virtual void SetSize(long value) => DataStream.SetLength(value); + + public virtual void Stat(IntPtr pstatstg, int grfStatFlag) => NotImplemented(); + + public virtual void UnlockRegion(long libOffset, long cb, int dwLockType) + { + } + + public virtual int Write(IntPtr buf, int length) + { + var buffer = new byte[length]; + Marshal.Copy(buf, buffer, 0, length); + return Write(buffer, length); + } + + public virtual int Write(byte[] buffer, int length) + { + ActualizeVirtualPosition(); + DataStream.Write(buffer, 0, length); + return length; + } + } } } \ No newline at end of file