diff --git a/HandyControl/Controls/NotifyIcon/NotifyIcon.cs b/HandyControl/Controls/NotifyIcon/NotifyIcon.cs
new file mode 100644
index 00000000..a2717693
--- /dev/null
+++ b/HandyControl/Controls/NotifyIcon/NotifyIcon.cs
@@ -0,0 +1,152 @@
+using System.Windows;
+using System.Windows.Media;
+using HandyControl.Data;
+using HandyControl.Tools;
+using HandyControl.Tools.Interop;
+
+namespace HandyControl.Controls
+{
+ public class NotifyIcon : FrameworkElement
+ {
+ private bool _added;
+
+ private readonly object _syncObj = new object();
+
+ private WindowClass _windowClass;
+
+ private readonly int _id;
+
+ private static int NextId;
+
+ private ImageSource _icon;
+
+ private IconHandle _defaultLargeIconHandle;
+
+ private IconHandle _defaultSmallIconHandle;
+
+ private IconHandle _currentLargeIconHandle;
+
+ private IconHandle _currentSmallIconHandle;
+
+ private const int WmTrayMouseMessage = NativeMethods.WM_USER + 1024;
+
+ public NotifyIcon()
+ {
+ _id = ++NextId;
+ _windowClass = new WindowClass();
+ }
+
+ public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
+ "Icon", typeof(ImageSource), typeof(NotifyIcon), new PropertyMetadata(default(ImageSource), OnIconChanged));
+
+ private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var ctl = (NotifyIcon) d;
+ ctl._icon = (ImageSource) e.NewValue;
+ ctl.UpdateIcon();
+ }
+
+ public ImageSource Icon
+ {
+ get => (ImageSource) GetValue(IconProperty);
+ set => SetValue(IconProperty, value);
+ }
+
+ private void UpdateIcon()
+ {
+ IconHandle largeIconHandle;
+ IconHandle smallIconHandle;
+
+ if (_icon != null)
+ {
+ IconHelper.GetIconHandlesFromImageSource(_icon, out largeIconHandle, out smallIconHandle);
+ }
+ else
+ {
+ if (_defaultLargeIconHandle == null && _defaultSmallIconHandle == null)
+ {
+ IconHelper.GetDefaultIconHandles(out largeIconHandle, out smallIconHandle);
+ _defaultLargeIconHandle = largeIconHandle;
+ _defaultSmallIconHandle = smallIconHandle;
+ }
+ else
+ {
+ largeIconHandle = _defaultLargeIconHandle;
+ smallIconHandle = _defaultSmallIconHandle;
+ }
+ }
+
+ if (_currentLargeIconHandle != null && _currentLargeIconHandle != _defaultLargeIconHandle)
+ {
+ _currentLargeIconHandle.Dispose();
+ }
+
+ if (_currentSmallIconHandle != null && _currentSmallIconHandle != _defaultSmallIconHandle)
+ {
+ _currentSmallIconHandle.Dispose();
+ }
+
+ _currentLargeIconHandle = largeIconHandle;
+ _currentSmallIconHandle = smallIconHandle;
+ }
+
+ public static readonly DependencyProperty BalloonTipIconProperty = DependencyProperty.Register(
+ "BalloonTipIcon", typeof(ToolTipIcon), typeof(NotifyIcon), new PropertyMetadata(default(ToolTipIcon)));
+
+ public ToolTipIcon BalloonTipIcon
+ {
+ get => (ToolTipIcon) GetValue(BalloonTipIconProperty);
+ set => SetValue(BalloonTipIconProperty, value);
+ }
+
+ public static readonly DependencyProperty BalloonTipTitleProperty = DependencyProperty.Register(
+ "BalloonTipTitle", typeof(string), typeof(NotifyIcon), new PropertyMetadata(default(string)));
+
+ public string BalloonTipTitle
+ {
+ get => (string) GetValue(BalloonTipTitleProperty);
+ set => SetValue(BalloonTipTitleProperty, value);
+ }
+
+ public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
+ "Text", typeof(string), typeof(NotifyIcon), new PropertyMetadata(default(string)));
+
+ public string Text
+ {
+ get => (string) GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
+ {
+ if (timeout < 0) return;
+ if (string.IsNullOrEmpty(tipTitle)) return;
+
+ if (_added)
+ {
+ if (DesignerHelper.IsInDesignMode) return;
+
+ IntSecurity.UnrestrictedWindows.Demand();
+
+ var data = new NOTIFYICONDATA
+ {
+ hWnd = _windowClass.MessageWindowHandle,
+ uID = _id,
+ uFlags = NativeMethods.NIF_INFO,
+ uTimeoutOrVersion = timeout,
+ szInfoTitle = tipTitle,
+ szInfo = tipText
+ };
+
+ switch (tipIcon)
+ {
+ case ToolTipIcon.Info: data.dwInfoFlags = NativeMethods.NIIF_INFO; break;
+ case ToolTipIcon.Warning: data.dwInfoFlags = NativeMethods.NIIF_WARNING; break;
+ case ToolTipIcon.Error: data.dwInfoFlags = NativeMethods.NIIF_ERROR; break;
+ case ToolTipIcon.None: data.dwInfoFlags = NativeMethods.NIIF_NONE; break;
+ }
+ UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_MODIFY, data);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Controls/NotifyIcon/NotifyIconNativeWindow.cs b/HandyControl/Controls/NotifyIcon/NotifyIconNativeWindow.cs
new file mode 100644
index 00000000..1bcf1a11
--- /dev/null
+++ b/HandyControl/Controls/NotifyIcon/NotifyIconNativeWindow.cs
@@ -0,0 +1,29 @@
+using System.Runtime.InteropServices;
+
+namespace HandyControl.Controls
+{
+ internal class NotifyIconNativeWindow
+ {
+ internal NotifyIcon Reference;
+
+ private GCHandle _rootRef;
+
+ public void LockReference(bool locked)
+ {
+ if (locked)
+ {
+ if (!_rootRef.IsAllocated)
+ {
+ _rootRef = GCHandle.Alloc(Reference, GCHandleType.Normal);
+ }
+ }
+ else
+ {
+ if (_rootRef.IsAllocated)
+ {
+ _rootRef.Free();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Controls/Other/NotifyIcon.cs b/HandyControl/Controls/Other/NotifyIcon.cs
deleted file mode 100644
index c792ac4f..00000000
--- a/HandyControl/Controls/Other/NotifyIcon.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.Windows;
-using System.Windows.Controls.Primitives;
-using System.Windows.Media;
-using HandyControl.Tools;
-
-namespace HandyControl.Controls
-{
- public class NotifyIcon : FrameworkElement
- {
- private Popup _trayPopup;
-
- private readonly object _syncObj = new object();
-
- public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
- "Icon", typeof(ImageSource), typeof(NotifyIcon), new PropertyMetadata(default(ImageSource)));
-
- public ImageSource Icon
- {
- get => (ImageSource) GetValue(IconProperty);
- set => SetValue(IconProperty, value);
- }
-
- private void UpdateIcon(bool showIconInTray)
- {
- lock (_syncObj)
- {
- if (DesignerHelper.IsInDesignMode) return;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/HandyControl/Data/Enum/ToolTipIcon.cs b/HandyControl/Data/Enum/ToolTipIcon.cs
new file mode 100644
index 00000000..d14beaed
--- /dev/null
+++ b/HandyControl/Data/Enum/ToolTipIcon.cs
@@ -0,0 +1,10 @@
+namespace HandyControl.Data
+{
+ public enum ToolTipIcon
+ {
+ None,
+ Info,
+ Warning,
+ Error
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Data/Icon.cs b/HandyControl/Data/Icon.cs
new file mode 100644
index 00000000..d962da56
--- /dev/null
+++ b/HandyControl/Data/Icon.cs
@@ -0,0 +1,41 @@
+using HandyControl.Tools.Interop;
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace HandyControl.Data
+{
+ public sealed class Icon : IDisposable
+ {
+ private IntPtr _handle;
+
+ ~Icon()
+ {
+ Dispose(false);
+ }
+
+ internal Icon(IntPtr handle)
+ {
+ if (handle == IntPtr.Zero)
+ {
+ throw new ArgumentException("InvalidGDIHandle", typeof(Icon).Name);
+ }
+ _handle = handle;
+ }
+
+ [SuppressMessage("ReSharper", "UnusedParameter.Local")]
+ private void Dispose(bool disposing)
+ {
+ if (_handle != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.DestroyIcon(_handle);
+ _handle = IntPtr.Zero;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/HandyControl.csproj b/HandyControl/HandyControl.csproj
index 3cb91255..b4fcfd7b 100644
--- a/HandyControl/HandyControl.csproj
+++ b/HandyControl/HandyControl.csproj
@@ -115,8 +115,9 @@
-
+
+
@@ -172,11 +173,13 @@
+
+
@@ -224,8 +227,13 @@
+
+
+
+
+
@@ -305,6 +313,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HandyControl/Interactivity/EventToCommand.cs b/HandyControl/Interactivity/EventToCommand.cs
index de449aa5..dca2bfae 100644
--- a/HandyControl/Interactivity/EventToCommand.cs
+++ b/HandyControl/Interactivity/EventToCommand.cs
@@ -1,4 +1,4 @@
-/*refrence from https://github.com/lbugnion/mvvmlight*/
+// reference from https://github.com/lbugnion/mvvmlight
//The MIT License(MIT)
diff --git a/HandyControl/Tools/Helper/CreateParams.cs b/HandyControl/Tools/Helper/CreateParams.cs
new file mode 100644
index 00000000..03f4d550
--- /dev/null
+++ b/HandyControl/Tools/Helper/CreateParams.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Text;
+
+namespace HandyControl.Tools
+{
+ internal class CreateParams
+ {
+ public string ClassName { get; set; }
+
+ public string Caption { get; set; }
+
+ public int Style { get; set; }
+
+ public int ExStyle{ get; set; }
+
+ public int ClassStyle { get; set; }
+
+ public int X { get; set; }
+
+ public int Y { get; set; }
+
+ public int Width { get; set; }
+
+ public int Height { get; set; }
+
+ public IntPtr Parent { get; set; }
+
+ public object Param { get; set; }
+
+ public override string ToString()
+ {
+ var sb = new StringBuilder(64);
+ sb.Append("CreateParams {'");
+ sb.Append(ClassName);
+ sb.Append("', '");
+ sb.Append(Caption);
+ sb.Append("', 0x");
+ sb.Append(Convert.ToString(Style, 16));
+ sb.Append(", 0x");
+ sb.Append(Convert.ToString(ExStyle, 16));
+ sb.Append(", {");
+ sb.Append(X);
+ sb.Append(", ");
+ sb.Append(Y);
+ sb.Append(", ");
+ sb.Append(Width);
+ sb.Append(", ");
+ sb.Append(Height);
+ sb.Append("}");
+ sb.Append("}");
+ return sb.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Helper/ExternDllHelper.cs b/HandyControl/Tools/Helper/ExternDllHelper.cs
index 77a55d5f..858885dc 100644
--- a/HandyControl/Tools/Helper/ExternDllHelper.cs
+++ b/HandyControl/Tools/Helper/ExternDllHelper.cs
@@ -17,49 +17,7 @@ namespace HandyControl.Tools
private const string User32 = "user32.dll";
- private const string Shell32 = "shell32.dll";
-
- public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
-
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
- public class NOTIFYICONDATA
- {
- public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA));
- public IntPtr hWnd;
- public int uID;
- public int uFlags;
- public int uCallbackMessage;
- public IntPtr hIcon;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
- public string szTip;
- public int dwState = 0;
- public int dwStateMask = 0;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
- public string szInfo;
- public int uTimeoutOrVersion;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
- public string szInfoTitle;
- public int dwInfoFlags;
- }
-
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
- public class WNDCLASS_D
- {
- public int style;
- public WndProc lpfnWndProc;
- public int cbClsExtra = 0;
- public int cbWndExtra = 0;
- public IntPtr hInstance = IntPtr.Zero;
- public IntPtr hIcon = IntPtr.Zero;
- public IntPtr hCursor = IntPtr.Zero;
- public IntPtr hbrBackground = IntPtr.Zero;
- public string lpszMenuName = null;
- public string lpszClassName = null;
- }
-
- [DllImport(Shell32, CharSet = CharSet.Auto)]
- [ResourceExposure(ResourceScope.None)]
- public static extern int Shell_NotifyIcon(int message, NOTIFYICONDATA pnid);
+ public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
public const int E_FAIL = unchecked((int) 0x80004005);
@@ -114,7 +72,6 @@ namespace HandyControl.Tools
[In]
int len);
-
int Write(
[In]
IntPtr buf,
@@ -128,7 +85,6 @@ namespace HandyControl.Tools
[In]
int dwOrigin);
-
void SetSize(
[In, MarshalAs(UnmanagedType.I8)]
long libNewSize);
@@ -142,15 +98,12 @@ namespace HandyControl.Tools
[Out, MarshalAs(UnmanagedType.LPArray)]
long[] pcbRead);
-
void Commit(
[In]
int grfCommitFlags);
-
void Revert();
-
void LockRegion(
[In, MarshalAs(UnmanagedType.I8)]
long libOffset,
@@ -159,7 +112,6 @@ namespace HandyControl.Tools
[In]
int dwLockType);
-
void UnlockRegion(
[In, MarshalAs(UnmanagedType.I8)]
long libOffset,
@@ -168,7 +120,6 @@ namespace HandyControl.Tools
[In]
int dwLockType);
-
void Stat(
[In]
IntPtr pStatstg,
diff --git a/HandyControl/Tools/Helper/IconHelper.cs b/HandyControl/Tools/Helper/IconHelper.cs
new file mode 100644
index 00000000..a45b146c
--- /dev/null
+++ b/HandyControl/Tools/Helper/IconHelper.cs
@@ -0,0 +1,292 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using HandyControl.Tools.Interop;
+
+namespace HandyControl.Tools
+{
+ internal static class IconHelper
+ {
+ private static Size _smallIconSize;
+ private static Size _iconSize;
+ private static int _systemBitDepth;
+
+ [SecurityCritical, SecuritySafeCritical]
+ public static void GetIconHandlesFromImageSource(ImageSource image, out IconHandle largeIconHandle, out IconHandle smallIconHandle)
+ {
+ EnsureSystemMetrics();
+ largeIconHandle = CreateIconHandleFromImageSource(image, _iconSize);
+ smallIconHandle = CreateIconHandleFromImageSource(image, _smallIconSize);
+ }
+
+ [SecurityCritical]
+ public static IconHandle CreateIconHandleFromImageSource(ImageSource image, Size size)
+ {
+ EnsureSystemMetrics();
+
+ var asGoodAsItGets = false;
+
+ var bf = image as BitmapFrame;
+ if (bf?.Decoder?.Frames != null)
+ {
+ bf = GetBestMatch(bf.Decoder.Frames, size);
+
+ asGoodAsItGets = bf.Decoder is IconBitmapDecoder || bf.PixelWidth == (int)size.Width && bf.PixelHeight == (int)size.Height;
+
+ image = bf;
+ }
+
+ if (!asGoodAsItGets)
+ {
+ bf = BitmapFrame.Create(GenerateBitmapSource(image, size));
+ }
+
+ return CreateIconHandleFromBitmapFrame(bf);
+ }
+
+ [SecurityCritical]
+ private static IconHandle CreateIconHandleFromBitmapFrame(BitmapFrame sourceBitmapFrame)
+ {
+ BitmapSource bitmapSource = sourceBitmapFrame;
+
+ if (bitmapSource.Format != PixelFormats.Bgra32 && bitmapSource.Format != PixelFormats.Pbgra32)
+ {
+ bitmapSource = new FormatConvertedBitmap(bitmapSource, PixelFormats.Bgra32, null, 0.0);
+ }
+
+ var w = bitmapSource.PixelWidth;
+ var h = bitmapSource.PixelHeight;
+ var bpp = bitmapSource.Format.BitsPerPixel;
+ var stride = (bpp * w + 31) / 32 * 4;
+ var sizeCopyPixels = stride * h;
+ var xor = new byte[sizeCopyPixels];
+ bitmapSource.CopyPixels(xor, stride, 0);
+
+ return CreateIconCursor(xor, w, h, 0, 0, true);
+ }
+
+ [SecurityCritical]
+ internal static IconHandle CreateIconCursor(byte[] colorArray, int width, int height, int xHotspot, int yHotspot, bool isIcon)
+ {
+ BitmapHandle colorBitmap = null;
+ BitmapHandle maskBitmap = null;
+
+ try
+ {
+ var bi = new BITMAPINFO(width, -height, 32)
+ {
+ bmiHeader_biCompression = NativeMethods.BI_RGB
+ };
+
+ var bits = IntPtr.Zero;
+ colorBitmap = UnsafeNativeMethods.CreateDIBSection(new HandleRef(null, IntPtr.Zero), ref bi, NativeMethods.DIB_RGB_COLORS, ref bits, null, 0);
+
+ if (colorBitmap.IsInvalid || bits == IntPtr.Zero)
+ {
+ return IconHandle.GetInvalidIcon();
+ }
+
+ Marshal.Copy(colorArray, 0, bits, colorArray.Length);
+ var maskArray = GenerateMaskArray(width, height, colorArray);
+
+ maskBitmap = UnsafeNativeMethods.CreateBitmap(width, height, 1, 1, maskArray);
+ if (maskBitmap.IsInvalid)
+ {
+ return IconHandle.GetInvalidIcon();
+ }
+
+ var iconInfo = new ICONINFO
+ {
+ fIcon = isIcon,
+ xHotspot = xHotspot,
+ yHotspot = yHotspot,
+ hbmMask = maskBitmap,
+ hbmColor = colorBitmap
+ };
+
+ return UnsafeNativeMethods.CreateIconIndirect(iconInfo);
+ }
+ finally
+ {
+ colorBitmap?.Dispose();
+ maskBitmap?.Dispose();
+ }
+ }
+
+ private static byte[] GenerateMaskArray(int width, int height, byte[] colorArray)
+ {
+ var nCount = width * height;
+ var bytesPerScanLine = AlignToBytes(width, 2) / 8;
+ var bitsMask = new byte[bytesPerScanLine * height];
+
+ for (var i = 0; i < nCount; i++)
+ {
+ var hPos = i % width;
+ var vPos = i / width;
+ var byteIndex = hPos / 8;
+ var offsetBit = (byte)(0x80 >> (hPos % 8));
+
+ if (colorArray[i * 4 + 3] == 0x00)
+ {
+ bitsMask[byteIndex + bytesPerScanLine * vPos] |= offsetBit;
+ }
+ else
+ {
+ bitsMask[byteIndex + bytesPerScanLine * vPos] &= (byte)~offsetBit;
+ }
+
+ if (hPos == width - 1 && width == 8)
+ {
+ bitsMask[1 + bytesPerScanLine * vPos] = 0xff;
+ }
+ }
+
+ return bitsMask;
+ }
+
+ internal static int AlignToBytes(double original, int nBytesCount)
+ {
+ var nBitsCount = 8 << (nBytesCount - 1);
+ return ((int)Math.Ceiling(original) + (nBitsCount - 1)) / nBitsCount * nBitsCount;
+ }
+
+ private static BitmapSource GenerateBitmapSource(ImageSource img, Size renderSize)
+ {
+ var drawingDimensions = new Rect(0, 0, renderSize.Width, renderSize.Height);
+
+ var renderRatio = renderSize.Width / renderSize.Height;
+ var aspectRatio = img.Width / img.Height;
+
+ if (img.Width <= renderSize.Width && img.Height <= renderSize.Height)
+ {
+ drawingDimensions = new Rect((renderSize.Width - img.Width) / 2, (renderSize.Height - img.Height) / 2, img.Width, img.Height);
+ }
+ else if (renderRatio > aspectRatio)
+ {
+ var scaledRenderWidth = (img.Width / img.Height) * renderSize.Width;
+ drawingDimensions = new Rect((renderSize.Width - scaledRenderWidth) / 2, 0, scaledRenderWidth, renderSize.Height);
+ }
+ else if (renderRatio < aspectRatio)
+ {
+ var scaledRenderHeight = img.Height / img.Width * renderSize.Height;
+ drawingDimensions = new Rect(0, (renderSize.Height - scaledRenderHeight) / 2, renderSize.Width, scaledRenderHeight);
+ }
+
+ var dv = new DrawingVisual();
+ var dc = dv.RenderOpen();
+ dc.DrawImage(img, drawingDimensions);
+ dc.Close();
+
+ var bmp = new RenderTargetBitmap((int)renderSize.Width, (int)renderSize.Height, 96, 96, PixelFormats.Pbgra32);
+ bmp.Render(dv);
+
+ return bmp;
+ }
+
+ private static BitmapFrame GetBestMatch(ReadOnlyCollection frames, Size size)
+ {
+ var bestScore = int.MaxValue;
+ var bestBpp = 0;
+ var bestIndex = 0;
+
+ var isBitmapIconDecoder = frames[0].Decoder is IconBitmapDecoder;
+
+ for (int i = 0; i < frames.Count && bestScore != 0; ++i)
+ {
+ var currentIconBitDepth = isBitmapIconDecoder ? frames[i].Thumbnail.Format.BitsPerPixel : frames[i].Format.BitsPerPixel;
+
+ if (currentIconBitDepth == 0)
+ {
+ currentIconBitDepth = 8;
+ }
+
+ var score = MatchImage(frames[i], size, currentIconBitDepth);
+ if (score < bestScore)
+ {
+ bestIndex = i;
+ bestBpp = currentIconBitDepth;
+ bestScore = score;
+ }
+ else if (score == bestScore)
+ {
+ if (bestBpp < currentIconBitDepth)
+ {
+ bestIndex = i;
+ bestBpp = currentIconBitDepth;
+ }
+ }
+ }
+
+ return frames[bestIndex];
+ }
+
+ private static int MatchImage(BitmapFrame frame, Size size, int bpp)
+ {
+ var score = 2 * MyAbs(bpp, _systemBitDepth, false) +
+ MyAbs(frame.PixelWidth, (int)size.Width, true) +
+ MyAbs(frame.PixelHeight, (int)size.Height, true);
+
+ return score;
+ }
+
+ private static int MyAbs(int valueHave, int valueWant, bool fPunish)
+ {
+ var diff = (valueHave - valueWant);
+
+ if (diff < 0)
+ {
+ diff = (fPunish ? -2 : -1) * diff;
+ }
+
+ return diff;
+ }
+
+ [SecurityCritical, SecuritySafeCritical]
+ private static void EnsureSystemMetrics()
+ {
+ if (_systemBitDepth == 0)
+ {
+ var hdcDesktop = new HandleRef(null, UnsafeNativeMethods.GetDC(new HandleRef()));
+ try
+ {
+ var sysBitDepth = UnsafeNativeMethods.GetDeviceCaps(hdcDesktop, NativeMethods.BITSPIXEL);
+ sysBitDepth *= UnsafeNativeMethods.GetDeviceCaps(hdcDesktop, NativeMethods.PLANES);
+
+ if (sysBitDepth == 8)
+ {
+ sysBitDepth = 4;
+ }
+
+ var cxSmallIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CXSMICON);
+ var cySmallIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CYSMICON);
+ var cxIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CXICON);
+ var cyIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CYICON);
+
+ _smallIconSize = new Size(cxSmallIcon, cySmallIcon);
+ _iconSize = new Size(cxIcon, cyIcon);
+ _systemBitDepth = sysBitDepth;
+ }
+ finally
+ {
+ UnsafeNativeMethods.ReleaseDC(new HandleRef(), hdcDesktop);
+ }
+ }
+ }
+
+ [SecurityCritical, SecuritySafeCritical]
+ public static void GetDefaultIconHandles(out IconHandle largeIconHandle, out IconHandle smallIconHandle)
+ {
+ largeIconHandle = null;
+ smallIconHandle = null;
+
+ SecurityHelper.DemandUIWindowPermission();
+
+ var iconModuleFile = UnsafeNativeMethods.GetModuleFileName(new HandleRef());
+ UnsafeNativeMethods.ExtractIconEx(iconModuleFile, 0, out largeIconHandle, out smallIconHandle, 1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Helper/SecurityHelper.cs b/HandyControl/Tools/Helper/SecurityHelper.cs
new file mode 100644
index 00000000..af582881
--- /dev/null
+++ b/HandyControl/Tools/Helper/SecurityHelper.cs
@@ -0,0 +1,20 @@
+using System.Security;
+using System.Security.Permissions;
+
+namespace HandyControl.Tools
+{
+ internal class SecurityHelper
+ {
+ private static UIPermission _allWindowsUIPermission;
+
+ [SecurityCritical]
+ internal static void DemandUIWindowPermission()
+ {
+ if (_allWindowsUIPermission == null)
+ {
+ _allWindowsUIPermission = new UIPermission(UIPermissionWindow.AllWindows);
+ }
+ _allWindowsUIPermission.Demand();
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/BITMAPINFO.cs b/HandyControl/Tools/Interop/BITMAPINFO.cs
new file mode 100644
index 00000000..4a803564
--- /dev/null
+++ b/HandyControl/Tools/Interop/BITMAPINFO.cs
@@ -0,0 +1,52 @@
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace HandyControl.Tools.Interop
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 2)]
+ internal struct BITMAPINFO
+ {
+ public int bmiHeader_biSize;
+
+ public int bmiHeader_biWidth;
+
+ public int bmiHeader_biHeight;
+
+ public short bmiHeader_biPlanes;
+
+ public short bmiHeader_biBitCount;
+
+ public int bmiHeader_biCompression;
+
+ public int bmiHeader_biSizeImage;
+
+ public int bmiHeader_biXPelsPerMeter;
+
+ public int bmiHeader_biYPelsPerMeter;
+
+ public int bmiHeader_biClrUsed;
+
+ public int bmiHeader_biClrImportant;
+
+ public BITMAPINFO(int width, int height, short bpp)
+ {
+ bmiHeader_biSize = SizeOf();
+ bmiHeader_biWidth = width;
+ bmiHeader_biHeight = height;
+ bmiHeader_biPlanes = 1;
+ bmiHeader_biBitCount = bpp;
+ bmiHeader_biCompression = 0;
+ bmiHeader_biSizeImage = 0;
+ bmiHeader_biXPelsPerMeter = 0;
+ bmiHeader_biYPelsPerMeter = 0;
+ bmiHeader_biClrUsed = 0;
+ bmiHeader_biClrImportant = 0;
+ }
+
+ [SecuritySafeCritical]
+ private static int SizeOf()
+ {
+ return Marshal.SizeOf(typeof(BITMAPINFO));
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/BitmapHandle.cs b/HandyControl/Tools/Interop/BitmapHandle.cs
new file mode 100644
index 00000000..2970e514
--- /dev/null
+++ b/HandyControl/Tools/Interop/BitmapHandle.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace HandyControl.Tools.Interop
+{
+ internal sealed class BitmapHandle : WpfSafeHandle
+ {
+ [SecurityCritical]
+ private BitmapHandle(bool ownsHandle) : base(ownsHandle, CommonHandles.GDI)
+ {
+ }
+
+ [SecurityCritical]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ protected override bool ReleaseHandle()
+ {
+ return UnsafeNativeMethods.DeleteObject(handle);
+ }
+
+ [SecurityCritical]
+ internal HandleRef MakeHandleRef(object obj)
+ {
+ return new HandleRef(obj, handle);
+ }
+
+ [SecurityCritical]
+ internal static BitmapHandle CreateFromHandle(IntPtr hbitmap, bool ownsHandle = true)
+ {
+ return new BitmapHandle(ownsHandle)
+ {
+ handle = hbitmap,
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/CommonHandles.cs b/HandyControl/Tools/Interop/CommonHandles.cs
new file mode 100644
index 00000000..deb2a7f5
--- /dev/null
+++ b/HandyControl/Tools/Interop/CommonHandles.cs
@@ -0,0 +1,13 @@
+namespace HandyControl.Tools.Interop
+{
+ internal static class CommonHandles
+ {
+ public static readonly int Icon = HandleCollector.RegisterType(nameof(Icon), 20, 500);
+
+ public static readonly int HDC = HandleCollector.RegisterType(nameof(HDC), 100, 2);
+
+ public static readonly int GDI = HandleCollector.RegisterType(nameof(GDI), 50, 500);
+
+ public static readonly int Kernel = HandleCollector.RegisterType(nameof(Kernel), 0, 1000);
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/ExternDll.cs b/HandyControl/Tools/Interop/ExternDll.cs
new file mode 100644
index 00000000..81fa0c71
--- /dev/null
+++ b/HandyControl/Tools/Interop/ExternDll.cs
@@ -0,0 +1,11 @@
+namespace HandyControl.Tools.Interop
+{
+ internal static class ExternDll
+ {
+ public const string
+ User32 = "user32.dll",
+ Gdi32 = "gdi32.dll",
+ Kernel32 = "kernel32.dll",
+ Shell32 = "shell32.dll";
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/HandleCollector.cs b/HandyControl/Tools/Interop/HandleCollector.cs
new file mode 100644
index 00000000..b53f3c96
--- /dev/null
+++ b/HandyControl/Tools/Interop/HandleCollector.cs
@@ -0,0 +1,132 @@
+// reference from https://referencesource.microsoft.com/#WindowsBase/Shared/MS/Win32/HandleCollector.cs,d0f99220d8e1b708
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace HandyControl.Tools.Interop
+{
+ internal static class HandleCollector
+ {
+ private static HandleType[] HandleTypes;
+ private static int HandleTypeCount;
+
+ private static readonly object HandleMutex = new object();
+
+ internal static IntPtr Add(IntPtr handle, int type)
+ {
+ HandleTypes[type - 1].Add();
+ return handle;
+ }
+
+ [System.Security.SecuritySafeCritical]
+ internal static SafeHandle Add(SafeHandle handle, int type)
+ {
+ HandleTypes[type - 1].Add();
+ return handle;
+ }
+
+ internal static void Add(int type)
+ {
+ HandleTypes[type - 1].Add();
+ }
+
+ internal static int RegisterType(string typeName, int expense, int initialThreshold)
+ {
+ lock (HandleMutex)
+ {
+ if (HandleTypeCount == 0 || HandleTypeCount == HandleTypes.Length)
+ {
+ HandleType[] newTypes = new HandleType[HandleTypeCount + 10];
+ if (HandleTypes != null)
+ {
+ Array.Copy(HandleTypes, 0, newTypes, 0, HandleTypeCount);
+ }
+ HandleTypes = newTypes;
+ }
+
+ HandleTypes[HandleTypeCount++] = new HandleType(expense, initialThreshold);
+ return HandleTypeCount;
+ }
+ }
+
+ internal static IntPtr Remove(IntPtr handle, int type)
+ {
+ HandleTypes[type - 1].Remove();
+ return handle;
+ }
+
+ [System.Security.SecuritySafeCritical]
+ internal static SafeHandle Remove(SafeHandle handle, int type)
+ {
+ HandleTypes[type - 1].Remove();
+ return handle;
+ }
+
+ internal static void Remove(int type)
+ {
+ HandleTypes[type - 1].Remove();
+ }
+
+ private class HandleType
+ {
+ private readonly int _initialThreshHold;
+ private int _threshHold;
+ private int _handleCount;
+ private readonly int _deltaPercent;
+
+ internal HandleType(int expense, int initialThreshHold)
+ {
+ _initialThreshHold = initialThreshHold;
+ _threshHold = initialThreshHold;
+ _deltaPercent = 100 - expense;
+ }
+
+ internal void Add()
+ {
+ lock (this)
+ {
+ _handleCount++;
+ var performCollect = NeedCollection();
+
+ if (!performCollect)
+ {
+ return;
+ }
+ }
+
+ GC.Collect();
+
+ var sleep = (100 - _deltaPercent) / 4;
+ System.Threading.Thread.Sleep(sleep);
+ }
+
+ private bool NeedCollection()
+ {
+
+ if (_handleCount > _threshHold)
+ {
+ _threshHold = _handleCount + ((_handleCount * _deltaPercent) / 100);
+ return true;
+ }
+
+ var oldThreshHold = (100 * _threshHold) / (100 + _deltaPercent);
+ if (oldThreshHold >= _initialThreshHold && _handleCount < (int)(oldThreshHold * .9F))
+ {
+ _threshHold = oldThreshHold;
+ }
+
+ return false;
+ }
+
+ internal void Remove()
+ {
+ lock (this)
+ {
+ _handleCount--;
+
+ _handleCount = Math.Max(0, _handleCount);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/ICONINFO.cs b/HandyControl/Tools/Interop/ICONINFO.cs
new file mode 100644
index 00000000..564e8df9
--- /dev/null
+++ b/HandyControl/Tools/Interop/ICONINFO.cs
@@ -0,0 +1,14 @@
+using System.Runtime.InteropServices;
+
+namespace HandyControl.Tools.Interop
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal class ICONINFO
+ {
+ public bool fIcon = false;
+ public int xHotspot = 0;
+ public int yHotspot = 0;
+ public BitmapHandle hbmMask = null;
+ public BitmapHandle hbmColor = null;
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/IconHandle.cs b/HandyControl/Tools/Interop/IconHandle.cs
new file mode 100644
index 00000000..cbaec541
--- /dev/null
+++ b/HandyControl/Tools/Interop/IconHandle.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Runtime.ConstrainedExecution;
+using System.Security;
+
+namespace HandyControl.Tools.Interop
+{
+ internal sealed class IconHandle : WpfSafeHandle
+ {
+ [SecurityCritical]
+ private IconHandle() : base(true, CommonHandles.Icon)
+ {
+ }
+
+ [SecurityCritical]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ protected override bool ReleaseHandle()
+ {
+ return UnsafeNativeMethods.DestroyIcon(handle);
+ }
+
+ [SecurityCritical, SecuritySafeCritical]
+ internal static IconHandle GetInvalidIcon()
+ {
+ return new IconHandle();
+ }
+
+ [SecurityCritical]
+ internal IntPtr CriticalGetHandle()
+ {
+ return handle;
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/IntSecurity.cs b/HandyControl/Tools/Interop/IntSecurity.cs
new file mode 100644
index 00000000..679ddaa4
--- /dev/null
+++ b/HandyControl/Tools/Interop/IntSecurity.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Security;
+using System.Security.Permissions;
+
+namespace HandyControl.Tools.Interop
+{
+ internal class IntSecurity
+ {
+ public static CodeAccessPermission UnrestrictedWindows =>
+ new Lazy(() => AllWindows).Value;
+
+ public static CodeAccessPermission AllWindows =>
+ new Lazy(() => new UIPermission(UIPermissionWindow.AllWindows)).Value;
+
+ public static CodeAccessPermission CreateAnyWindow =>
+ new Lazy(() => SafeSubWindows).Value;
+
+ public static CodeAccessPermission SafeSubWindows =>
+ new Lazy(() => new UIPermission(UIPermissionWindow.SafeSubWindows)).Value;
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/NOTIFYICONDATA.cs b/HandyControl/Tools/Interop/NOTIFYICONDATA.cs
new file mode 100644
index 00000000..6794cbb0
--- /dev/null
+++ b/HandyControl/Tools/Interop/NOTIFYICONDATA.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+
+namespace HandyControl.Tools.Interop
+{
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public class NOTIFYICONDATA
+ {
+ public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA));
+ public IntPtr hWnd;
+ public int uID;
+ public int uFlags;
+ public int uCallbackMessage;
+ public IntPtr hIcon;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
+ public string szTip;
+ public int dwState = 0;
+ public int dwStateMask = 0;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+ public string szInfo;
+ public int uTimeoutOrVersion;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
+ public string szInfoTitle;
+ public int dwInfoFlags;
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/NativeMethods.cs b/HandyControl/Tools/Interop/NativeMethods.cs
new file mode 100644
index 00000000..c670c683
--- /dev/null
+++ b/HandyControl/Tools/Interop/NativeMethods.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+
+namespace HandyControl.Tools.Interop
+{
+ public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
+
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ [SuppressMessage("ReSharper", "IdentifierTypo")]
+ internal class NativeMethods
+ {
+ public const int
+ WS_CHILD = 0x40000000,
+ WM_USER = 0x0400,
+ BITSPIXEL = 12,
+ PLANES = 14,
+ BI_RGB = 0,
+ DIB_RGB_COLORS = 0,
+ NIF_MESSAGE = 0x00000001,
+ NIF_INFO = 0x00000010,
+ NIF_ICON = 0x00000002,
+ NIF_TIP = 0x00000004,
+ NIM_ADD = 0x00000000,
+ NIM_DELETE = 0x00000002,
+ NIM_MODIFY = 0x00000001,
+ NIIF_NONE = 0x00000000,
+ NIIF_INFO = 0x00000001,
+ NIIF_WARNING = 0x00000002,
+ NIIF_ERROR = 0x00000003;
+
+ [DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern int RegisterWindowMessage(string msg);
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/NativeValues.cs b/HandyControl/Tools/Interop/NativeValues.cs
new file mode 100644
index 00000000..628123f7
--- /dev/null
+++ b/HandyControl/Tools/Interop/NativeValues.cs
@@ -0,0 +1,98 @@
+namespace HandyControl.Tools.Interop
+{
+ internal enum SM
+ {
+ CXSCREEN = 0,
+ CYSCREEN = 1,
+ CXVSCROLL = 2,
+ CYHSCROLL = 3,
+ CYCAPTION = 4,
+ CXBORDER = 5,
+ CYBORDER = 6,
+ CXFIXEDFRAME = 7,
+ CYFIXEDFRAME = 8,
+ CYVTHUMB = 9,
+ CXHTHUMB = 10,
+ CXICON = 11,
+ CYICON = 12,
+ CXCURSOR = 13,
+ CYCURSOR = 14,
+ CYMENU = 15,
+ CXFULLSCREEN = 16,
+ CYFULLSCREEN = 17,
+ CYKANJIWINDOW = 18,
+ MOUSEPRESENT = 19,
+ CYVSCROLL = 20,
+ CXHSCROLL = 21,
+ DEBUG = 22,
+ SWAPBUTTON = 23,
+ CXMIN = 28,
+ CYMIN = 29,
+ CXSIZE = 30,
+ CYSIZE = 31,
+ CXFRAME = 32,
+ CXSIZEFRAME = CXFRAME,
+ CYFRAME = 33,
+ CYSIZEFRAME = CYFRAME,
+ CXMINTRACK = 34,
+ CYMINTRACK = 35,
+ CXDOUBLECLK = 36,
+ CYDOUBLECLK = 37,
+ CXICONSPACING = 38,
+ CYICONSPACING = 39,
+ MENUDROPALIGNMENT = 40,
+ PENWINDOWS = 41,
+ DBCSENABLED = 42,
+ CMOUSEBUTTONS = 43,
+ SECURE = 44,
+ CXEDGE = 45,
+ CYEDGE = 46,
+ CXMINSPACING = 47,
+ CYMINSPACING = 48,
+ CXSMICON = 49,
+ CYSMICON = 50,
+ CYSMCAPTION = 51,
+ CXSMSIZE = 52,
+ CYSMSIZE = 53,
+ CXMENUSIZE = 54,
+ CYMENUSIZE = 55,
+ ARRANGE = 56,
+ CXMINIMIZED = 57,
+ CYMINIMIZED = 58,
+ CXMAXTRACK = 59,
+ CYMAXTRACK = 60,
+ CXMAXIMIZED = 61,
+ CYMAXIMIZED = 62,
+ NETWORK = 63,
+ CLEANBOOT = 67,
+ CXDRAG = 68,
+ CYDRAG = 69,
+ SHOWSOUNDS = 70,
+ CXMENUCHECK = 71,
+ CYMENUCHECK = 72,
+ SLOWMACHINE = 73,
+ MIDEASTENABLED = 74,
+ MOUSEWHEELPRESENT = 75,
+ XVIRTUALSCREEN = 76,
+ YVIRTUALSCREEN = 77,
+ CXVIRTUALSCREEN = 78,
+ CYVIRTUALSCREEN = 79,
+ CMONITORS = 80,
+ SAMEDISPLAYFORMAT = 81,
+ IMMENABLED = 82,
+ CXFOCUSBORDER = 83,
+ CYFOCUSBORDER = 84,
+ TABLETPC = 86,
+ MEDIACENTER = 87,
+ REMOTESESSION = 0x1000,
+ REMOTECONTROL = 0x2001,
+ }
+
+ internal static class Win32Constant
+ {
+ internal const int MAX_PATH = 260;
+ internal const int INFOTIPSIZE = 1024;
+ internal const int TRUE = 1;
+ internal const int FALSE = 0;
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/SafeFileMappingHandle.cs b/HandyControl/Tools/Interop/SafeFileMappingHandle.cs
new file mode 100644
index 00000000..ad26db90
--- /dev/null
+++ b/HandyControl/Tools/Interop/SafeFileMappingHandle.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Security.Permissions;
+using Microsoft.Win32.SafeHandles;
+
+namespace HandyControl.Tools.Interop
+{
+ internal sealed class SafeFileMappingHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ [SecurityCritical]
+ internal SafeFileMappingHandle(IntPtr handle) : base(false)
+ {
+ SetHandle(handle);
+ }
+
+ [SecurityCritical, SecuritySafeCritical]
+ internal SafeFileMappingHandle() : base(true)
+ {
+ }
+
+ public override bool IsInvalid
+ {
+ [SecurityCritical, SecuritySafeCritical]
+ get => handle == IntPtr.Zero;
+ }
+
+ [SecurityCritical, SecuritySafeCritical]
+ protected override bool ReleaseHandle()
+ {
+ new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
+ try
+ {
+ return CloseHandleNoThrow(new HandleRef(null, handle));
+ }
+ finally
+ {
+ CodeAccessPermission.RevertAssert();
+ }
+ }
+
+ [SecurityCritical]
+ public static bool CloseHandleNoThrow(HandleRef handle)
+ {
+ HandleCollector.Remove((IntPtr)handle, CommonHandles.Kernel);
+ var result = UnsafeNativeMethods.IntCloseHandle(handle);
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/UnsafeNativeMethods.cs b/HandyControl/Tools/Interop/UnsafeNativeMethods.cs
new file mode 100644
index 00000000..ed6eb2a4
--- /dev/null
+++ b/HandyControl/Tools/Interop/UnsafeNativeMethods.cs
@@ -0,0 +1,173 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Security;
+using System.Text;
+
+namespace HandyControl.Tools.Interop
+{
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ internal class UnsafeNativeMethods
+ {
+ [SecurityCritical]
+ [SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, EntryPoint = nameof(GetDC), CharSet = CharSet.Auto)]
+ public static extern IntPtr IntGetDC(HandleRef hWnd);
+
+ [SecurityCritical]
+ public static IntPtr GetDC(HandleRef hWnd)
+ {
+ var hDc = IntGetDC(hWnd);
+ if (hDc == IntPtr.Zero)
+ {
+ throw new Win32Exception();
+ }
+
+ return HandleCollector.Add(hDc, CommonHandles.HDC);
+ }
+
+ [SecurityCritical]
+ [SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.User32, ExactSpelling = true, EntryPoint = nameof(ReleaseDC), CharSet = CharSet.Auto)]
+ public static extern int IntReleaseDC(HandleRef hWnd, HandleRef hDC);
+
+ [SecurityCritical]
+ public static int ReleaseDC(HandleRef hWnd, HandleRef hDC)
+ {
+ HandleCollector.Remove((IntPtr)hDC, CommonHandles.HDC);
+ return IntReleaseDC(hWnd, hDC);
+ }
+
+ [SecurityCritical]
+ [SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
+ public static extern int GetDeviceCaps(HandleRef hDC, int nIndex);
+
+ [SecurityCritical]
+ [SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.User32)]
+ public static extern int GetSystemMetrics(SM nIndex);
+
+ [SecurityCritical]
+ [SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.User32, EntryPoint = nameof(DestroyIcon), CharSet = CharSet.Auto, SetLastError = true)]
+ private static extern bool IntDestroyIcon(IntPtr hIcon);
+
+ [SecurityCritical]
+ public static bool DestroyIcon(IntPtr hIcon)
+ {
+ var result = IntDestroyIcon(hIcon);
+ return result;
+ }
+
+ [SecurityCritical]
+ [SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.Gdi32, EntryPoint = nameof(DeleteObject), CharSet = CharSet.Auto, SetLastError = true)]
+ private static extern bool IntDeleteObject(IntPtr hObject);
+
+ [SecurityCritical]
+ public static bool DeleteObject(IntPtr hObject)
+ {
+ var result = IntDeleteObject(hObject);
+ return result;
+ }
+
+ [SecurityCritical]
+ public static BitmapHandle CreateDIBSection(HandleRef hdc, ref BITMAPINFO bitmapInfo, int iUsage, ref IntPtr ppvBits, SafeFileMappingHandle hSection, int dwOffset)
+ {
+ if (hSection == null)
+ {
+ hSection = new SafeFileMappingHandle(IntPtr.Zero);
+ }
+
+ var hBitmap = PrivateCreateDIBSection(hdc, ref bitmapInfo, iUsage, ref ppvBits, hSection, dwOffset);
+ return hBitmap;
+ }
+
+ [DllImport(ExternDll.Kernel32, EntryPoint = "CloseHandle", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern bool IntCloseHandle(HandleRef handle);
+
+ [SecurityCritical, SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, EntryPoint = nameof(CreateDIBSection))]
+ private static extern BitmapHandle PrivateCreateDIBSection(HandleRef hdc, ref BITMAPINFO bitmapInfo, int iUsage, ref IntPtr ppvBits, SafeFileMappingHandle hSection, int dwOffset);
+
+ [SecurityCritical, SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, EntryPoint = nameof(CreateIconIndirect))]
+ private static extern IconHandle PrivateCreateIconIndirect([In, MarshalAs(UnmanagedType.LPStruct)]ICONINFO iconInfo);
+
+ [SecurityCritical]
+ public static IconHandle CreateIconIndirect([In, MarshalAs(UnmanagedType.LPStruct)]ICONINFO iconInfo)
+ {
+ var hIcon = PrivateCreateIconIndirect(iconInfo);
+ return hIcon;
+ }
+
+ [SecurityCritical, SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, EntryPoint = nameof(CreateBitmap))]
+ private static extern BitmapHandle PrivateCreateBitmap(int width, int height, int planes, int bitsPerPixel, byte[] lpvBits);
+
+ [SecurityCritical]
+ internal static BitmapHandle CreateBitmap(int width, int height, int planes, int bitsPerPixel, byte[] lpvBits)
+ {
+ var hBitmap = PrivateCreateBitmap(width, height, planes, bitsPerPixel, lpvBits);
+ return hBitmap;
+ }
+
+ [SecurityCritical, SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.Kernel32, EntryPoint = "GetModuleFileName", CharSet = CharSet.Unicode, SetLastError = true)]
+ private static extern int IntGetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);
+
+ [SecurityCritical]
+ internal static string GetModuleFileName(HandleRef hModule)
+ {
+ var buffer = new StringBuilder(Win32Constant.MAX_PATH);
+ while (true)
+ {
+ var size = IntGetModuleFileName(hModule, buffer, buffer.Capacity);
+ if (size == 0)
+ {
+ throw new Win32Exception();
+ }
+
+ if (size == buffer.Capacity)
+ {
+ buffer.EnsureCapacity(buffer.Capacity * 2);
+ continue;
+ }
+
+ return buffer.ToString();
+ }
+ }
+
+ [SecurityCritical, SuppressUnmanagedCodeSecurity]
+ [DllImport(ExternDll.Shell32, CharSet = CharSet.Auto, BestFitMapping = false, ThrowOnUnmappableChar = true)]
+ internal static extern int ExtractIconEx(string szExeFileName, int nIconIndex, out IconHandle phiconLarge, out IconHandle phiconSmall, int nIcons);
+
+ [DllImport(ExternDll.Shell32, CharSet = CharSet.Auto)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern int Shell_NotifyIcon(int message, NOTIFYICONDATA pnid);
+
+ [SecurityCritical]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ [DllImport(ExternDll.User32, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CreateWindowExW")]
+ public static extern IntPtr CreateWindowEx(
+ int dwExStyle,
+ [MarshalAs(UnmanagedType.LPWStr)] string lpClassName,
+ [MarshalAs(UnmanagedType.LPWStr)] string lpWindowName,
+ int dwStyle,
+ int x,
+ int y,
+ int nWidth,
+ int nHeight,
+ IntPtr hWndParent,
+ IntPtr hMenu,
+ IntPtr hInstance,
+ IntPtr lpParam);
+
+ [DllImport(ExternDll.User32, CharSet = CharSet.Auto, SetLastError = true)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern short RegisterClass(WNDCLASS wc);
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/WNDCLASS.cs b/HandyControl/Tools/Interop/WNDCLASS.cs
new file mode 100644
index 00000000..363d083f
--- /dev/null
+++ b/HandyControl/Tools/Interop/WNDCLASS.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+
+namespace HandyControl.Tools.Interop
+{
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ [SuppressMessage("ReSharper", "IdentifierTypo")]
+ internal class WNDCLASS
+ {
+ public int style;
+ public ExternDllHelper.WndProc lpfnWndProc;
+ public int cbClsExtra = 0;
+ public int cbWndExtra = 0;
+ public IntPtr hInstance = IntPtr.Zero;
+ public IntPtr hIcon = IntPtr.Zero;
+ public IntPtr hCursor = IntPtr.Zero;
+ public IntPtr hbrBackground = IntPtr.Zero;
+ public string lpszMenuName = null;
+ public string lpszClassName = null;
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/WindowClass.cs b/HandyControl/Tools/Interop/WindowClass.cs
new file mode 100644
index 00000000..db49dc77
--- /dev/null
+++ b/HandyControl/Tools/Interop/WindowClass.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Security.Permissions;
+
+namespace HandyControl.Tools.Interop
+{
+ [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ internal class WindowClass
+ {
+ internal string WindowClassName;
+
+ private static readonly int WmTaskbarcreated = NativeMethods.RegisterWindowMessage("TaskbarCreated");
+
+ internal IntPtr MessageWindowHandle { get; private set; }
+
+ private readonly object _syncObj = new object();
+
+ private bool _added;
+
+ public WindowClass()
+ {
+ RegisterClass();
+ }
+
+ private void RegisterClass()
+ {
+ WindowClassName = $"HandyControl.Controls.NotifyIcon{Guid.NewGuid()}";
+ var wndclass = new WNDCLASS
+ {
+ style = 0,
+ lpfnWndProc = Callback,
+ cbClsExtra = 0,
+ cbWndExtra = 0,
+ hInstance = IntPtr.Zero,
+ hIcon = IntPtr.Zero,
+ hCursor = IntPtr.Zero,
+ hbrBackground = IntPtr.Zero,
+ lpszMenuName = string.Empty,
+ lpszClassName = WindowClassName
+ };
+
+ UnsafeNativeMethods.RegisterClass(wndclass);
+ MessageWindowHandle = UnsafeNativeMethods.CreateWindowEx(0, WindowClassName, "", 0, 0, 0, 1, 1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
+ }
+
+ public IntPtr Callback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)
+ {
+ if (msg == WmTaskbarcreated)
+ {
+ _added = false;
+ AddIcon();
+ }
+ return IntPtr.Zero;
+ }
+
+ private void AddIcon()
+ {
+ lock (_syncObj)
+ {
+ if (DesignerHelper.IsInDesignMode) return;
+
+ //NativeMethods.NOTIFYICONDATA data = new NativeMethods.NOTIFYICONDATA();
+ //data.uCallbackMessage = WM_TRAYMOUSEMESSAGE;
+ //data.uFlags = NativeMethods.NIF_MESSAGE;
+ //if (showIconInTray)
+ //{
+ // if (window.Handle == IntPtr.Zero)
+ // {
+ // window.CreateHandle(new CreateParams());
+ // }
+ //}
+ //data.hWnd = window.Handle;
+ //data.uID = id;
+ //data.hIcon = IntPtr.Zero;
+ //data.szTip = null;
+ //if (icon != null)
+ //{
+ // data.uFlags |= NativeMethods.NIF_ICON;
+ // data.hIcon = icon.Handle;
+ //}
+ //data.uFlags |= NativeMethods.NIF_TIP;
+ //data.szTip = text;
+
+ //if (showIconInTray && icon != null)
+ //{
+ // if (!added)
+ // {
+ // UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_ADD, data);
+ // added = true;
+ // }
+ // else
+ // {
+ // UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_MODIFY, data);
+ // }
+ //}
+ //else if (added)
+ //{
+ // UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_DELETE, data);
+ // added = false;
+ //}
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Tools/Interop/WpfSafeHandle.cs b/HandyControl/Tools/Interop/WpfSafeHandle.cs
new file mode 100644
index 00000000..8141a2cf
--- /dev/null
+++ b/HandyControl/Tools/Interop/WpfSafeHandle.cs
@@ -0,0 +1,24 @@
+using System.Security;
+using Microsoft.Win32.SafeHandles;
+
+namespace HandyControl.Tools.Interop
+{
+ internal abstract class WpfSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ private readonly int _collectorId;
+
+ [SecurityCritical]
+ protected WpfSafeHandle(bool ownsHandle, int collectorId) : base(ownsHandle)
+ {
+ HandleCollector.Add(collectorId);
+ _collectorId = collectorId;
+ }
+
+ [SecurityCritical, SecuritySafeCritical]
+ protected override void Dispose(bool disposing)
+ {
+ HandleCollector.Remove(_collectorId);
+ base.Dispose(disposing);
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControlDemo/Properties/Langs/Lang.en.resx b/HandyControlDemo/Properties/Langs/Lang.en.resx
index 58dc6dc0..27a87236 100644
--- a/HandyControlDemo/Properties/Langs/Lang.en.resx
+++ b/HandyControlDemo/Properties/Langs/Lang.en.resx
@@ -500,7 +500,7 @@
NotifyIcon
-
+
Badge
diff --git a/HandyControlDemo/Properties/Langs/Lang.resx b/HandyControlDemo/Properties/Langs/Lang.resx
index 3687f4be..dc7baedd 100644
--- a/HandyControlDemo/Properties/Langs/Lang.resx
+++ b/HandyControlDemo/Properties/Langs/Lang.resx
@@ -500,7 +500,7 @@
托盘图标
-
+
标记