internal static void InvalidatePreview(TaskbarWindow taskbarWindow)
        {
            if (taskbarWindow != null)

                _ = DesktopWindowManager.DwmInvalidateIconicBitmaps(
                    taskbarWindow.WindowToTellTaskbarAbout);
        }
        internal static void AddTabbedThumbnail(TabbedThumbnail preview)
        {
            // Create a TOP-LEVEL proxy window for the user's source window/control
            TaskbarWindow taskbarWindow = preview.WindowHandle == IntPtr.Zero
                ? GetTaskbarWindow(preview.WindowsControl, TaskbarProxyWindowType.TabbedThumbnail)
                : GetTaskbarWindow(preview.WindowHandle, TaskbarProxyWindowType.TabbedThumbnail);

            // get the TaskbarWindow for UIElement/WindowHandle respectfully.

            //create taskbar, or set its TabbedThumbnail
            if (taskbarWindow == null)
            {
                taskbarWindow = new TaskbarWindow(preview);
                _taskbarWindowList.Add(taskbarWindow);
            }

            else if (taskbarWindow.TabbedThumbnail == null)

                taskbarWindow.TabbedThumbnail = preview;

            // Listen for Title changes
            preview.TitleChanged += new EventHandler(ThumbnailPreview_TitleChanged);
            preview.TooltipChanged += new EventHandler(ThumbnailPreview_TooltipChanged);

            // Get/Set properties for proxy window
            IntPtr windowHandle = taskbarWindow.WindowToTellTaskbarAbout;

            // Register this new tab and set it as being active.
            TaskbarList.Instance.RegisterTab(windowHandle, preview.ParentWindowHandle);
            TaskbarList.Instance.SetTabOrder(windowHandle, IntPtr.Zero);
            TaskbarList.Instance.SetTabActive(windowHandle, preview.ParentWindowHandle, 0);

            // We need to make sure we can set these properties even when running with admin 

            var changeFilterResult = new ChangeFilterStruct
            {
                cbSize = (uint)Marshal.SizeOf
#if CS7
                <
#else
                (typeof(
#endif
                ChangeFilterStruct
#if CS7
                >(
#else
                )
#endif
                )
            };

            _ = HandlerNativeMethods.ChangeWindowMessageFilterEx(
                windowHandle,
                WindowMessage.DWMSendIconicThumbnail,
                MessageFilterAction.Allow,
                ref changeFilterResult);

            changeFilterResult = new ChangeFilterStruct
            {
                cbSize = (uint)Marshal.SizeOf
#if CS7
                <
#else
                (typeof(
#endif
                ChangeFilterStruct
#if CS7
                >(
#else
                )
#endif
                )
            };

            _ = HandlerNativeMethods.ChangeWindowMessageFilterEx(
                windowHandle,
                WindowMessage.DWMSendIconicLivePreviewBitmap,
                MessageFilterAction.Allow,
                ref changeFilterResult);

            // BUG: There should be somewhere to disable CustomWindowPreview. I didn't find it.
            DesktopWindowManager.EnableCustomWindowPreview(windowHandle, true);

            // Make sure we use the initial title set by the user
            // Trigger a "fake" title changed event, so the title is set on the taskbar thumbnail.
            // Empty/null title will be ignored.
            ThumbnailPreview_TitleChanged(preview, EventArgs.Empty);
            ThumbnailPreview_TooltipChanged(preview, EventArgs.Empty);

            // Indicate to the preview that we've added it on the taskbar
            preview.AddedToTaskbar = true;
        }
        private static bool DispatchLivePreviewBitmapMessage(ref System.Windows.Forms.Message m, TaskbarWindow taskbarWindow)
        {
            if (m.Msg == (int)WindowMessage.DWMSendIconicLivePreviewBitmap)
            {
                // Try to get the width/height
                int width = (int)(((long)m.LParam) >> 16);
                int height = (int)(((long)m.LParam) & (0xFFFF));

                // Default size for the thumbnail
                var realWindowSize = new System.Drawing.Size(200, 200);

                if (taskbarWindow.TabbedThumbnail.WindowHandle != IntPtr.Zero)

                    _ = HandlerNativeMethods.GetClientSize(taskbarWindow.TabbedThumbnail.WindowHandle, out realWindowSize);

                else if (taskbarWindow.TabbedThumbnail.WindowsControl != null)

                    realWindowSize = new System.Drawing.Size(
                        Convert.ToInt32(taskbarWindow.TabbedThumbnail.WindowsControl.RenderSize.Width),
                        Convert.ToInt32(taskbarWindow.TabbedThumbnail.WindowsControl.RenderSize.Height));

                // If we don't have a valid height/width, use the original window's size
                if (width <= 0)

                    width = realWindowSize.Width;

                if (height <= 0)

                    height = realWindowSize.Height;

                // Fire an event to let the user update their bitmap
                // Raise the event
                taskbarWindow.TabbedThumbnail.OnTabbedThumbnailBitmapRequested();

                // capture the bitmap for the given control
                // If the user has already specified us a bitmap to use, use that.
                IntPtr hBitmap = taskbarWindow.TabbedThumbnail.CurrentHBitmap == IntPtr.Zero ? GrabBitmap(taskbarWindow, realWindowSize) : taskbarWindow.TabbedThumbnail.CurrentHBitmap;

                // If we have a valid parent window handle,
                // calculate the offset so we can place the "peek" bitmap
                // correctly on the app window
                if (taskbarWindow.TabbedThumbnail.ParentWindowHandle != IntPtr.Zero && taskbarWindow.TabbedThumbnail.WindowHandle != IntPtr.Zero)
                {
                    // if we don't have a offset specified already by the user...
                    Point offset = !taskbarWindow.TabbedThumbnail.PeekOffset.HasValue
                        ? Shell.DesktopWindowManager.GetParentOffsetOfChild(taskbarWindow.TabbedThumbnail.WindowHandle, taskbarWindow.TabbedThumbnail.ParentWindowHandle)
                        : new Point(Convert.ToInt32(taskbarWindow.TabbedThumbnail.PeekOffset.Value.X),
                            Convert.ToInt32(taskbarWindow.TabbedThumbnail.PeekOffset.Value.Y));

                    // Only set the peek bitmap if it's not null. 
                    // If it's null (either we didn't get the bitmap or size was 0),
                    // let DWM handle it
                    if (hBitmap != IntPtr.Zero && offset.X >= 0 && offset.Y >= 0)

                        DesktopWindowManager.SetPeekBitmap(
                            taskbarWindow.WindowToTellTaskbarAbout,
                            hBitmap, offset,
                            taskbarWindow.TabbedThumbnail.DisplayFrameAroundBitmap);

                    // If the bitmap we have is not coming from the user (i.e. we created it here),
                    // then make sure we delete it as we don't need it now.
                    if (taskbarWindow.TabbedThumbnail.CurrentHBitmap == IntPtr.Zero)

                        _ = GDI.DeleteObject(hBitmap);

                    return true;
                }

                // Else, we don't have a valid window handle from the user. This is mostly likely because
                // we have a WPF UIElement control. If that's the case, use a different screen capture method
                // and also couple of ways to try to calculate the control's offset w.r.t it's parent.
                else if (taskbarWindow.TabbedThumbnail.ParentWindowHandle != IntPtr.Zero &&
                    taskbarWindow.TabbedThumbnail.WindowsControl != null)
                {
                    System.Windows.Point offset;

                    if (!taskbarWindow.TabbedThumbnail.PeekOffset.HasValue)
                    {
                        // Calculate the offset for a WPF UIElement control
                        // For hidden controls, we can't seem to perform the transform.
                        GeneralTransform objGeneralTransform = taskbarWindow.TabbedThumbnail.WindowsControl.TransformToVisual(taskbarWindow.TabbedThumbnail.WindowsControlParentWindow);
                        offset = objGeneralTransform.Transform(new System.Windows.Point(0, 0));
                    }

                    else

                        offset = new System.Windows.Point(taskbarWindow.TabbedThumbnail.PeekOffset.Value.X, taskbarWindow.TabbedThumbnail.PeekOffset.Value.Y);

                    // Only set the peek bitmap if it's not null. 
                    // If it's null (either we didn't get the bitmap or size was 0),
                    // let DWM handle it
                    if (hBitmap != IntPtr.Zero)

                        if (offset.X >= 0 && offset.Y >= 0)

                            DesktopWindowManager.SetPeekBitmap(
                                taskbarWindow.WindowToTellTaskbarAbout,
                                hBitmap, new Point((int)offset.X, (int)offset.Y),
                                taskbarWindow.TabbedThumbnail.DisplayFrameAroundBitmap);

                        else

                            DesktopWindowManager.SetPeekBitmap(
                                taskbarWindow.WindowToTellTaskbarAbout,
                                hBitmap,
                                taskbarWindow.TabbedThumbnail.DisplayFrameAroundBitmap);

                    // If the bitmap we have is not coming from the user (i.e. we created it here),
                    // then make sure we delete it as we don't need it now.
                    if (taskbarWindow.TabbedThumbnail.CurrentHBitmap == IntPtr.Zero)

                        _ = GDI.DeleteObject(hBitmap);

                    return true;
                }

                else
                {
                    // Else (no parent specified), just set the bitmap. It would take over the entire 
                    // application window (would work only if you are a MDI app)

                    // Only set the peek bitmap if it's not null. 
                    // If it's null (either we didn't get the bitmap or size was 0),
                    // let DWM handle it
                    if (hBitmap != null)

                        DesktopWindowManager.SetPeekBitmap(taskbarWindow.WindowToTellTaskbarAbout, hBitmap, taskbarWindow.TabbedThumbnail.DisplayFrameAroundBitmap);

                    // If the bitmap we have is not coming from the user (i.e. we created it here),
                    // then make sure we delete it as we don't need it now.
                    if (taskbarWindow.TabbedThumbnail.CurrentHBitmap == IntPtr.Zero)

                        _ = GDI.DeleteObject(hBitmap);

                    return true;
                }
            }

            return false;
        }
        private static bool DispatchSendIconThumbnailMessage(ref System.Windows.Forms.Message m, TaskbarWindow taskbarWindow)
        {
            if (m.Msg == (int)WindowMessage.DWMSendIconicThumbnail)
            {
                int width = (int)((long)m.LParam >> 16);
                int height = (int)(((long)m.LParam) & (0xFFFF));
                var requestedSize = new System.Drawing.Size(width, height);

                // Fire an event to let the user update their bitmap
                taskbarWindow.TabbedThumbnail.OnTabbedThumbnailBitmapRequested();

                IntPtr hBitmap;

                // Default size for the thumbnail
                var realWindowSize = new System.Drawing.Size(200, 200);

                // Get the size of teh control or UIElement
                if (taskbarWindow.TabbedThumbnail.WindowHandle != IntPtr.Zero)

                    _ = HandlerNativeMethods.GetClientSize(taskbarWindow.TabbedThumbnail.WindowHandle, out realWindowSize);

                else if (taskbarWindow.TabbedThumbnail.WindowsControl != null)

                    realWindowSize = new System.Drawing.Size(
                        Convert.ToInt32(taskbarWindow.TabbedThumbnail.WindowsControl.RenderSize.Width),
                        Convert.ToInt32(taskbarWindow.TabbedThumbnail.WindowsControl.RenderSize.Height));

                if (realWindowSize.Height == -1 && realWindowSize.Width == -1)

                    realWindowSize.Width = realWindowSize.Height = 199;

                // capture the bitmap for the given control
                // If the user has already specified us a bitmap to use, use that.
                if (taskbarWindow.TabbedThumbnail.ClippingRectangle != null &&
                    taskbarWindow.TabbedThumbnail.ClippingRectangle.Value != Rectangle.Empty)
                {
                    hBitmap = taskbarWindow.TabbedThumbnail.CurrentHBitmap == IntPtr.Zero
                        ? GrabBitmap(taskbarWindow, realWindowSize)
                        : taskbarWindow.TabbedThumbnail.CurrentHBitmap;

                    // Clip the bitmap we just got.
                    Bitmap bmp = Image.FromHbitmap(hBitmap);

                    Rectangle clippingRectangle = taskbarWindow.TabbedThumbnail.ClippingRectangle.Value;

                    // If our clipping rect is out of bounds, update it
                    if (clippingRectangle.Height > requestedSize.Height)

                        clippingRectangle.Height = requestedSize.Height;

                    if (clippingRectangle.Width > requestedSize.Width)

                        clippingRectangle.Width = requestedSize.Width;

                    // NOTE: Is this a memory leak?
                    bmp = bmp.Clone(clippingRectangle, bmp.PixelFormat);

                    // Make sure we dispose the bitmap before assigning, otherwise we'll have a memory leak
                    if (hBitmap != IntPtr.Zero && taskbarWindow.TabbedThumbnail.CurrentHBitmap == IntPtr.Zero)

                        _ = GDI.DeleteObject(hBitmap);

                    hBitmap = bmp.GetHbitmap();
                    bmp.Dispose();
                }

                else
                {
                    // Else, user didn't want any clipping, if they haven't provided us a bitmap,
                    // use the screencapture utility and capture it.

                    hBitmap = taskbarWindow.TabbedThumbnail.CurrentHBitmap;

                    // If no bitmap, capture one using the utility
                    if (hBitmap == IntPtr.Zero)

                        hBitmap = GrabBitmap(taskbarWindow, realWindowSize);
                }

                // Only set the thumbnail if it's not null. 
                // If it's null (either we didn't get the bitmap or size was 0),
                // let DWM handle it
                if (hBitmap != IntPtr.Zero)
                {
                    Bitmap temp = TabbedThumbnailScreenCapture.ResizeImageWithAspect(
                        hBitmap, requestedSize.Width, requestedSize.Height, true);

                    if (taskbarWindow.TabbedThumbnail.CurrentHBitmap == IntPtr.Zero)

                        _ = GDI.DeleteObject(hBitmap);

                    hBitmap = temp.GetHbitmap();
                    DesktopWindowManager.SetIconicThumbnail(taskbarWindow.WindowToTellTaskbarAbout, hBitmap);
                    temp.Dispose();
                }

                // If the bitmap we have is not coming from the user (i.e. we created it here),
                // then make sure we delete it as we don't need it now.
                if (taskbarWindow.TabbedThumbnail.CurrentHBitmap == IntPtr.Zero)

                    _ = GDI.DeleteObject(hBitmap);

                return true;
            }
            return false;
        }