using System.Runtime.InteropServices.WindowsRuntime; using System.Security.Cryptography; using System.Text.Json; using LogiBoardToolBox.Views; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media.Imaging; using Windows.Graphics.Imaging; using Windows.Storage.Streams; namespace LogiBoardToolBox.Utilities; internal static partial class UiVerification { /// Exports the real imported workspace without injecting input or editing device settings. public static async Task RunImportReviewAsync(MainPage page) { var index = Array.IndexOf(args, "--import-review"); if (index < 0) return; if (index + 1 >= args.Length) throw new ArgumentException("需要导入核对输出目录。"); var folder = Path.GetFullPath(args[index + 1]); Directory.CreateDirectory(folder); var vm = page.ViewModel; var side = vm.IsSideView; GetWindowThreadProcessId(GetForegroundWindow(), out var foregroundBefore); var images = new List(); try { vm.IsSideView = false; await Capture("assignments-front"); vm.IsSideView = true; await Capture("assignments-side"); } finally { vm.IsSideView = side; } GetWindowThreadProcessId(GetForegroundWindow(), out var foregroundAfter); await File.WriteAllTextAsync(Path.Combine(folder, "workspace.json"), JsonSerializer.Serialize(new { ProcessId = Environment.ProcessId, BuildSha256 = Convert.ToHexString(SHA256.HashData(await File.ReadAllBytesAsync(typeof(UiVerification).Assembly.Location))), vm.HasError, vm.Status, vm.ModeSummary, Configuration = vm.SelectedConfiguration?.Label, ForegroundBefore = foregroundBefore, ForegroundAfter = foregroundAfter, Images = images, Bindings = vm.AllBindings.Where(b => b.SelectedAction.Id == "macro").Select(b => new { b.Index, b.GShift, b.Label, b.Value, b.DisplayAction, Original = Convert.ToHexString(b.Original) }), Palette = vm.AssignmentItems.Select(i => new { i.Name, i.AssignedTo, i.Description }), Macros = vm.Macros.Select(m => new { m.Name, m.Script, Repeat = m.Repeat.ToString(), m.UseStandardDelay, m.StandardDelay, m.HoldScript, m.ReleaseScript }), HostMacroEngine = vm.HostMacrosRunning, SoftwareLighting = vm.LightsyncRunning }, new JsonSerializerOptions { WriteIndented = true })); async Task Capture(string name) { var root = (FrameworkElement)App.MainWindow.Content; foreach (var element in Tree(root).OfType()) { element.InvalidateMeasure(); element.InvalidateArrange(); } root.UpdateLayout(); await Task.Delay(500); var bitmap = new RenderTargetBitmap(); await bitmap.RenderAsync(root); var buffer = await bitmap.GetPixelsAsync(); if (bitmap.PixelWidth < 500 || bitmap.PixelHeight < 400 || buffer.Length == 0) throw new InvalidOperationException("导入核对截图尺寸无效。"); using var stream = new InMemoryRandomAccessStream(); var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream); encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)bitmap.PixelWidth, (uint)bitmap.PixelHeight, 96, 96, buffer.ToArray()); await encoder.FlushAsync(); using var reader = new DataReader(stream.GetInputStreamAt(0)); await reader.LoadAsync((uint)stream.Size); var bytes = new byte[(int)stream.Size]; reader.ReadBytes(bytes); var path = Path.Combine(folder, name + ".png"); await File.WriteAllBytesAsync(path, bytes); GetWindowThreadProcessId(GetForegroundWindow(), out var foreground); images.Add(new { Path = path, Sha256 = Convert.ToHexString(SHA256.HashData(bytes)), Width = bitmap.PixelWidth, Height = bitmap.PixelHeight, SideView = vm.IsSideView, ForegroundProcess = foreground }); } } }