mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-04-03 11:05:16 +08:00
Add capture test
This commit is contained in:
@@ -21,6 +21,10 @@ GetWindowThreadProcessId
|
||||
mouse_event
|
||||
PostMessage
|
||||
ReleaseDC
|
||||
EnumWindows
|
||||
GetWindowText
|
||||
IsWindowVisible
|
||||
GetWindowTextLength
|
||||
|
||||
// COM & WinRT
|
||||
D3D11CreateDevice
|
||||
|
||||
@@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterGenshinImpact.Win32",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fischless.WindowCapture", "Fischless.WindowCapture\Fischless.WindowCapture.csproj", "{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vision.WindowCapture.Test", "Vision.WindowCapture.Test\Vision.WindowCapture.Test.csproj", "{D35CB953-C666-4E57-9A9A-3AAE5BF78402}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -51,6 +53,14 @@ Global
|
||||
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Release|Any CPU.Build.0 = Release|x64
|
||||
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Release|x64.ActiveCfg = Release|x64
|
||||
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Release|x64.Build.0 = Release|x64
|
||||
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
9
Vision.WindowCapture.Test/App.xaml
Normal file
9
Vision.WindowCapture.Test/App.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<Application x:Class="Vision.WindowCapture.Test.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Vision.WindowCapture.Test"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
17
Vision.WindowCapture.Test/App.xaml.cs
Normal file
17
Vision.WindowCapture.Test/App.xaml.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace Vision.WindowCapture.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
||||
10
Vision.WindowCapture.Test/AssemblyInfo.cs
Normal file
10
Vision.WindowCapture.Test/AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
12
Vision.WindowCapture.Test/CaptureTestWindow.xaml
Normal file
12
Vision.WindowCapture.Test/CaptureTestWindow.xaml
Normal file
@@ -0,0 +1,12 @@
|
||||
<Window x:Class="Vision.WindowCapture.Test.CaptureTestWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="CaptureTestWindow" Height="450" Width="800">
|
||||
<Grid>
|
||||
<Image x:Name="DisplayCaptureResultImage" Height="Auto" Width="Auto" />
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
69
Vision.WindowCapture.Test/CaptureTestWindow.xaml.cs
Normal file
69
Vision.WindowCapture.Test/CaptureTestWindow.xaml.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Windows.Win32.Foundation;
|
||||
|
||||
namespace Vision.WindowCapture.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// CaptureTestWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class CaptureTestWindow : Window
|
||||
{
|
||||
private IWindowCapture? _capture;
|
||||
public CaptureTestWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void StartCapture(IntPtr hWnd, CaptureModeEnum captureMode)
|
||||
{
|
||||
if (hWnd == IntPtr.Zero)
|
||||
{
|
||||
MessageBox.Show("请选择窗口");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_capture = WindowCaptureFactory.Create(captureMode);
|
||||
_capture.Start((HWND)hWnd);
|
||||
|
||||
CompositionTarget.Rendering += Loop;
|
||||
}
|
||||
|
||||
private void Loop(object? sender, EventArgs e)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var bitmap = _capture?.Capture();
|
||||
sw.Stop();
|
||||
Debug.WriteLine("截图耗时:" + sw.ElapsedMilliseconds);
|
||||
|
||||
if (bitmap != null)
|
||||
{
|
||||
sw.Reset();
|
||||
sw.Start();
|
||||
DisplayCaptureResultImage.Source = ToBitmapImage(bitmap);
|
||||
sw.Stop();
|
||||
Debug.WriteLine("转换耗时:" + sw.ElapsedMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
public static BitmapImage ToBitmapImage( Bitmap bitmap)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
|
||||
var image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
image.StreamSource = ms;
|
||||
image.EndInit();
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Vision.WindowCapture.Test/MainWindow.xaml
Normal file
12
Vision.WindowCapture.Test/MainWindow.xaml
Normal file
@@ -0,0 +1,12 @@
|
||||
<Window x:Class="Vision.WindowCapture.Test.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Vision.WindowCapture.Test"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="400" Width="400">
|
||||
<Canvas>
|
||||
<Button x:Name="CaptureBtn" Content="Capture" Canvas.Left="30" Canvas.Top="98" Width="75" Click="CaptureBtn_OnClick"/>
|
||||
</Canvas>
|
||||
</Window>
|
||||
41
Vision.WindowCapture.Test/MainWindow.xaml.cs
Normal file
41
Vision.WindowCapture.Test/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Vision.WindowCapture.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void CaptureBtn_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var picker = new PickerWindow();
|
||||
var hWnd = picker.PickCaptureTarget(new WindowInteropHelper(this).Handle);
|
||||
if (hWnd != IntPtr.Zero)
|
||||
{
|
||||
var captureWindow = new CaptureTestWindow();
|
||||
captureWindow.StartCapture(hWnd, CaptureModeEnum.WindowsGraphicsCapture);
|
||||
captureWindow.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Vision.WindowCapture.Test/PickerWindow.xaml
Normal file
36
Vision.WindowCapture.Test/PickerWindow.xaml
Normal file
@@ -0,0 +1,36 @@
|
||||
<Window x:Class="Vision.WindowCapture.Test.PickerWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Vision.WindowCapture.Test"
|
||||
mc:Ignorable="d"
|
||||
Title="选择捕获窗口" Height="450" Width="800">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Margin="5,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock FontSize="20" Text="双击选中要捕获的窗口" />
|
||||
</StackPanel>
|
||||
|
||||
<ListBox
|
||||
x:Name="WindowList"
|
||||
Grid.Row="1"
|
||||
Padding="0,10"
|
||||
BorderThickness="0">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl MouseDoubleClick="WindowsOnMouseDoubleClick">
|
||||
<TextBlock FontSize="14" Text="{Binding Name}" />
|
||||
</ContentControl>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</Window>
|
||||
107
Vision.WindowCapture.Test/PickerWindow.xaml.cs
Normal file
107
Vision.WindowCapture.Test/PickerWindow.xaml.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using static Windows.Win32.PInvoke;
|
||||
|
||||
namespace Vision.WindowCapture.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// PickerWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class PickerWindow : Window
|
||||
{
|
||||
private readonly string[] _ignoreProcesses = { "applicationframehost", "shellexperiencehost", "systemsettings", "winstore.app", "searchui" };
|
||||
|
||||
public PickerWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
FindWindows();
|
||||
}
|
||||
|
||||
public IntPtr PickCaptureTarget(IntPtr hWnd)
|
||||
{
|
||||
new WindowInteropHelper(this).Owner = hWnd;
|
||||
ShowDialog();
|
||||
|
||||
return ((CapturableWindow?)WindowList.SelectedItem)?.Handle ?? IntPtr.Zero;
|
||||
}
|
||||
|
||||
private unsafe void FindWindows()
|
||||
{
|
||||
var wih = new WindowInteropHelper(this);
|
||||
EnumWindows((hWnd, lParam) =>
|
||||
{
|
||||
// ignore invisible windows
|
||||
if (!IsWindowVisible(hWnd))
|
||||
return true;
|
||||
|
||||
// ignore untitled windows
|
||||
string title;
|
||||
int bufferSize = GetWindowTextLength(hWnd) + 1;
|
||||
fixed (char* windowNameChars = new char[bufferSize])
|
||||
{
|
||||
if (GetWindowText(hWnd, windowNameChars, bufferSize) == 0)
|
||||
{
|
||||
int errorCode = Marshal.GetLastWin32Error();
|
||||
if (errorCode != 0)
|
||||
{
|
||||
throw new Win32Exception(errorCode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
title = new string(windowNameChars);
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ignore me
|
||||
if (wih.Handle == hWnd)
|
||||
return true;
|
||||
|
||||
uint processId;
|
||||
GetWindowThreadProcessId(hWnd, &processId);
|
||||
|
||||
// ignore by process name
|
||||
var process = Process.GetProcessById((int)processId);
|
||||
if (_ignoreProcesses.Contains(process.ProcessName.ToLower()))
|
||||
return true;
|
||||
|
||||
WindowList.Items.Add(new CapturableWindow
|
||||
{
|
||||
Handle = hWnd,
|
||||
Name = $"{title} ({process.ProcessName}.exe)"
|
||||
});
|
||||
|
||||
return true;
|
||||
}, IntPtr.Zero);
|
||||
}
|
||||
|
||||
private void WindowsOnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
public struct CapturableWindow
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public IntPtr Handle { get; set; }
|
||||
}
|
||||
}
|
||||
21
Vision.WindowCapture.Test/Vision.WindowCapture.Test.csproj
Normal file
21
Vision.WindowCapture.Test/Vision.WindowCapture.Test.csproj
Normal file
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0-windows10.0.22000.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Vision.WindowCapture\Vision.WindowCapture.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="CaptureTestWindow.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user