diff --git a/InkCanvasForClass/InkCanvasForClass.csproj b/InkCanvasForClass/InkCanvasForClass.csproj index ff222db..ec48f94 100644 --- a/InkCanvasForClass/InkCanvasForClass.csproj +++ b/InkCanvasForClass/InkCanvasForClass.csproj @@ -160,6 +160,7 @@ + diff --git a/InkCanvasForClass/InkCanvasForClass.csproj.user b/InkCanvasForClass/InkCanvasForClass.csproj.user index 7bdca1a..0e5f0de 100644 --- a/InkCanvasForClass/InkCanvasForClass.csproj.user +++ b/InkCanvasForClass/InkCanvasForClass.csproj.user @@ -17,6 +17,12 @@ Code + + Code + + + Code + @@ -37,6 +43,12 @@ Designer + + Designer + + + Designer + Designer diff --git a/InkCanvasForClass/MainWindow_cs/MW_FloatingBarIcons.cs b/InkCanvasForClass/MainWindow_cs/MW_FloatingBarIcons.cs index de03149..6c4c09f 100644 --- a/InkCanvasForClass/MainWindow_cs/MW_FloatingBarIcons.cs +++ b/InkCanvasForClass/MainWindow_cs/MW_FloatingBarIcons.cs @@ -23,6 +23,7 @@ using System.Text; using System.Globalization; using System.Windows.Data; using System.Xml.Linq; +using Ink_Canvas.Popups; using Image = System.Windows.Controls.Image; namespace Ink_Canvas { @@ -1026,11 +1027,11 @@ namespace Ink_Canvas { private async void SymbolIconScreenshot_MouseUp(object sender, MouseButtonEventArgs e) { HideSubPanelsImmediately(); await Task.Delay(50); - SaveScreenShotToDesktop(); + //SaveScreenShotToDesktop(); + var scrwin = new ScreenshotWindow(this); + scrwin.Show(); } - - private void ImageCountdownTimer_MouseUp(object sender, MouseButtonEventArgs e) { LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed; RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed; diff --git a/InkCanvasForClass/MainWindow_cs/MW_Screenshot.cs b/InkCanvasForClass/MainWindow_cs/MW_Screenshot.cs index ccc4be5..af35d08 100644 --- a/InkCanvasForClass/MainWindow_cs/MW_Screenshot.cs +++ b/InkCanvasForClass/MainWindow_cs/MW_Screenshot.cs @@ -1,8 +1,17 @@ using System; +using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; +using System.Runtime.InteropServices; +using System.Text; using System.Windows; +using System.Windows.Interop; +using System.Windows.Media; +using OSVersionExtension; +using Vanara.PInvoke; +using OperatingSystem = OSVersionExtension.OperatingSystem; +using PixelFormat = System.Drawing.Imaging.PixelFormat; namespace Ink_Canvas { @@ -33,8 +42,209 @@ namespace Ink_Canvas } } - private void SaveScreenShotToDesktop() + #region MagnificationAPI 獲取屏幕截圖並過濾ICC窗口 + + public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong) { + if (IntPtr.Size == 8) + return SetWindowLongPtr64(hWnd, nIndex, dwNewLong); + else + return new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32())); + } + + [DllImport("user32.dll", EntryPoint="SetWindowLong")] + private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll", EntryPoint="SetWindowLongPtr")] + private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong); + + [DllImport("user32.dll", EntryPoint="GetWindowLong")] + static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); + + [DllImport("user32.dll", SetLastError=true)] + private static extern IntPtr CreateWindowEx( + uint dwExStyle, + string lpClassName, + string lpWindowName, + uint dwStyle, + int x, + int y, + int nWidth, + int nHeight, + IntPtr hWndParent, + IntPtr hMenu, + IntPtr hInstance, + IntPtr lpParam); + + public void SaveScreenshotToDesktopByMagnificationAPI(HWND[] excludedHwnds, Action callbackAction) { + + if (OSVersion.GetOperatingSystem() < OperatingSystem.Windows8 && OSVersion.GetOperatingSystem() > OperatingSystem.Windows10) return; + if (!Magnification.MagInitialize()) return; + + // 創建宿主窗體 + var mainWinMag = new Window(); + mainWinMag.WindowState = WindowState.Maximized; + mainWinMag.WindowStyle = WindowStyle.None; + mainWinMag.ResizeMode = ResizeMode.NoResize; + mainWinMag.Background = new SolidColorBrush(Colors.Transparent); + mainWinMag.AllowsTransparency = true; + mainWinMag.Show(); + var handle = new WindowInteropHelper(mainWinMag).Handle; + SetWindowPos(handle, new IntPtr(1), 0, 0, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, + System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height, 0x0080); // SWP_HIDEWINDOW + SetWindowLongPtr(handle, -20, new IntPtr((int)GetWindowLongPtr(handle, -20) | 0x00080000)); + SetLayeredWindowAttributes(handle,0, 255, (byte)0x2); + + // 創建放大鏡窗體(使用Win32方法) + var hwndMag = CreateWindowEx(0,"Magnifier", "ICCMagnifierWindow", (uint)(0x40000000L | 0x10000000L), 0, 0, + System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, + System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height, handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + + // 過濾窗口 + var hwnds = new List { new HWND(hwndMag) }; + hwnds.AddRange(excludedHwnds); + if (!Magnification.MagSetWindowFilterList(new HWND(hwndMag), + Magnification.MW_FILTERMODE.MW_FILTERMODE_EXCLUDE, hwnds.Count, hwnds.ToArray())) return; + + // 保存數據 + if (!Magnification.MagSetImageScalingCallback(new HWND(hwndMag), + (hwnd, srcdata, srcheader, destdata, destheader, unclipped, clipped, dirty) => { + Bitmap bm = new Bitmap((int)srcheader.width, (int)srcheader.height, (int)srcheader.width * 4, + PixelFormat.Format32bppRgb, srcdata); + callbackAction(bm); + return true; + })) return; + + // 設置窗口Source + if (!Magnification.MagSetWindowSource(new HWND(hwndMag), + new RECT(0, 0, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, + System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height))) return; + + // 關閉宿主窗體 + mainWinMag.Close(); + } + + #endregion + + #region 窗口截图 + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] + static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); + public static IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex) + { + if (IntPtr.Size > 4) + return GetClassLongPtr64(hWnd, nIndex); + else + return new IntPtr(GetClassLongPtr32(hWnd, nIndex)); + } + [DllImport("user32.dll", EntryPoint = "GetClassLong")] + public static extern uint GetClassLongPtr32(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll", EntryPoint = "GetClassLongPtr")] + public static extern IntPtr GetClassLongPtr64(IntPtr hWnd, int nIndex); + [DllImport("user32.dll", EntryPoint = "EnumDesktopWindows", + ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool EnumDesktopWindows(IntPtr hDesktop, Delegate lpEnumCallbackFunction, IntPtr lParam); + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsWindowVisible(IntPtr hWnd); + [DllImport("user32.dll", EntryPoint = "GetWindowText", + ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)] + public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount); + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags); + [DllImport("user32.dll")] + private static extern bool GetWindowRect(IntPtr handle, out RECT rect); + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern int GetWindowTextLength(IntPtr hWnd); + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); + + public Icon GetAppIcon(IntPtr hwnd) { + IntPtr iconHandle = SendMessage(hwnd,0x7F,2,0); + if(iconHandle == IntPtr.Zero) + iconHandle = SendMessage(hwnd,0x7F,0,0); + if(iconHandle == IntPtr.Zero) + iconHandle = SendMessage(hwnd,0x7F,1,0); + if (iconHandle == IntPtr.Zero) + iconHandle = GetClassLongPtr(hwnd, -14); + if (iconHandle == IntPtr.Zero) + iconHandle = GetClassLongPtr(hwnd, -34); + if(iconHandle == IntPtr.Zero) + return null; + Icon icn = System.Drawing.Icon.FromHandle(iconHandle); + return icn; + } + + public struct WindowInformation { + public string Title; + public Bitmap WindowBitmap; + public Icon AppIcon; + public bool IsVisible; + public int Width; + public int Height; + public RECT Rect; + public WINDOWPLACEMENT Placement; + } + + public struct WINDOWPLACEMENT { + public int length; + public int flags; + public int showCmd; + public System.Drawing.Point ptMinPosition; + public System.Drawing.Point ptMaxPosition; + public System.Drawing.Rectangle rcNormalPosition; + } + + public delegate bool EnumDesktopWindowsDelegate(IntPtr hWnd, int lParam); + + public WindowInformation[] GetAllWindows() { + var windows = new List(); + if (!EnumDesktopWindows(IntPtr.Zero, new EnumDesktopWindowsDelegate((hwnd, param) => { + var isvisible = IsWindowVisible(hwnd); + if (!isvisible) return true; + var icon = GetAppIcon(hwnd); + var length = GetWindowTextLength(hwnd) + 1; + var title = new StringBuilder(length); + GetWindowText(hwnd, title, length); + if (title.ToString().Length == 0) return true; + RECT rect; + WINDOWPLACEMENT placement = new WINDOWPLACEMENT(); + GetWindowPlacement(hwnd, ref placement); + if (placement.showCmd == 2) return true; + GetWindowRect(hwnd, out rect); + var w = rect.Width; + var h = rect.Height; + if (w == 0 || h == 0) return true; + Bitmap bmp = new Bitmap(rect.Width, rect.Height); + Graphics memoryGraphics = Graphics.FromImage(bmp); + IntPtr hdc = memoryGraphics.GetHdc(); + PrintWindow(hwnd, hdc, 2); + windows.Add(new WindowInformation() { + AppIcon = icon, + Title = title.ToString(), + IsVisible = isvisible, + WindowBitmap = bmp, + Width = w, + Height = h, + Rect = rect, + Placement = placement, + }); + memoryGraphics.ReleaseHdc(hdc); + return true; + }), + IntPtr.Zero)) return new WindowInformation[]{}; + return windows.ToArray(); + } + + #endregion + + private void SaveScreenShotToDesktop() { var bitmap = GetScreenshotBitmap(); string savePath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); bitmap.Save(savePath + @"\" + DateTime.Now.ToString("u").Replace(':', '-') + ".png", ImageFormat.Png); diff --git a/InkCanvasForClass/Popups/ScreenshotWindow.xaml b/InkCanvasForClass/Popups/ScreenshotWindow.xaml new file mode 100644 index 0000000..d89193d --- /dev/null +++ b/InkCanvasForClass/Popups/ScreenshotWindow.xaml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/InkCanvasForClass/Popups/ScreenshotWindow.xaml.cs b/InkCanvasForClass/Popups/ScreenshotWindow.xaml.cs new file mode 100644 index 0000000..d954b4a --- /dev/null +++ b/InkCanvasForClass/Popups/ScreenshotWindow.xaml.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing.Imaging; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using System.Xml.Linq; +using Vanara.PInvoke; + +namespace Ink_Canvas.Popups +{ + /// + /// ScreenshotWindow.xaml 的交互逻辑 + /// + public partial class ScreenshotWindow : Window { + + private MainWindow mainWindow; + + public ScreenshotWindow(MainWindow mainWin) { + InitializeComponent(); + + mainWindow = mainWin; + + iconList = new Border[] { + FullScreenIcon, + WindowIcon, + SelectionIcon + }; + iconGeometryList = new GeometryDrawing[] { + FullScreenIconGeometry, + WindowIconsGeometry, + SelectionIconGeometry, + }; + iconTextList = new TextBlock[] { + FullScreenIconText, + WindowIconText, + SelectionIconText, + }; + + foreach (var b in iconList) { + b.MouseLeave += IconMouseLeave; + b.MouseUp += IconMouseUp; + b.MouseDown += IconMouseDown; + } + + CaptureButton.MouseUp += CaptureButton_MouseUp; + CaptureButton.MouseDown += CaptureButton_MouseDown; + CaptureButton.MouseLeave += CaptureButton_MouseLeave; + + UpdateModeIconSelection(); + ReArrangeWindowPosition(); + mainWin.Hide(); + } + + private Border lastDownIcon; + private int selectedMode = 0; + + private void ReArrangeWindowPosition() { + var workAreaWidth = SystemParameters.WorkArea.Width; + var workAreaHeight = SystemParameters.WorkArea.Height; + var toolbarHeight = SystemParameters.PrimaryScreenHeight - SystemParameters.FullPrimaryScreenHeight - + SystemParameters.WindowCaptionHeight; + Left = (workAreaWidth - Width) / 2; + Top = workAreaHeight - Height - toolbarHeight - 64; + } + + private void UpdateModeIconSelection() { + foreach (var b in iconList) b.Background = new SolidColorBrush(Colors.Transparent); + foreach (var g in iconGeometryList) g.Brush = new SolidColorBrush(Color.FromRgb(24, 24, 27)); + foreach (var t in iconTextList) t.Foreground = new SolidColorBrush(Color.FromRgb(24, 24, 27)); + iconList[selectedMode].Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); + iconGeometryList[selectedMode].Brush = new SolidColorBrush(Colors.White); + iconTextList[selectedMode].Foreground = new SolidColorBrush(Colors.White); + } + + private bool isCaptureButtonDown = false; + + private void CaptureButton_MouseDown(object sender, MouseButtonEventArgs e) { + if (isCaptureButtonDown) return; + + isCaptureButtonDown = true; + var sb = new Storyboard(); + var animation = new DoubleAnimation { + From = 1, + To = 0.9, + Duration = TimeSpan.FromMilliseconds(200) + }; + var animation2 = new DoubleAnimation { + From = 1, + To = 0.9, + Duration = TimeSpan.FromMilliseconds(200) + }; + var animation3 = new ThicknessAnimation() { + From = new Thickness(5), + To = new Thickness(7), + Duration = TimeSpan.FromMilliseconds(200) + }; + Storyboard.SetTargetProperty(animation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)")); + Storyboard.SetTargetProperty(animation2, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)")); + Storyboard.SetTargetProperty(animation3, new PropertyPath(Border.BorderThicknessProperty)); + animation.EasingFunction = new CubicEase(); + animation2.EasingFunction = new CubicEase(); + animation3.EasingFunction = new CubicEase(); + sb.Children.Add(animation); + sb.Children.Add(animation2); + sb.Children.Add(animation3); + sb.Begin(CaptureButton); + } + + private void CaptureButton_MouseUp(object sender, MouseButtonEventArgs e) { + if (isCaptureButtonDown != true) return; + CaptureButton_MouseLeave(sender, null); + + if (selectedMode == 0) CaptureFullScreen(); + } + + private void CaptureFullScreen() { + mainWindow.SaveScreenshotToDesktopByMagnificationAPI(new HWND[] { + new WindowInteropHelper(mainWindow).Handle, + new WindowInteropHelper(this).Handle, + }, async bitmap => { + string savePath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); + bitmap.Save(savePath + @"\" + DateTime.Now.ToString("u").Replace(':', '-') + ".bmp", ImageFormat.Bmp); + mainWindow.ShowNewToast("已保存截图到桌面!",MW_Toast.ToastType.Success,3000); + await Task.Delay(50); + Close(); + }); + } + + private void CaptureButton_MouseLeave(object sender, MouseEventArgs e) { + if (isCaptureButtonDown != true) return; + + var sb = new Storyboard(); + var animation = new DoubleAnimation { + From = 0.9, + To = 1, + Duration = TimeSpan.FromMilliseconds(200) + }; + var animation2 = new DoubleAnimation { + From = 0.9, + To = 1, + Duration = TimeSpan.FromMilliseconds(200) + }; + var animation3 = new ThicknessAnimation() { + From = new Thickness(7), + To = new Thickness(5), + Duration = TimeSpan.FromMilliseconds(200) + }; + Storyboard.SetTargetProperty(animation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)")); + Storyboard.SetTargetProperty(animation2, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)")); + Storyboard.SetTargetProperty(animation3, new PropertyPath(Border.BorderThicknessProperty)); + animation.EasingFunction = new CubicEase(); + animation2.EasingFunction = new CubicEase(); + animation3.EasingFunction = new CubicEase(); + sb.Children.Add(animation); + sb.Children.Add(animation2); + sb.Children.Add(animation3); + sb.Begin(CaptureButton); + + isCaptureButtonDown = false; + } + + private void IconMouseLeave(object sender, MouseEventArgs e) { + if (lastDownIcon == null) return; + lastDownIcon = null; + var b = (Border)sender; + if (Array.IndexOf(iconList,b)!=selectedMode) + b.Background = new SolidColorBrush(Colors.Transparent); + } + + private void IconMouseDown(object sender, MouseButtonEventArgs e) { + if (lastDownIcon != null) return; + lastDownIcon = (Border)sender; + var b = (Border)sender; + if (Array.IndexOf(iconList,b)!=selectedMode) + b.Background = new SolidColorBrush(Color.FromArgb(22, 24, 24, 27)); + } + + private WindowScreenshotGridWindow _screenshotGridWindow = null; + + private void IconMouseUp(object sender, MouseButtonEventArgs e) { + if (lastDownIcon == null) return; + IconMouseLeave(sender, null); + var index = Array.IndexOf(iconList, (Border)sender); + selectedMode = index; + UpdateModeIconSelection(); + + if (selectedMode == 1) { + _screenshotGridWindow = new WindowScreenshotGridWindow(mainWindow.GetAllWindows()); + _screenshotGridWindow.Show(); + } else if (_screenshotGridWindow != null) { + _screenshotGridWindow.Close(); + _screenshotGridWindow = null; + } + } + + private Border[] iconList = new Border[] { }; + private GeometryDrawing[] iconGeometryList = new GeometryDrawing[] { }; + private TextBlock[] iconTextList = new TextBlock[] { }; + + private void CloseButton_CloseWindow(object sender, MouseButtonEventArgs e) { + Close(); + } + + protected override void OnClosed(EventArgs e) { + mainWindow.Show(); + base.OnClosed(e); + } + } +} diff --git a/InkCanvasForClass/Popups/WindowScreenshotGridWindow.xaml b/InkCanvasForClass/Popups/WindowScreenshotGridWindow.xaml new file mode 100644 index 0000000..c42377b --- /dev/null +++ b/InkCanvasForClass/Popups/WindowScreenshotGridWindow.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/InkCanvasForClass/Popups/WindowScreenshotGridWindow.xaml.cs b/InkCanvasForClass/Popups/WindowScreenshotGridWindow.xaml.cs new file mode 100644 index 0000000..5fc075b --- /dev/null +++ b/InkCanvasForClass/Popups/WindowScreenshotGridWindow.xaml.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Ink_Canvas.Popups +{ + /// + /// WindowScreenshotGridWindow.xaml 的交互逻辑 + /// + public partial class WindowScreenshotGridWindow : Window + { + + private BitmapImage BitmapToImageSource(Bitmap bitmap) + { + using (MemoryStream memory = new MemoryStream()) + { + bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp); + memory.Position = 0; + BitmapImage bitmapimage = new BitmapImage(); + bitmapimage.BeginInit(); + bitmapimage.StreamSource = memory; + bitmapimage.CacheOption = BitmapCacheOption.OnLoad; + bitmapimage.EndInit(); + + return bitmapimage; + } + } + + private class Win { + public string Title { get; set; } + public BitmapImage Bitmap { get; set; } + } + + private ObservableCollection _windows = new ObservableCollection(); + + public WindowScreenshotGridWindow(MainWindow.WindowInformation[] wins) { + InitializeComponent(); + _windows.Clear(); + WindowsItemControl.ItemsSource = _windows; + foreach (var windowInformation in wins) { + _windows.Add(new Win() { + Title = windowInformation.Title, + Bitmap = BitmapToImageSource(windowInformation.WindowBitmap) + }); + Trace.WriteLine(windowInformation.Title); + } + } + } +}