using System; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Windows; using System.Windows.Interop; using System.Windows.Media; using Standard; namespace Microsoft.Windows.Shell; public sealed class TaskbarItemInfo : Freezable { protected override Freezable CreateInstanceCore() { return new TaskbarItemInfo(); } [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")] public static TaskbarItemInfo GetTaskbarItemInfo(Window window) { Verify.IsNotNull(window, "window"); return (TaskbarItemInfo) window.GetValue(TaskbarItemInfo.TaskbarItemInfoProperty); } [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")] public static void SetTaskbarItemInfo(Window window, TaskbarItemInfo value) { Verify.IsNotNull(window, "window"); window.SetValue(TaskbarItemInfo.TaskbarItemInfoProperty, value); } private static void _OnTaskbarItemInfoChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (DesignerProperties.GetIsInDesignMode(d)) { return; } Window window = (Window) d; TaskbarItemInfo taskbarItemInfo = (TaskbarItemInfo) e.OldValue; TaskbarItemInfo taskbarItemInfo2 = (TaskbarItemInfo) e.NewValue; if (taskbarItemInfo == taskbarItemInfo2) { return; } if (!Utility.IsOSWindows7OrNewer) { return; } if (taskbarItemInfo != null && taskbarItemInfo._window != null) { taskbarItemInfo._DetachWindow(); } if (taskbarItemInfo2 != null) { taskbarItemInfo2._SetWindow(window); } } private static object _CoerceTaskbarItemInfoValue(DependencyObject d, object value) { if (DesignerProperties.GetIsInDesignMode(d)) { return value; } Verify.IsNotNull(d, "d"); Window window = (Window) d; TaskbarItemInfo taskbarItemInfo = (TaskbarItemInfo) value; if (taskbarItemInfo != null && taskbarItemInfo._window != null && taskbarItemInfo._window != window) { throw new NotSupportedException(); } window.VerifyAccess(); return taskbarItemInfo; } public TaskbarItemProgressState ProgressState { get { return (TaskbarItemProgressState) base.GetValue(TaskbarItemInfo.ProgressStateProperty); } set { base.SetValue(TaskbarItemInfo.ProgressStateProperty, value); } } private void _OnProgressStateChanged() { if (!this._isAttached) { return; } this._UpdateProgressState(true); } private static TaskbarItemProgressState _CoerceProgressState(TaskbarItemProgressState value) { switch (value) { case TaskbarItemProgressState.None: case TaskbarItemProgressState.Indeterminate: case TaskbarItemProgressState.Normal: case TaskbarItemProgressState.Error: case TaskbarItemProgressState.Paused: break; default: value = TaskbarItemProgressState.None; break; } return value; } public double ProgressValue { get { return (double) base.GetValue(TaskbarItemInfo.ProgressValueProperty); } set { base.SetValue(TaskbarItemInfo.ProgressValueProperty, value); } } private void _OnProgressValueChanged() { if (!this._isAttached) { return; } this._UpdateProgressValue(true); } private static double _CoerceProgressValue(double progressValue) { if (double.IsNaN(progressValue)) { progressValue = 0.0; } progressValue = Math.Max(progressValue, 0.0); progressValue = Math.Min(1.0, progressValue); return progressValue; } public ImageSource Overlay { get { return (ImageSource) base.GetValue(TaskbarItemInfo.OverlayProperty); } set { base.SetValue(TaskbarItemInfo.OverlayProperty, value); } } private void _OnOverlayChanged() { if (!this._isAttached) { return; } this._UpdateOverlay(true); } public string Description { get { return (string) base.GetValue(TaskbarItemInfo.DescriptionProperty); } set { base.SetValue(TaskbarItemInfo.DescriptionProperty, value); } } private void _OnDescriptionChanged() { if (!this._isAttached) { return; } this._UpdateTooltip(true); } public Thickness ThumbnailClipMargin { get { return (Thickness) base.GetValue(TaskbarItemInfo.ThumbnailClipMarginProperty); } set { base.SetValue(TaskbarItemInfo.ThumbnailClipMarginProperty, value); } } private void _OnThumbnailClipMarginChanged() { if (!this._isAttached) { return; } this._UpdateThumbnailClipping(true); } private static Thickness _CoerceThumbnailClipMargin(Thickness margin) { if (margin.Left < 0.0 || margin.Right < 0.0 || margin.Top < 0.0 || margin.Bottom < 0.0) { return TaskbarItemInfo._EmptyThickness; } return margin; } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")] public ThumbButtonInfoCollection ThumbButtonInfos { get { return (ThumbButtonInfoCollection) base.GetValue(TaskbarItemInfo.ThumbButtonInfosProperty); } set { base.SetValue(TaskbarItemInfo.ThumbButtonInfosProperty, value); } } private void _OnThumbButtonsChanged() { if (!this._isAttached) { return; } this._UpdateThumbButtons(true); } private IntPtr _GetHICONFromImageSource(ImageSource image, Size dimensions) { if (this._gdipToken == null) { this._gdipToken = SafeGdiplusStartupToken.Startup(); } return Utility.GenerateHICON(image, dimensions); } public TaskbarItemInfo() { if (!DesignerProperties.GetIsInDesignMode(this)) { ITaskbarList taskbarList = null; try { taskbarList = CLSID.CoCreateInstance("56FDF344-FD6D-11d0-958A-006097C9A090"); taskbarList.HrInit(); this._taskbarList = (taskbarList as ITaskbarList3); taskbarList = null; } finally { Utility.SafeRelease(ref taskbarList); } this._overlaySize = new Size((double) NativeMethods.GetSystemMetrics(SM.CXSMICON), (double) NativeMethods.GetSystemMetrics(SM.CYSMICON)); } this.ThumbButtonInfos = new ThumbButtonInfoCollection(); } private void _SetWindow(Window window) { if (window == null) { return; } this._window = window; if (this._taskbarList == null) { return; } IntPtr handle = new WindowInteropHelper(this._window).Handle; if (!(handle != IntPtr.Zero)) { this._window.SourceInitialized += this._OnWindowSourceInitialized; return; } this._hwndSource = HwndSource.FromHwnd(handle); this._hwndSource.AddHook(new HwndSourceHook(this._WndProc)); this._OnIsAttachedChanged(true); } private void _OnWindowSourceInitialized(object sender, EventArgs e) { this._window.SourceInitialized -= this._OnWindowSourceInitialized; IntPtr handle = new WindowInteropHelper(this._window).Handle; this._hwndSource = HwndSource.FromHwnd(handle); this._hwndSource.AddHook(new HwndSourceHook(this._WndProc)); MSGFLTINFO msgfltinfo; NativeMethods.ChangeWindowMessageFilterEx(handle, TaskbarItemInfo.WM_TASKBARBUTTONCREATED, MSGFLT.ALLOW, out msgfltinfo); NativeMethods.ChangeWindowMessageFilterEx(handle, WM.COMMAND, MSGFLT.ALLOW, out msgfltinfo); } private IntPtr _WndProc(IntPtr hwnd, int uMsg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (uMsg == (int) TaskbarItemInfo.WM_TASKBARBUTTONCREATED) { this._OnIsAttachedChanged(true); this._isAttached = true; handled = false; } else if (uMsg != 5) { if (uMsg == 273 && Utility.HIWORD(wParam.ToInt32()) == 6144) { int index = Utility.LOWORD(wParam.ToInt32()); this.ThumbButtonInfos[index].InvokeClick(); handled = true; } } else { this._UpdateThumbnailClipping(this._isAttached); handled = false; } return IntPtr.Zero; } private void _OnIsAttachedChanged(bool attached) { this._haveAddedButtons = false; if (!attached && this._hwndSource == null) { return; } this._UpdateOverlay(attached); this._UpdateProgressState(attached); this._UpdateProgressValue(attached); this._UpdateTooltip(attached); this._UpdateThumbnailClipping(attached); this._UpdateThumbButtons(attached); if (!attached) { this._hwndSource = null; } } private void _DetachWindow() { this._window.SourceInitialized -= this._OnWindowSourceInitialized; this._isAttached = false; this._OnIsAttachedChanged(false); this._window = null; } private HRESULT _UpdateOverlay(bool attached) { ImageSource overlay = this.Overlay; if (overlay == null || !attached) { return this._taskbarList.SetOverlayIcon(this._hwndSource.Handle, IntPtr.Zero, null); } IntPtr hIcon = IntPtr.Zero; HRESULT result; try { hIcon = this._GetHICONFromImageSource(overlay, this._overlaySize); result = this._taskbarList.SetOverlayIcon(this._hwndSource.Handle, hIcon, null); } finally { Utility.SafeDestroyIcon(ref hIcon); } return result; } private HRESULT _UpdateTooltip(bool attached) { string pszTip = this.Description ?? ""; if (!attached) { pszTip = ""; } return this._taskbarList.SetThumbnailTooltip(this._hwndSource.Handle, pszTip); } private HRESULT _UpdateProgressValue(bool attached) { if (!attached || this.ProgressState == TaskbarItemProgressState.None || this.ProgressState == TaskbarItemProgressState.Indeterminate) { return HRESULT.S_OK; } ulong ullCompleted = (ulong) (this.ProgressValue * 1000.0); return this._taskbarList.SetProgressValue(this._hwndSource.Handle, ullCompleted, 1000UL); } private HRESULT _UpdateProgressState(bool attached) { TaskbarItemProgressState progressState = this.ProgressState; TBPF tbpFlags = TBPF.NOPROGRESS; if (attached) { tbpFlags = progressState switch { TaskbarItemProgressState.None => TBPF.NOPROGRESS, TaskbarItemProgressState.Indeterminate => TBPF.INDETERMINATE, TaskbarItemProgressState.Normal => TBPF.NORMAL, TaskbarItemProgressState.Error => TBPF.ERROR, TaskbarItemProgressState.Paused => TBPF.PAUSED, _ => TBPF.NOPROGRESS }; } HRESULT result = this._taskbarList.SetProgressState(this._hwndSource.Handle, tbpFlags); if (result.Succeeded) { result = this._UpdateProgressValue(attached); } return result; } private HRESULT _UpdateThumbnailClipping(bool attached) { RefRECT prcClip = null; if (attached && this.ThumbnailClipMargin != TaskbarItemInfo._EmptyThickness) { Thickness thumbnailClipMargin = this.ThumbnailClipMargin; RECT clientRect = NativeMethods.GetClientRect(this._hwndSource.Handle); Rect rect = DpiHelper.DeviceRectToLogical(new Rect((double) clientRect.Left, (double) clientRect.Top, (double) clientRect.Width, (double) clientRect.Height)); if (thumbnailClipMargin.Left + thumbnailClipMargin.Right >= rect.Width || thumbnailClipMargin.Top + thumbnailClipMargin.Bottom >= rect.Height) { prcClip = new RefRECT(0, 0, 0, 0); } else { Rect logicalRectangle = new Rect(thumbnailClipMargin.Left, thumbnailClipMargin.Top, rect.Width - thumbnailClipMargin.Left - thumbnailClipMargin.Right, rect.Height - thumbnailClipMargin.Top - thumbnailClipMargin.Bottom); Rect rect2 = DpiHelper.LogicalRectToDevice(logicalRectangle); prcClip = new RefRECT((int) rect2.Left, (int) rect2.Top, (int) rect2.Right, (int) rect2.Bottom); } } return this._taskbarList.SetThumbnailClip(this._hwndSource.Handle, prcClip); } private HRESULT _RegisterThumbButtons() { HRESULT hresult = HRESULT.S_OK; if (!this._haveAddedButtons) { THUMBBUTTON[] array = new THUMBBUTTON[7]; for (int i = 0; i < 7; i++) { array[i] = new THUMBBUTTON { iId = (uint) i, dwFlags = (THBF.DISABLED | THBF.NOBACKGROUND | THBF.HIDDEN), dwMask = (THB.ICON | THB.TOOLTIP | THB.FLAGS) }; } hresult = this._taskbarList.ThumbBarAddButtons(this._hwndSource.Handle, (uint) array.Length, array); if (hresult == HRESULT.E_INVALIDARG) { hresult = HRESULT.S_FALSE; } this._haveAddedButtons = hresult.Succeeded; } return hresult; } private HRESULT _UpdateThumbButtons(bool attached) { THUMBBUTTON[] array = new THUMBBUTTON[7]; HRESULT result = this._RegisterThumbButtons(); if (result.Failed) { return result; } ThumbButtonInfoCollection thumbButtonInfos = this.ThumbButtonInfos; HRESULT result2; try { uint num = 0u; if (!attached || thumbButtonInfos == null) { goto IL_1AE; } using (FreezableCollection.Enumerator enumerator = thumbButtonInfos.GetEnumerator()) { while (enumerator.MoveNext()) { ThumbButtonInfo thumbButtonInfo = enumerator.Current; THUMBBUTTON thumbbutton = new THUMBBUTTON { iId = num, dwMask = (THB.ICON | THB.TOOLTIP | THB.FLAGS) }; switch (thumbButtonInfo.Visibility) { case Visibility.Visible: goto IL_A5; case Visibility.Hidden: thumbbutton.dwFlags = (THBF.DISABLED | THBF.NOBACKGROUND); thumbbutton.hIcon = IntPtr.Zero; break; case Visibility.Collapsed: thumbbutton.dwFlags = THBF.HIDDEN; break; default: goto IL_A5; } IL_146: array[(int) ((UIntPtr) num)] = thumbbutton; num += 1u; if (num != 7u) { continue; } break; IL_A5: thumbbutton.szTip = (thumbButtonInfo.Description ?? ""); thumbbutton.hIcon = this._GetHICONFromImageSource(thumbButtonInfo.ImageSource, this._overlaySize); if (!thumbButtonInfo.IsBackgroundVisible) { thumbbutton.dwFlags |= THBF.NOBACKGROUND; } if (!thumbButtonInfo.IsEnabled) { thumbbutton.dwFlags |= THBF.DISABLED; } else { thumbbutton.dwFlags = thumbbutton.dwFlags; } if (!thumbButtonInfo.IsInteractive) { thumbbutton.dwFlags |= THBF.NONINTERACTIVE; } if (thumbButtonInfo.DismissWhenClicked) { thumbbutton.dwFlags |= THBF.DISMISSONCLICK; goto IL_146; } goto IL_146; } goto IL_1AE; } IL_179: array[(int) ((UIntPtr) num)] = new THUMBBUTTON { iId = num, dwFlags = (THBF.DISABLED | THBF.NOBACKGROUND | THBF.HIDDEN), dwMask = (THB.ICON | THB.TOOLTIP | THB.FLAGS) }; num += 1u; IL_1AE: if (num < 7u) { goto IL_179; } result2 = this._taskbarList.ThumbBarUpdateButtons(this._hwndSource.Handle, (uint) array.Length, array); } finally { foreach (THUMBBUTTON thumbbutton2 in array) { IntPtr hIcon = thumbbutton2.hIcon; if (IntPtr.Zero != hIcon) { Utility.SafeDestroyIcon(ref hIcon); } } } return result2; } private const int c_MaximumThumbButtons = 7; private static readonly WM WM_TASKBARBUTTONCREATED = NativeMethods.RegisterWindowMessage("TaskbarButtonCreated"); private static readonly Thickness _EmptyThickness = default(Thickness); private SafeGdiplusStartupToken _gdipToken; private bool _haveAddedButtons; private Window _window; private HwndSource _hwndSource; private ITaskbarList3 _taskbarList; private readonly Size _overlaySize; private bool _isAttached; public static readonly DependencyProperty TaskbarItemInfoProperty = DependencyProperty.RegisterAttached("TaskbarItemInfo", typeof(TaskbarItemInfo), typeof(TaskbarItemInfo), new PropertyMetadata(null, new PropertyChangedCallback(TaskbarItemInfo._OnTaskbarItemInfoChanged), new CoerceValueCallback(TaskbarItemInfo._CoerceTaskbarItemInfoValue))); public static readonly DependencyProperty ProgressStateProperty = DependencyProperty.Register("ProgressState", typeof(TaskbarItemProgressState), typeof(TaskbarItemInfo), new PropertyMetadata(TaskbarItemProgressState.None, delegate (DependencyObject d, DependencyPropertyChangedEventArgs e) { ((TaskbarItemInfo) d)._OnProgressStateChanged(); }, (DependencyObject d, object e) => TaskbarItemInfo._CoerceProgressState((TaskbarItemProgressState) e))); public static readonly DependencyProperty ProgressValueProperty = DependencyProperty.Register("ProgressValue", typeof(double), typeof(TaskbarItemInfo), new PropertyMetadata(0.0, delegate (DependencyObject d, DependencyPropertyChangedEventArgs e) { ((TaskbarItemInfo) d)._OnProgressValueChanged(); }, (DependencyObject d, object e) => TaskbarItemInfo._CoerceProgressValue((double) e))); public static readonly DependencyProperty OverlayProperty = DependencyProperty.Register("Overlay", typeof(ImageSource), typeof(TaskbarItemInfo), new PropertyMetadata(null, delegate (DependencyObject d, DependencyPropertyChangedEventArgs e) { ((TaskbarItemInfo) d)._OnOverlayChanged(); })); public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(TaskbarItemInfo), new PropertyMetadata(string.Empty, delegate (DependencyObject d, DependencyPropertyChangedEventArgs e) { ((TaskbarItemInfo) d)._OnDescriptionChanged(); })); public static readonly DependencyProperty ThumbnailClipMarginProperty = DependencyProperty.Register("ThumbnailClipMargin", typeof(Thickness), typeof(TaskbarItemInfo), new PropertyMetadata(default(Thickness), delegate (DependencyObject d, DependencyPropertyChangedEventArgs e) { ((TaskbarItemInfo) d)._OnThumbnailClipMarginChanged(); }, (DependencyObject d, object e) => TaskbarItemInfo._CoerceThumbnailClipMargin((Thickness) e))); [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")] public static readonly DependencyProperty ThumbButtonInfosProperty = DependencyProperty.Register("ThumbButtonInfos", typeof(ThumbButtonInfoCollection), typeof(TaskbarItemInfo), new PropertyMetadata(null, delegate (DependencyObject d, DependencyPropertyChangedEventArgs e) { ((TaskbarItemInfo) d)._OnThumbButtonsChanged(); })); }