refactor: use Fischless

This commit is contained in:
huiyadanli
2023-10-02 18:00:14 +08:00
parent 45e86a7a34
commit bbd8ed5067
32 changed files with 1033 additions and 253 deletions

View File

@@ -9,24 +9,48 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vision.WindowCapture", "Vis
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterGenshinImpact.Win32", "BetterGenshinImpact.Win32\BetterGenshinImpact.Win32.csproj", "{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fischless.WindowCapture", "Fischless.WindowCapture\Fischless.WindowCapture.csproj", "{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Debug|x64.ActiveCfg = Debug|Any CPU
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Debug|x64.Build.0 = Debug|Any CPU
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Release|Any CPU.Build.0 = Release|Any CPU
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Release|x64.ActiveCfg = Release|Any CPU
{75EC89E2-413D-4725-BCEA-AFAC57708F07}.Release|x64.Build.0 = Release|Any CPU
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Debug|x64.ActiveCfg = Debug|Any CPU
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Debug|x64.Build.0 = Debug|Any CPU
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Release|Any CPU.Build.0 = Release|Any CPU
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Release|x64.ActiveCfg = Release|Any CPU
{C9080C1D-1B26-46CB-A494-A5E7FE3FCEBA}.Release|x64.Build.0 = Release|Any CPU
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Debug|x64.ActiveCfg = Debug|Any CPU
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Debug|x64.Build.0 = Debug|Any CPU
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Release|Any CPU.Build.0 = Release|Any CPU
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Release|x64.ActiveCfg = Release|Any CPU
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Release|x64.Build.0 = Release|Any CPU
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Debug|Any CPU.ActiveCfg = Debug|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Debug|Any CPU.Build.0 = Debug|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Debug|x64.ActiveCfg = Debug|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Debug|x64.Build.0 = Debug|x64
{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}.Release|Any CPU.ActiveCfg = Release|x64
{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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,63 +1,65 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows10.0.22000.0</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows10.0.22621.0</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<UseWindowsForms>True</UseWindowsForms>
<LangVersion>11.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="H.InputSimulator" Version="1.4.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="OpenCvSharp4.Windows" Version="4.8.0.20230708" />
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.46-beta">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageReference Include="MouseKeyHook" Version="5.7.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.RichTextBox.Wpf" Version="1.1.0" />
<PackageReference Include="Vanara.PInvoke.SHCore" Version="3.4.16" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.4.16" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="H.InputSimulator" Version="1.4.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="OpenCvSharp4.Windows" Version="4.8.0.20230708" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.46-beta">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageReference Include="MouseKeyHook" Version="5.7.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.RichTextBox.Wpf" Version="1.1.0" />
<PackageReference Include="Vanara.PInvoke.SHCore" Version="3.4.16" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.4.16" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Vision.WindowCapture\Vision.WindowCapture.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="GameTask\AutoGeniusInvokation\" />
<Folder Include="GameTask\UseActiveCode\" />
<Folder Include="Service\Interface\" />
</ItemGroup>
<ItemGroup>
<Folder Include="GameTask\AutoGeniusInvokation\" />
<Folder Include="GameTask\UseActiveCode\" />
<Folder Include="Service\Interface\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Fischless.WindowCapture\Fischless.WindowCapture.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="View\MaskWindow.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Update="View\MaskWindow.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="GameTask\AutoFishing\Assets\1920x1080\Space.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="GameTask\AutoPick\Assets\1920x1080\F.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="GameTask\AutoSkip\Assets\1920x1080\menu.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="GameTask\AutoSkip\Assets\1920x1080\option.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="GameTask\AutoSkip\Assets\1920x1080\stop_auto.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Update="GameTask\AutoFishing\Assets\1920x1080\Space.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="GameTask\AutoPick\Assets\1920x1080\F.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="GameTask\AutoSkip\Assets\1920x1080\menu.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="GameTask\AutoSkip\Assets\1920x1080\option.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="GameTask\AutoSkip\Assets\1920x1080\stop_auto.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
</Project>

View File

@@ -1,7 +1,7 @@
using BetterGenshinImpact.GameTask.AutoFishing;
using BetterGenshinImpact.GameTask.AutoSkip;
using System;
using Vision.WindowCapture;
using Fischless.WindowCapture;
namespace BetterGenshinImpact.Core.Config
{
@@ -14,7 +14,7 @@ namespace BetterGenshinImpact.Core.Config
/// <summary>
/// 窗口捕获的方式
/// </summary>
public string CaptureMode { get; set; } = CaptureModeEnum.BitBlt.ToString();
public string CaptureMode { get; set; } = CaptureModes.BitBlt.ToString();
/// <summary>
/// 窗口捕获帧数/触发器触发频率

View File

@@ -1,8 +1,7 @@
using System.Threading;
using BetterGenshinImpact.Helpers;
using System.Threading;
using System.Windows;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using BetterGenshinImpact.Helpers;
using static Windows.Win32.PInvoke;
using Vanara.PInvoke;
namespace BetterGenshinImpact.Core.Simulator;
@@ -10,22 +9,19 @@ public class MouseEventSimulator
{
public static void Move(int x, int y)
{
mouse_event(
MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE | MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE,
x * 65535 / PrimaryScreen.DESKTOP.Width,
y * 65535 / PrimaryScreen.DESKTOP.Height,
0,
0);
User32.mouse_event(User32.MOUSEEVENTF.MOUSEEVENTF_ABSOLUTE | User32.MOUSEEVENTF.MOUSEEVENTF_MOVE,
x * 65535 / PrimaryScreen.DESKTOP.Width, y * 65535 / PrimaryScreen.DESKTOP.Height,
0, 0);
}
public static void LeftButtonDown()
{
mouse_event(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
User32.mouse_event(User32.MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
}
public static void LeftButtonUp()
{
mouse_event(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
User32.mouse_event(User32.MOUSEEVENTF.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
public static bool Click(int x, int y)

View File

@@ -1,16 +1,20 @@
using System.Threading;
using Windows.Win32.Foundation;
using static Windows.Win32.PInvoke;
using System;
using System.Threading;
using Vanara.PInvoke;
namespace BetterGenshinImpact.Core.Simulator;
public class PostMessageSimulator
{
private readonly HWND _hWnd;
public static readonly uint WM_LBUTTONDOWN = 0x201; //按下鼠标左键
public PostMessageSimulator(HWND hWnd)
public static readonly uint WM_LBUTTONUP = 0x202; //释放鼠标左键
private readonly IntPtr _hWnd;
public PostMessageSimulator(IntPtr hWnd)
{
_hWnd = hWnd;
this._hWnd = hWnd;
}
/// <summary>
@@ -20,10 +24,10 @@ public class PostMessageSimulator
/// <param name="y"></param>
public void LeftButtonClick(int x, int y)
{
LPARAM p = y << 16 | x;
PostMessage(_hWnd, WM_LBUTTONDOWN, default, p);
IntPtr p = (y << 16) | x;
User32.PostMessage(_hWnd, WM_LBUTTONDOWN, IntPtr.Zero, p);
Thread.Sleep(100);
PostMessage(_hWnd, WM_LBUTTONUP, default, p);
User32.PostMessage(_hWnd, WM_LBUTTONUP, IntPtr.Zero, p);
}
/// <summary>
@@ -31,7 +35,7 @@ public class PostMessageSimulator
/// </summary>
public void LeftButtonDown()
{
PostMessage(_hWnd, WM_LBUTTONDOWN, default, default);
User32.PostMessage(_hWnd, WM_LBUTTONDOWN, IntPtr.Zero);
}
/// <summary>
@@ -39,6 +43,6 @@ public class PostMessageSimulator
/// </summary>
public void LeftButtonUp()
{
PostMessage(_hWnd, WM_LBUTTONUP, default, default);
User32.PostMessage(_hWnd, WM_LBUTTONUP, IntPtr.Zero);
}
}

View File

@@ -1,10 +1,10 @@
using Windows.Win32.Foundation;
using System;
namespace BetterGenshinImpact.Core.Simulator;
public class Simulator
{
public static PostMessageSimulator PostMessage(HWND hWnd)
public static PostMessageSimulator PostMessage(IntPtr hWnd)
{
return new PostMessageSimulator(hWnd);
}

View File

@@ -1,7 +1,6 @@
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.AutoFishing.Assets;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.View;
@@ -12,9 +11,8 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using WindowsInput;
using static Vanara.PInvoke.User32;
namespace BetterGenshinImpact.GameTask.AutoFishing
{
@@ -285,7 +283,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
private int _noRectsCount = 0;
private Rect _cur, _left, _right;
private MOUSE_EVENT_FLAGS _prevMouseEvent = 0x0;
private MOUSEEVENTF _prevMouseEvent = 0x0;
private bool _findFishBoxTips;
@@ -331,21 +329,21 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
if (_cur.X < _left.X)
{
if (_prevMouseEvent != MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN)
if (_prevMouseEvent != MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
{
simulator.Mouse.LeftButtonDown();
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonDown();
_prevMouseEvent = MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN;
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN;
//Debug.WriteLine("进度不到 左键按下");
}
}
else
{
if (_prevMouseEvent == MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN)
if (_prevMouseEvent == MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
{
simulator.Mouse.LeftButtonUp();
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonUp();
_prevMouseEvent = MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP;
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTUP;
//Debug.WriteLine("进度超出 左键松开");
}
}
@@ -360,21 +358,21 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
if (_right.X + _right.Width - (_cur.X + _cur.Width) <= _cur.X - _left.X)
{
if (_prevMouseEvent == MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN)
if (_prevMouseEvent == MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
{
simulator.Mouse.LeftButtonUp();
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonUp();
_prevMouseEvent = MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP;
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTUP;
//Debug.WriteLine("进入框内中间 左键松开");
}
}
else
{
if (_prevMouseEvent != MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN)
if (_prevMouseEvent != MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
{
simulator.Mouse.LeftButtonDown();
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonDown();
_prevMouseEvent = MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN;
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN;
//Debug.WriteLine("未到框内中间 左键按下");
}
}

View File

@@ -1,114 +1,109 @@
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.GameTask.AutoSkip.Assets;
using BetterGenshinImpact.Helpers.Extensions;
using BetterGenshinImpact.View.Drawable;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using BetterGenshinImpact.GameTask.Model;
using WindowsInput;
using Point = OpenCvSharp.Point;
namespace BetterGenshinImpact.GameTask.AutoSkip
namespace BetterGenshinImpact.GameTask.AutoSkip;
/// <summary>
/// 自动剧情有选项点击
/// </summary>
public class AutoSkipTrigger : ITaskTrigger
{
/// <summary>
/// 自动剧情有选项点击
/// </summary>
public class AutoSkipTrigger : ITaskTrigger
private readonly ILogger<AutoSkipTrigger> _logger = App.GetLogger<AutoSkipTrigger>();
public string Name => "自动剧情";
public bool IsEnabled { get; set; }
public int Priority => 20;
public bool IsExclusive => false;
private readonly AutoSkipAssets _autoSkipAssets;
public AutoSkipTrigger()
{
private readonly ILogger<AutoSkipTrigger> _logger = App.GetLogger<AutoSkipTrigger>();
_autoSkipAssets = new AutoSkipAssets();
}
public string Name => "自动剧情";
public bool IsEnabled { get; set; }
public int Priority => 20;
public bool IsExclusive => false;
public void Init()
{
IsEnabled = true;
}
private readonly AutoSkipAssets _autoSkipAssets;
public AutoSkipTrigger()
public void OnCapture(CaptureContent content)
{
if (content.IsReachInterval(TimeSpan.FromMilliseconds(200)))
{
_autoSkipAssets = new AutoSkipAssets();
return;
}
public void Init()
// 找左上角剧情自动的按钮
content.CaptureRectArea.Find(_autoSkipAssets.StopAutoButtonRo, (_) =>
{
IsEnabled = true;
}
new InputSimulator().Keyboard.KeyPress(VirtualKeyCode.SPACE);
});
public void OnCapture(CaptureContent content)
// 不存在则找右下的选项按钮
content.CaptureRectArea.Find(_autoSkipAssets.OptionButtonRo, (optionButtonRectArea) =>
{
if (content.IsReachInterval(TimeSpan.FromMilliseconds(200)))
// 不存在菜单的情况下 剧情在播放中
var menuRectArea = content.CaptureRectArea.Find(_autoSkipAssets.MenuRo);
if (menuRectArea.IsEmpty())
{
return;
optionButtonRectArea.ClickCenter();
_logger.LogInformation("点击选项按钮");
}
});
// 找左上角剧情自动的按钮
content.CaptureRectArea.Find(_autoSkipAssets.StopAutoButtonRo, (_) =>
{
new InputSimulator().Keyboard.KeyPress(VirtualKeyCode.SPACE);
});
// 不存在则找右下的选项按钮
content.CaptureRectArea.Find(_autoSkipAssets.OptionButtonRo, (optionButtonRectArea) =>
{
// 不存在菜单的情况下 剧情在播放中
var menuRectArea = content.CaptureRectArea.Find(_autoSkipAssets.MenuRo);
if (menuRectArea.IsEmpty())
{
optionButtonRectArea.ClickCenter();
_logger.LogInformation("点击选项按钮");
}
});
// 黑屏剧情要点击鼠标(多次) 几乎全黑的时候不用点击
var grayMat = content.CaptureRectArea.SrcGreyMat;
var blackCount = OpenCvCommonHelper.CountGrayMatColor(content.SrcGreyMat, 0);
var rate = blackCount * 1.0 / (grayMat.Width * grayMat.Height);
if (rate > 0.7 && rate < 0.99)
{
new InputSimulator().Mouse.LeftButtonClick();
Debug.WriteLine($"点击黑屏剧情:{rate}");
return;
}
// TODO 自动交付材料
//var grayMat = content.SrcGreyMat;
//// 找左上角剧情自动的按钮
//var grayLeftTopMat = CutHelper.CutLeftTop(grayMat, grayMat.Width / 5, grayMat.Height / 5);
//var p1 = OldMatchTemplateHelper.FindSingleTarget(grayLeftTopMat, AutoSkipAssets.StopAutoButtonMat, 0.9);
//if (p1 is { X: > 0, Y: > 0 })
//{
// //_logger.LogInformation($"找到剧情自动按钮:{p1}");
// VisionContext.Instance().DrawContent.PutRect("StopAutoButton",
// p1.CenterPointToRect(AutoSkipAssets.StopAutoButtonMat).ToRectDrawable());
// new InputSimulator().Keyboard.KeyPress(VirtualKeyCode.SPACE);
//}
//else
//{
// VisionContext.Instance().DrawContent.RemoveRect("StopAutoButton");
//}
//// 不存在则找右下的选项按钮
//var grayRightBottomMat = content.SrcGreyRightBottomMat;
//var p2 = OldMatchTemplateHelper.FindSingleTarget(grayRightBottomMat, AutoSkipAssets.OptionMat);
//if (p2 is { X: > 0, Y: > 0 })
//{
// // 不存在菜单的情况下 剧情在播放中
// var grayLeftTopMat2 = CutHelper.CutLeftTop(grayMat, grayMat.Width / 4, grayMat.Height / 4);
// var pMenu = OldMatchTemplateHelper.FindSingleTarget(grayLeftTopMat2, AutoSkipAssets.MenuMat);
// if (pMenu is { X: 0, Y: 0 })
// {
// p2 = p2.ToDesktopPositionOffset65535(grayMat.Width - grayMat.Width / 2,
// grayMat.Height - grayMat.Height / 3 * 2);
// new InputSimulator().Mouse.MoveMouseTo(p2.X, p2.Y).LeftButtonClick();
// _logger.LogInformation($"点击选项按钮:{p2}");
// return;
// }
//}
// 黑屏剧情要点击鼠标(多次) 几乎全黑的时候不用点击
var grayMat = content.CaptureRectArea.SrcGreyMat;
var blackCount = OpenCvCommonHelper.CountGrayMatColor(content.SrcGreyMat, 0);
var rate = blackCount * 1.0 / (grayMat.Width * grayMat.Height);
if (rate > 0.7 && rate < 0.99)
{
new InputSimulator().Mouse.LeftButtonClick();
Debug.WriteLine($"点击黑屏剧情:{rate}");
return;
}
// TODO 自动交付材料
//var grayMat = content.SrcGreyMat;
//// 找左上角剧情自动的按钮
//var grayLeftTopMat = CutHelper.CutLeftTop(grayMat, grayMat.Width / 5, grayMat.Height / 5);
//var p1 = OldMatchTemplateHelper.FindSingleTarget(grayLeftTopMat, AutoSkipAssets.StopAutoButtonMat, 0.9);
//if (p1 is { X: > 0, Y: > 0 })
//{
// //_logger.LogInformation($"找到剧情自动按钮:{p1}");
// VisionContext.Instance().DrawContent.PutRect("StopAutoButton",
// p1.CenterPointToRect(AutoSkipAssets.StopAutoButtonMat).ToRectDrawable());
// new InputSimulator().Keyboard.KeyPress(VirtualKeyCode.SPACE);
//}
//else
//{
// VisionContext.Instance().DrawContent.RemoveRect("StopAutoButton");
//}
//// 不存在则找右下的选项按钮
//var grayRightBottomMat = content.SrcGreyRightBottomMat;
//var p2 = OldMatchTemplateHelper.FindSingleTarget(grayRightBottomMat, AutoSkipAssets.OptionMat);
//if (p2 is { X: > 0, Y: > 0 })
//{
// // 不存在菜单的情况下 剧情在播放中
// var grayLeftTopMat2 = CutHelper.CutLeftTop(grayMat, grayMat.Width / 4, grayMat.Height / 4);
// var pMenu = OldMatchTemplateHelper.FindSingleTarget(grayLeftTopMat2, AutoSkipAssets.MenuMat);
// if (pMenu is { X: 0, Y: 0 })
// {
// p2 = p2.ToDesktopPositionOffset65535(grayMat.Width - grayMat.Width / 2,
// grayMat.Height - grayMat.Height / 3 * 2);
// new InputSimulator().Mouse.MoveMouseTo(p2.X, p2.Y).LeftButtonClick();
// _logger.LogInformation($"点击选项按钮:{p2}");
// return;
// }
//}
}
}

View File

@@ -17,6 +17,8 @@ public class CaptureContent
public int FrameIndex { get; }
public double TimerInterval { get; }
public int FrameRate => (int)(1000 / TimerInterval);
public RectArea CaptureRectArea { get; private set; }
public CaptureContent(Bitmap srcBitmap, int frameIndex, double interval)

View File

@@ -1,13 +1,7 @@
using BetterGenshinImpact.Helpers;
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Win32.Foundation;
using OpenCvSharp;
using Vanara.PInvoke;
using Size = System.Drawing.Size;
namespace BetterGenshinImpact.GameTask.Model

View File

@@ -1,8 +1,7 @@
using System;
using System.Diagnostics;
using System.Linq;
using Windows.Win32.Foundation;
using static Windows.Win32.PInvoke;
using Vanara.PInvoke;
namespace BetterGenshinImpact.GameTask
{
@@ -21,8 +20,8 @@ namespace BetterGenshinImpact.GameTask
public static bool IsGenshinImpactActive2()
{
var hWnd = GetForegroundWindow();
return (nint)hWnd == TaskContext.Instance().GameHandle;
var hWnd = User32.GetForegroundWindow();
return hWnd == TaskContext.Instance().GameHandle;
}
public static nint FindHandleByProcessName(params string[] names)
@@ -39,29 +38,26 @@ namespace BetterGenshinImpact.GameTask
return 0;
}
public static unsafe string? GetActiveProcessName()
public static string? GetActiveProcessName()
{
try
{
var hWnd = GetForegroundWindow();
uint pid = default;
_ = GetWindowThreadProcessId(hWnd, &pid);
var hWnd = User32.GetForegroundWindow();
_ = User32.GetWindowThreadProcessId(hWnd, out var pid);
var p = Process.GetProcessById((int)pid);
return p.ProcessName;
}
catch (Exception ex)
catch
{
Debug.WriteLine(ex);
return null;
}
}
public static unsafe Process GetProcessByHandle(nint hWnd)
public static Process? GetProcessByHandle(IntPtr hWnd)
{
try
{
uint pid = default;
_ = GetWindowThreadProcessId((HWND)hWnd, &pid);
_ = User32.GetWindowThreadProcessId(hWnd, out var pid);
var p = Process.GetProcessById((int)pid);
return p;
}
@@ -79,7 +75,7 @@ namespace BetterGenshinImpact.GameTask
/// <returns></returns>
public static RECT GetWindowRect(IntPtr hWnd)
{
Windows.Win32.PInvoke.GetWindowRect((HWND)hWnd, out var windowRect);
User32.GetWindowRect(hWnd, out var windowRect);
return windowRect;
}
@@ -90,7 +86,7 @@ namespace BetterGenshinImpact.GameTask
/// <returns></returns>
public static RECT GetGameScreenRect(IntPtr hWnd)
{
GetClientRect((HWND)hWnd, out var clientRect);
User32.GetClientRect(hWnd, out var clientRect);
return clientRect;
}

View File

@@ -1,13 +1,11 @@
using Microsoft.Extensions.Logging;
using Fischless.WindowCapture;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows;
using Windows.Win32.Foundation;
using BetterGenshinImpact.GameTask.Model;
using Vision.WindowCapture;
using Vanara.PInvoke;
namespace BetterGenshinImpact.GameTask
{
@@ -30,7 +28,7 @@ namespace BetterGenshinImpact.GameTask
//_timer.Tick += Tick;
}
public void Start(IntPtr hWnd, CaptureModeEnum mode, int interval = 50)
public void Start(IntPtr hWnd, CaptureModes mode, int interval = 50)
{
// 初始化任务上下文
TaskContext.Instance().Init(hWnd);
@@ -39,7 +37,7 @@ namespace BetterGenshinImpact.GameTask
// 初始化截图器
_capture = WindowCaptureFactory.Create(mode);
_capture.Start((HWND)hWnd);
_capture.Start(hWnd);
// 启动定时器
_frameIndex = 0;

View File

@@ -1,8 +1,6 @@
using System;
using System.Windows;
using System.Windows.Interop;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using Vanara.PInvoke;
using HMONITOR = Vanara.PInvoke.HMONITOR;
using HWND = Vanara.PInvoke.HWND;

View File

@@ -1,8 +1,6 @@
using BetterGenshinImpact.GameTask;
using OpenCvSharp;
using System;
using Windows.Win32.Foundation;
using BetterGenshinImpact.Core.Simulator;
namespace BetterGenshinImpact.Helpers.Extensions
{
@@ -33,7 +31,7 @@ namespace BetterGenshinImpact.Helpers.Extensions
return point;
}
var rc = SystemControl.GetWindowRect((HWND)TaskContext.Instance().GameHandle);
var rc = SystemControl.GetWindowRect(TaskContext.Instance().GameHandle);
return new Point(rc.X + point.X + offsetX, rc.Y + point.Y + offsetY);
}

View File

@@ -1,6 +1,7 @@
using System.Drawing;
using Windows.Win32.Graphics.Gdi;
using static Windows.Win32.PInvoke;
using System;
using System.Drawing;
using static Vanara.PInvoke.Gdi32;
using Vanara.PInvoke;
namespace BetterGenshinImpact.Helpers
{
@@ -13,13 +14,13 @@ namespace BetterGenshinImpact.Helpers
{
get
{
var hdc = GetDC(default);
var hdc = User32.GetDC(IntPtr.Zero);
var size = new Size
{
Width = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.HORZRES),
Height = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.VERTRES)
Width = Gdi32.GetDeviceCaps(hdc, DeviceCap.HORZRES),
Height = Gdi32.GetDeviceCaps(hdc, DeviceCap.VERTRES)
};
ReleaseDC(default, hdc);
User32.ReleaseDC(IntPtr.Zero, hdc);
return size;
}
}
@@ -30,9 +31,9 @@ namespace BetterGenshinImpact.Helpers
{
get
{
var hdc = GetDC(default);
var dpiX = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.LOGPIXELSX);
ReleaseDC(default, hdc);
var hdc = User32.GetDC(IntPtr.Zero);
var dpiX = Gdi32.GetDeviceCaps(hdc, DeviceCap.LOGPIXELSX);
User32.ReleaseDC(IntPtr.Zero, hdc);
return dpiX;
}
}
@@ -43,9 +44,9 @@ namespace BetterGenshinImpact.Helpers
{
get
{
var hdc = GetDC(default);
var dpiX = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.LOGPIXELSY);
ReleaseDC(default, hdc);
var hdc = User32.GetDC(IntPtr.Zero);
var dpiX = Gdi32.GetDeviceCaps(hdc, DeviceCap.LOGPIXELSY);
User32.ReleaseDC(IntPtr.Zero, hdc);
return dpiX;
}
}
@@ -56,13 +57,13 @@ namespace BetterGenshinImpact.Helpers
{
get
{
var hdc = GetDC(default);
var hdc = User32.GetDC(IntPtr.Zero);
var size = new Size
{
Width = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.DESKTOPHORZRES),
Height = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.DESKTOPVERTRES)
Width = Gdi32.GetDeviceCaps(hdc, DeviceCap.DESKTOPHORZRES),
Height = Gdi32.GetDeviceCaps(hdc, DeviceCap.DESKTOPVERTRES)
};
ReleaseDC(default, hdc);
User32.ReleaseDC(IntPtr.Zero, hdc);
return size;
}
}
@@ -74,9 +75,9 @@ namespace BetterGenshinImpact.Helpers
{
get
{
var hdc = GetDC(default);
var scaleX = (float)GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.DESKTOPHORZRES) / GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.HORZRES);
ReleaseDC(default, hdc);
var hdc = User32.GetDC(IntPtr.Zero);
var scaleX = (float)Gdi32.GetDeviceCaps(hdc, DeviceCap.DESKTOPHORZRES) / (float)Gdi32.GetDeviceCaps(hdc, DeviceCap.HORZRES);
User32.ReleaseDC(IntPtr.Zero, hdc);
return scaleX;
}
}
@@ -87,11 +88,12 @@ namespace BetterGenshinImpact.Helpers
{
get
{
var hdc = GetDC(default);
var scaleY = (float)GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.DESKTOPVERTRES) / GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.VERTRES);
ReleaseDC(default, hdc);
var hdc = User32.GetDC(IntPtr.Zero);
var scaleY = (float)Gdi32.GetDeviceCaps(hdc, DeviceCap.DESKTOPVERTRES) / (float)Gdi32.GetDeviceCaps(hdc, DeviceCap.VERTRES);
User32.ReleaseDC(IntPtr.Zero, hdc);
return scaleY;
}
}
}
}

View File

@@ -1,10 +1,10 @@
using BetterGenshinImpact.Helpers.Extensions;
using Fischless.WindowCapture;
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;
using Windows.Win32.Foundation;
using Vision.WindowCapture;
using Vanara.PInvoke;
namespace BetterGenshinImpact.View.Test
{
@@ -19,7 +19,7 @@ namespace BetterGenshinImpact.View.Test
InitializeComponent();
}
public void StartCapture(IntPtr hWnd, CaptureModeEnum captureMode)
public void StartCapture(IntPtr hWnd, CaptureModes captureMode)
{
if (hWnd == IntPtr.Zero)
{
@@ -29,7 +29,7 @@ namespace BetterGenshinImpact.View.Test
_capture = WindowCaptureFactory.Create(captureMode);
_capture.Start((HWND)hWnd);
_capture.Start(hWnd);
CompositionTarget.Rendering += Loop;
}

View File

@@ -1,17 +1,15 @@
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.View;
using BetterGenshinImpact.View.Test;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Fischless.WindowCapture;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Windows;
using Vision.WindowCapture;
using Windows.Win32.Foundation;
using static Windows.Win32.PInvoke;
using Vanara.PInvoke;
namespace BetterGenshinImpact.ViewModel
{
@@ -19,7 +17,7 @@ namespace BetterGenshinImpact.ViewModel
{
[ObservableProperty] private string[] _modeNames = WindowCaptureFactory.ModeNames();
[ObservableProperty] private string? _selectedMode = CaptureModeEnum.BitBlt.ToString();
[ObservableProperty] private string? _selectedMode = CaptureModes.BitBlt.ToString();
private MaskWindow? _maskWindow;
private readonly ILogger<MainWindowViewModel> _logger = App.GetLogger<MainWindowViewModel>();
@@ -98,7 +96,7 @@ namespace BetterGenshinImpact.ViewModel
private void ShowMaskWindow(IntPtr hWnd)
{
GetWindowRect((HWND)hWnd, out var rect);
User32.GetWindowRect(hWnd, out var rect);
//var x = rect.X;
//var y = rect.Y;
//var w = rect.Width;

View File

@@ -0,0 +1,113 @@
using System.Diagnostics;
using Vanara.PInvoke;
namespace Fischless.WindowCapture.BitBlt;
public class BitBltCapture : IWindowCapture
{
private nint _hWnd;
public bool IsCapturing { get; private set; }
public bool IsClientEnabled { get; set; } = false;
public bool IsCursorCaptureEnabled { get; set; } = false;
public void Dispose()
{
Stop();
}
public void Start(nint hWnd)
{
_hWnd = hWnd;
IsCapturing = true;
}
public Bitmap? Capture()
{
if (_hWnd == IntPtr.Zero)
{
return null;
}
try
{
if (IsClientEnabled)
{
_ = User32.GetWindowRect(_hWnd, out var windowRect);
var width = windowRect.right - windowRect.left;
var height = windowRect.bottom - windowRect.top;
var hdcSrc = User32.GetWindowDC(_hWnd);
var hdcDest = Gdi32.CreateCompatibleDC(hdcSrc);
var hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, width, height);
var hOld = Gdi32.SelectObject(hdcDest, hBitmap);
_ = Gdi32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, Gdi32.RasterOperationMode.SRCCOPY);
_ = Gdi32.SelectObject(hdcDest, hOld);
_ = Gdi32.DeleteDC(hdcDest);
_ = User32.ReleaseDC(_hWnd, hdcSrc);
var bitmap = hBitmap.ToBitmap();
_ = Gdi32.DeleteObject(hBitmap);
return bitmap;
}
else
{
_ = User32.GetClientRect(_hWnd, out var windowRect);
int x = default, y = default;
int width = windowRect.right - windowRect.left;
int height = windowRect.bottom - windowRect.top;
Bitmap bitmap = new(width, height);
using System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap);
nint hdcDest = g.GetHdc();
Gdi32.SafeHDC hdcSrc = User32.GetDC(_hWnd == IntPtr.Zero ? User32.GetDesktopWindow() : _hWnd);
_ = Gdi32.StretchBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, width, height, Gdi32.RasterOperationMode.SRCCOPY);
g.ReleaseHdc();
_ = Gdi32.DeleteDC(hdcDest);
_ = Gdi32.DeleteDC(hdcSrc);
return bitmap;
}
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return null!;
}
public Bitmap? Capture(int x, int y, int width, int height)
{
if (_hWnd == IntPtr.Zero)
{
return null;
}
if (IsClientEnabled)
{
throw new NotSupportedException();
}
try
{
Bitmap copied = new(width, height);
using System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(copied);
nint hdcDest = g.GetHdc();
Gdi32.SafeHDC hdcSrc = User32.GetDC(_hWnd == IntPtr.Zero ? User32.GetDesktopWindow() : _hWnd);
_ = Gdi32.StretchBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, width, height, Gdi32.RasterOperationMode.SRCCOPY);
g.ReleaseHdc();
_ = Gdi32.DeleteDC(hdcDest);
_ = Gdi32.DeleteDC(hdcSrc);
return copied;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return null;
}
public void Stop()
{
_hWnd = IntPtr.Zero;
IsCapturing = false;
}
}

View File

@@ -0,0 +1,14 @@
namespace Fischless.WindowCapture;
internal static class BitmapExtensions
{
public static Bitmap Crop(this Bitmap src, int x, int y, int width, int height)
{
Rectangle cropRect = new(x, y, width, height);
Bitmap target = new(cropRect.Width, cropRect.Height);
using System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(target);
g.DrawImage(src, new Rectangle(0, 0, target.Width, target.Height), cropRect, GraphicsUnit.Pixel);
return target;
}
}

View File

@@ -0,0 +1,28 @@
using System.Windows;
using Vanara.PInvoke;
namespace Fischless.WindowCapture;
internal static class CaptionHelper
{
public static bool IsFullScreenMode(nint hWnd)
{
if (hWnd == IntPtr.Zero)
{
return false;
}
int exStyle = User32.GetWindowLong(hWnd, User32.WindowLongFlags.GWL_EXSTYLE);
if ((exStyle & (int)User32.WindowStylesEx.WS_EX_TOPMOST) != 0)
{
return true;
}
return false;
}
public static int GetSystemCaptionHeight()
{
return (int)Math.Round(SystemParameters.CaptionHeight * DpiHelper.ScaleY);
}
}

View File

@@ -0,0 +1,9 @@
namespace Fischless.WindowCapture;
public static class CaptureModeExtensions
{
public static CaptureModes ToCaptureMode(this string modeName)
{
return (CaptureModes)Enum.Parse(typeof(CaptureModes), modeName);
}
}

View File

@@ -0,0 +1,7 @@
namespace Fischless.WindowCapture;
public enum CaptureModes
{
BitBlt,
WindowsGraphicsCapture,
}

View File

@@ -0,0 +1,27 @@
using System.Windows.Interop;
using Vanara.PInvoke;
using Application = System.Windows.Application;
namespace Fischless.WindowCapture;
internal static class DpiHelper
{
public static float ScaleY => GetScaleY();
private static float GetScaleY()
{
if (Environment.OSVersion.Version >= new Version(6, 3)
&& Application.Current?.MainWindow != null)
{
HWND hWnd = new WindowInteropHelper(Application.Current?.MainWindow).Handle;
HMONITOR hMonitor = User32.MonitorFromWindow(hWnd, User32.MonitorFlags.MONITOR_DEFAULTTONEAREST);
_ = SHCore.GetDpiForMonitor(hMonitor, SHCore.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out _, out uint dpiY);
return dpiY / 96f;
}
HDC hdc = User32.GetDC(HWND.NULL);
float scaleY = Gdi32.GetDeviceCaps(hdc, Gdi32.DeviceCap.LOGPIXELSY);
_ = User32.ReleaseDC(HWND.NULL, hdc);
return scaleY / 96f;
}
}

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows10.0.22621.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<UseWindowsForms>True</UseWindowsForms>
<UseWPF>True</UseWPF>
<LangVersion>11.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
<PackageReference Include="Vanara.PInvoke.Gdi32" Version="3.4.16" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.4.16" />
<PackageReference Include="Vanara.PInvoke.SHCore" Version="3.4.16" />
<PackageReference Include="Vanara.Windows.Extensions" Version="3.4.16" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,152 @@
using System.Diagnostics;
using System.Windows;
using Vanara.PInvoke;
using Windows.Graphics.Capture;
using Windows.Graphics.DirectX;
namespace Fischless.WindowCapture.Graphics;
public class GraphicsCapture : IWindowCapture
{
private nint _hWnd;
private Direct3D11CaptureFramePool _captureFramePool = null!;
private GraphicsCaptureItem _captureItem = null!;
private GraphicsCaptureSession _captureSession = null!;
public bool IsCapturing { get; private set; }
public bool IsClientEnabled { get; set; } = false;
public void Dispose()
{
Stop();
}
public void Start(nint hWnd)
{
_hWnd = hWnd;
IsCapturing = true;
_captureItem = CaptureHelper.CreateItemForWindow(_hWnd);
if (_captureItem == null)
{
throw new InvalidOperationException("Failed to create capture item.");
}
_captureItem.Closed += CaptureItemOnClosed;
var device = Direct3D11Helper.CreateDevice();
_captureFramePool = Direct3D11CaptureFramePool.Create(device, DirectXPixelFormat.B8G8R8A8UIntNormalized, 2,
_captureItem.Size);
_captureSession = _captureFramePool.CreateCaptureSession(_captureItem);
_captureSession.IsCursorCaptureEnabled = false;
_captureSession.IsBorderRequired = false;
_captureSession.StartCapture();
IsCapturing = true;
}
public Bitmap? Capture()
{
if (_hWnd == IntPtr.Zero)
{
return null;
}
try
{
using var frame = _captureFramePool?.TryGetNextFrame();
if (frame == null)
{
return null;
}
Bitmap? bitmap = frame.ToBitmap();
if (bitmap == null)
{
return null;
}
if (IsClientEnabled)
{
return bitmap;
}
else
{
_ = User32.GetClientRect(_hWnd, out var windowRect);
int border = Math.Max(SystemParameters.Border, 1);
int captionHeight = CaptionHelper.IsFullScreenMode(_hWnd)
? default
: Math.Max(CaptionHelper.GetSystemCaptionHeight(), frame.ContentSize.Height - windowRect.Height - border * 2);
using (bitmap)
{
return bitmap.Crop(
border,
border + captionHeight,
frame.ContentSize.Width - border * 2,
frame.ContentSize.Height - border * 2 - captionHeight
);
}
}
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return null;
}
public Bitmap? Capture(int x, int y, int width, int height)
{
if (_hWnd == IntPtr.Zero)
{
return null;
}
try
{
using var frame = _captureFramePool?.TryGetNextFrame();
using Bitmap bitmap = frame?.ToBitmap();
_ = User32.GetClientRect(_hWnd, out var windowRect);
int border = Math.Max(SystemParameters.Border, 1);
int captionHeight = CaptionHelper.IsFullScreenMode(_hWnd)
? default
: Math.Max(CaptionHelper.GetSystemCaptionHeight()
, frame.ContentSize.Height - windowRect.Height - border * 2
);
return bitmap.Crop(
border + x,
border + y + captionHeight,
width - border * 2,
height - border * 2 - captionHeight
);
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return null;
}
public void Stop()
{
_captureSession?.Dispose();
_captureFramePool?.Dispose();
_captureSession = null!;
_captureFramePool = null!;
_captureItem = null!;
_hWnd = IntPtr.Zero;
IsCapturing = false;
}
private void CaptureItemOnClosed(GraphicsCaptureItem sender, object args)
{
Stop();
}
}

View File

@@ -0,0 +1,49 @@
using System.Runtime.InteropServices;
using Windows.Graphics.Capture;
using WinRT;
namespace Fischless.WindowCapture.Graphics;
public static class CaptureHelper
{
static readonly Guid GraphicsCaptureItemGuid = new("79C3F95B-31F7-4EC2-A464-632EF5D30760");
[ComImport]
[Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IInitializeWithWindow
{
void Initialize(
nint hWnd);
}
[ComImport]
[Guid("3628E81B-3CAC-4C60-B7F4-23CE0E0C3356")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IGraphicsCaptureItemInterop
{
nint CreateForWindow(
[In] nint window,
[In] ref Guid iid);
nint CreateForMonitor(
[In] nint monitor,
[In] ref Guid iid);
}
public static void SetWindow(this GraphicsCapturePicker picker, nint hWnd)
{
var interop = picker.As<IInitializeWithWindow>();
interop.Initialize(hWnd);
}
public static GraphicsCaptureItem CreateItemForWindow(nint hWnd)
{
var factory = WinrtModule.GetActivationFactory("Windows.Graphics.Capture.GraphicsCaptureItem");
var interop = factory.AsInterface<IGraphicsCaptureItemInterop>();
var itemPointer = interop.CreateForWindow(hWnd, GraphicsCaptureItemGuid);
return GraphicsCaptureItem.FromAbi(itemPointer);
}
}

View File

@@ -0,0 +1,115 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Windows.Graphics.DirectX.Direct3D11;
using WinRT;
namespace Fischless.WindowCapture.Graphics;
[SuppressMessage("CodeQuality", "IDE0052:")]
public static class Direct3D11Helper
{
internal static Guid IInspectable = new("AF86E2E0-B12D-4c6a-9C5A-D7AA65101E90");
internal static Guid ID3D11Resource = new("dc8e63f3-d12b-4952-b47b-5e45026a862d");
internal static Guid IDXGIAdapter3 = new("645967A4-1392-4310-A798-8053CE3E93FD");
internal static Guid ID3D11Device = new("db6f6ddb-ac77-4e88-8253-819df9bbf140");
internal static Guid ID3D11Texture2D = new("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
[ComImport]
[Guid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IDirect3DDxgiInterfaceAccess
{
nint GetInterface([In] ref Guid iid);
};
[DllImport(
"d3d11.dll",
EntryPoint = "CreateDirect3D11DeviceFromDXGIDevice",
SetLastError = true,
CharSet = CharSet.Unicode,
ExactSpelling = true,
CallingConvention = CallingConvention.StdCall
)]
static extern uint CreateDirect3D11DeviceFromDXGIDevice(nint dxgiDevice, out nint graphicsDevice);
[DllImport(
"d3d11.dll",
EntryPoint = "CreateDirect3D11SurfaceFromDXGISurface",
SetLastError = true,
CharSet = CharSet.Unicode,
ExactSpelling = true,
CallingConvention = CallingConvention.StdCall
)]
static extern uint CreateDirect3D11SurfaceFromDXGISurface(nint dxgiSurface, out nint graphicsSurface);
public static IDirect3DDevice CreateDevice()
{
return CreateDevice(false);
}
public static IDirect3DDevice CreateDevice(bool useWARP)
{
var d3dDevice = new SharpDX.Direct3D11.Device(
useWARP ? SharpDX.Direct3D.DriverType.Software : SharpDX.Direct3D.DriverType.Hardware,
SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport);
var device = CreateDirect3DDeviceFromSharpDXDevice(d3dDevice);
return device;
}
public static IDirect3DDevice CreateDirect3DDeviceFromSharpDXDevice(SharpDX.Direct3D11.Device d3dDevice)
{
IDirect3DDevice device = null;
// Acquire the DXGI interface for the Direct3D device.
using (var dxgiDevice = d3dDevice.QueryInterface<SharpDX.DXGI.Device3>())
{
// Wrap the native device using a WinRT interop object.
uint hr = CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.NativePointer, out nint pUnknown);
if (hr == 0)
{
device = MarshalInterface<IDirect3DDevice>.FromAbi(pUnknown);
Marshal.Release(pUnknown);
}
}
return device;
}
public static IDirect3DSurface CreateDirect3DSurfaceFromSharpDXTexture(SharpDX.Direct3D11.Texture2D texture)
{
IDirect3DSurface surface = null;
// Acquire the DXGI interface for the Direct3D surface.
using (var dxgiSurface = texture.QueryInterface<SharpDX.DXGI.Surface>())
{
// Wrap the native device using a WinRT interop object.
uint hr = CreateDirect3D11SurfaceFromDXGISurface(dxgiSurface.NativePointer, out nint pUnknown);
if (hr == 0)
{
surface = Marshal.GetObjectForIUnknown(pUnknown) as IDirect3DSurface;
Marshal.Release(pUnknown);
}
}
return surface;
}
public static SharpDX.Direct3D11.Device CreateSharpDXDevice(IDirect3DDevice device)
{
var access = (IDirect3DDxgiInterfaceAccess)device;
var d3dPointer = access.GetInterface(ID3D11Device);
var d3dDevice = new SharpDX.Direct3D11.Device(d3dPointer);
return d3dDevice;
}
public static SharpDX.Direct3D11.Texture2D CreateSharpDXTexture2D(IDirect3DSurface surface)
{
var access = surface.As<IDirect3DDxgiInterfaceAccess>();
var d3dPointer = access.GetInterface(ID3D11Texture2D);
var d3dSurface = new SharpDX.Direct3D11.Texture2D(d3dPointer);
return d3dSurface;
}
}

View File

@@ -0,0 +1,51 @@
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using System.Drawing.Imaging;
using Windows.Graphics.Capture;
namespace Fischless.WindowCapture.Graphics;
public static class Texture2DExtensions
{
public static Bitmap? ToBitmap(this Direct3D11CaptureFrame frame)
{
var texture2dBitmap = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface);
var d3dDevice = texture2dBitmap.Device;
// Create texture copy
var staging = new Texture2D(d3dDevice, new Texture2DDescription
{
Width = frame.ContentSize.Width,
Height = frame.ContentSize.Height,
MipLevels = 1,
ArraySize = 1,
Format = texture2dBitmap.Description.Format,
Usage = ResourceUsage.Staging,
SampleDescription = new SampleDescription(1, 0),
BindFlags = BindFlags.None,
CpuAccessFlags = CpuAccessFlags.Read,
OptionFlags = ResourceOptionFlags.None
});
try
{
// Copy data
d3dDevice.ImmediateContext.CopyResource(texture2dBitmap, staging);
var dataBox = d3dDevice.ImmediateContext.MapSubresource(staging, 0, 0, MapMode.Read,
SharpDX.Direct3D11.MapFlags.None,
out DataStream stream);
var bitmap = new Bitmap(staging.Description.Width, staging.Description.Height, dataBox.RowPitch,
PixelFormat.Format32bppArgb, dataBox.DataPointer);
return bitmap;
}
finally
{
staging.Dispose();
}
}
}

View File

@@ -0,0 +1,111 @@
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using WinRT;
namespace Fischless.WindowCapture.Graphics;
#pragma warning disable CS0649
[ComImport]
[Guid("3628E81B-3CAC-4C60-B7F4-23CE0E0C3356")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IGraphicsCaptureItemInterop
{
int CreateForWindow([In] nint window, [In] ref Guid iid, out nint result);
int CreateForMonitor([In] nint monitor, [In] ref Guid iid, out nint result);
}
[Guid("00000035-0000-0000-C000-000000000046")]
internal unsafe struct IActivationFactoryVftbl
{
public readonly IInspectable.Vftbl IInspectableVftbl;
private readonly void* _ActivateInstance;
public delegate* unmanaged[Stdcall]<nint, nint*, int> ActivateInstance => (delegate* unmanaged[Stdcall]<nint, nint*, int>)_ActivateInstance;
}
internal class Platform
{
[DllImport("api-ms-win-core-com-l1-1-0.dll")]
internal static extern int CoDecrementMTAUsage(nint cookie);
[DllImport("api-ms-win-core-com-l1-1-0.dll")]
internal static extern unsafe int CoIncrementMTAUsage(nint* cookie);
[DllImport("api-ms-win-core-winrt-l1-1-0.dll")]
internal static extern unsafe int RoGetActivationFactory(nint runtimeClassId, ref Guid iid, nint* factory);
}
/// <summary>
/// https://github.com/zlatanov/windows-screen-recorder
/// </summary>
internal static class WinrtModule
{
private static readonly Dictionary<string, ObjectReference<IActivationFactoryVftbl>> Cache = new();
public static ObjectReference<IActivationFactoryVftbl> GetActivationFactory(string runtimeClassId)
{
lock (Cache)
{
if (Cache.TryGetValue(runtimeClassId, out var factory))
return factory;
var m = MarshalString.CreateMarshaler(runtimeClassId);
try
{
var instancePtr = GetActivationFactory(MarshalString.GetAbi(m));
factory = ObjectReference<IActivationFactoryVftbl>.Attach(ref instancePtr);
Cache.Add(runtimeClassId, factory);
return factory;
}
finally
{
m.Dispose();
}
}
}
private static unsafe nint GetActivationFactory(nint hstrRuntimeClassId)
{
if (s_cookie == IntPtr.Zero)
{
lock (s_lock)
{
if (s_cookie == IntPtr.Zero)
{
nint cookie;
Marshal.ThrowExceptionForHR(Platform.CoIncrementMTAUsage(&cookie));
s_cookie = cookie;
}
}
}
Guid iid = typeof(IActivationFactoryVftbl).GUID;
nint instancePtr;
int hr = Platform.RoGetActivationFactory(hstrRuntimeClassId, ref iid, &instancePtr);
if (hr == 0)
return instancePtr;
throw new Win32Exception(hr);
}
public static bool ResurrectObjectReference(IObjectReference objRef)
{
var disposedField = objRef.GetType().GetField("disposed", BindingFlags.NonPublic | BindingFlags.Instance)!;
if (!(bool)disposedField.GetValue(objRef)!)
return false;
disposedField.SetValue(objRef, false);
GC.ReRegisterForFinalize(objRef);
return true;
}
private static nint s_cookie;
private static readonly object s_lock = new();
}

View File

@@ -0,0 +1,14 @@
namespace Fischless.WindowCapture;
public interface IWindowCapture : IDisposable
{
public bool IsCapturing { get; }
public bool IsClientEnabled { get; set; }
public void Start(nint hWnd);
public Bitmap? Capture();
public Bitmap? Capture(int x, int y, int width, int height);
public void Stop();
}

View File

@@ -0,0 +1,44 @@
using System.Diagnostics;
namespace Fischless.WindowCapture;
public class WindowCaptureBenchmark
{
public static void Action()
{
foreach (CaptureModes mode in Enum.GetValues(typeof(CaptureModes)))
{
_ = Task.Run(async () =>
{
IWindowCapture capture = WindowCaptureFactory.Create(mode);
capture.Start(GetHwnd());
await Task.Delay(1234);
using Bitmap frame = capture.Capture();
frame?.Save($"Benchmark_{mode}_{frame.Width}x{frame.Height}_DPI{DpiHelper.ScaleY * 100f:F0}.jpg");
});
}
}
private static nint GetHwnd()
{
Process[] processes = Process.GetProcessesByName("YuanShen");
if (processes.Length <= 0)
{
processes = Process.GetProcessesByName("Genshin Impact");
}
if (processes.Length <= 0)
{
processes = Process.GetProcessesByName("GenshinImpact");
}
if (processes.Length > 0)
{
foreach (Process? process in processes)
{
return process.MainWindowHandle;
}
}
return IntPtr.Zero;
}
}

View File

@@ -0,0 +1,19 @@
namespace Fischless.WindowCapture;
public class WindowCaptureFactory
{
public static string[] ModeNames()
{
return Enum.GetNames(typeof(CaptureModes));
}
public static IWindowCapture Create(CaptureModes mode)
{
return mode switch
{
CaptureModes.BitBlt => new BitBlt.BitBltCapture(),
CaptureModes.WindowsGraphicsCapture => new Graphics.GraphicsCapture(),
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null),
};
}
}