diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 148129f5..3d918f99 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -15,7 +15,7 @@ - + diff --git a/v2rayN/v2rayN.Desktop/Common/QRCodeAvaloniaUtils.cs b/v2rayN/v2rayN.Desktop/Common/QRCodeAvaloniaUtils.cs new file mode 100644 index 00000000..90f60c7f --- /dev/null +++ b/v2rayN/v2rayN.Desktop/Common/QRCodeAvaloniaUtils.cs @@ -0,0 +1,189 @@ +using System.Runtime.InteropServices; +using SkiaSharp; + +namespace v2rayN.Desktop.Common; + +public partial class QRCodeAvaloniaUtils +{ + public static byte[]? CaptureScreen() + { + if (!Utils.IsWindows()) + { + return null; + } + + try + { + return CaptureScreenWindows(); + } + catch (Exception ex) + { + Logging.SaveLog("CaptureScreen", ex); + return null; + } + } + + private static byte[]? CaptureScreenWindows() + { + var hdcScreen = IntPtr.Zero; + var hdcMemory = IntPtr.Zero; + var hBitmap = IntPtr.Zero; + + try + { + var workArea = new RECT(); + SystemParametersInfo(SPI_GETWORKAREA, 0, ref workArea, 0); + + var left = workArea.Left; + var top = workArea.Top; + var width = workArea.Right - workArea.Left; + var height = workArea.Bottom - workArea.Top; + + if (width <= 0 || height <= 0) + { + left = 0; + top = 0; + width = GetSystemMetrics(0); + height = GetSystemMetrics(1); + } + + hdcScreen = GetDC(IntPtr.Zero); + if (hdcScreen == IntPtr.Zero) + { + return null; + } + + hdcMemory = CreateCompatibleDC(hdcScreen); + hBitmap = CreateCompatibleBitmap(hdcScreen, width, height); + + if (hBitmap == IntPtr.Zero) + { + return null; + } + + SelectObject(hdcMemory, hBitmap); + + const int SRCCOPY = 0x00CC0020; + BitBlt(hdcMemory, 0, 0, width, height, hdcScreen, left, top, SRCCOPY); + + var bmi = new BITMAPINFO + { + biSize = Marshal.SizeOf(typeof(BITMAPINFO)), + biWidth = width, + biHeight = -height, + biPlanes = 1, + biBitCount = 32, + biCompression = 0 + }; + + var imageSize = width * height * 4; + var imageData = new byte[imageSize]; + + var scanLines = GetDIBits(hdcScreen, hBitmap, 0, (uint)height, imageData, ref bmi, 0); + + if (scanLines == 0) + { + return null; + } + + using var bitmap = new SKBitmap(width, height, SKColorType.Bgra8888, SKAlphaType.Premul); + Marshal.Copy(imageData, 0, bitmap.GetPixels(), imageSize); + + using var image = SKImage.FromBitmap(bitmap); + using var encoded = image.Encode(SKEncodedImageFormat.Png, 100); + return encoded.ToArray(); + } + catch (Exception ex) + { + Logging.SaveLog("CaptureScreenWindows", ex); + return null; + } + finally + { + if (hBitmap != IntPtr.Zero) + { + DeleteObject(hBitmap); + } + + if (hdcMemory != IntPtr.Zero) + { + DeleteDC(hdcMemory); + } + + if (hdcScreen != IntPtr.Zero) + { + ReleaseDC(IntPtr.Zero, hdcScreen); + } + } + } + + #region Win32 API + + [LibraryImport("user32.dll")] + private static partial IntPtr GetDC(IntPtr hwnd); + + [LibraryImport("user32.dll")] + private static partial int ReleaseDC(IntPtr hwnd, IntPtr hdc); + + [LibraryImport("gdi32.dll")] + private static partial IntPtr CreateCompatibleDC(IntPtr hdc); + + [LibraryImport("gdi32.dll")] + private static partial IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight); + + [LibraryImport("gdi32.dll")] + private static partial IntPtr SelectObject(IntPtr hdc, IntPtr hObject); + + [LibraryImport("gdi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, + IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop); + + [LibraryImport("gdi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool DeleteObject(IntPtr hObject); + + [LibraryImport("gdi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool DeleteDC(IntPtr hdc); + + [LibraryImport("gdi32.dll")] + private static partial int GetDIBits(IntPtr hdc, IntPtr hbmp, uint uStartScan, uint cScanLines, + byte[] lpvBits, ref BITMAPINFO lpbmi, uint uUsage); + + [LibraryImport("user32.dll")] + private static partial int GetSystemMetrics(int nIndex); + + [LibraryImport("user32.dll", EntryPoint = "SystemParametersInfoW", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool SystemParametersInfo(int uiAction, int uiParam, ref RECT pvParam, int fWinIni); + + private const int SPI_GETWORKAREA = 0x0030; + + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + [StructLayout(LayoutKind.Sequential)] + private struct BITMAPINFO + { + public int biSize; + public int biWidth; + public int biHeight; + public short biPlanes; + public short biBitCount; + public int biCompression; + public int biSizeImage; + public int biXPelsPerMeter; + public int biYPelsPerMeter; + public int biClrUsed; + public int biClrImportant; + } + + #endregion Win32 API +} diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index 996e7c83..b221d75d 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -162,8 +162,8 @@ public partial class MainWindow : WindowBase else { Title = $"{Utils.GetVersion()}"; + menuAddServerViaScan.IsVisible = false; } - menuAddServerViaScan.IsVisible = false; if (_config.UiItem.AutoHideStartup && Utils.IsWindows()) { @@ -336,17 +336,17 @@ public partial class MainWindow : WindowBase public async Task ScanScreenTaskAsync() { - //ShowHideWindow(false); + ShowHideWindow(false); - NoticeManager.Instance.SendMessageAndEnqueue("Not yet implemented.(还未实现)"); - await Task.CompletedTask; - //if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - //{ - // //var bytes = QRCodeHelper.CaptureScreen(desktop); - // //await ViewModel?.ScanScreenResult(bytes); - //} + await Task.Delay(200); - //ShowHideWindow(true); + var bytes = QRCodeAvaloniaUtils.CaptureScreen(); + if (bytes != null && ViewModel != null) + { + await ViewModel.ScanScreenResult(bytes); + } + + ShowHideWindow(true); } private async Task ScanImageTaskAsync() diff --git a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj index 6212ce12..71773766 100644 --- a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj +++ b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj @@ -1,4 +1,4 @@ - + WinExe @@ -6,6 +6,7 @@ true true v2rayN + true @@ -52,4 +53,4 @@ - \ No newline at end of file +