mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-17 09:26:50 +08:00
refactor: use Fischless
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
/// 窗口捕获帧数/触发器触发频率
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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("未到框内中间 左键按下");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
113
Fischless.WindowCapture/BitBlt/BitBltCapture.cs
Normal file
113
Fischless.WindowCapture/BitBlt/BitBltCapture.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
14
Fischless.WindowCapture/BitmapExtensions.cs
Normal file
14
Fischless.WindowCapture/BitmapExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
28
Fischless.WindowCapture/CaptionHelper.cs
Normal file
28
Fischless.WindowCapture/CaptionHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
9
Fischless.WindowCapture/CaptureModeExtensions.cs
Normal file
9
Fischless.WindowCapture/CaptureModeExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
7
Fischless.WindowCapture/CaptureModes.cs
Normal file
7
Fischless.WindowCapture/CaptureModes.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Fischless.WindowCapture;
|
||||
|
||||
public enum CaptureModes
|
||||
{
|
||||
BitBlt,
|
||||
WindowsGraphicsCapture,
|
||||
}
|
||||
27
Fischless.WindowCapture/DpiHelper.cs
Normal file
27
Fischless.WindowCapture/DpiHelper.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
22
Fischless.WindowCapture/Fischless.WindowCapture.csproj
Normal file
22
Fischless.WindowCapture/Fischless.WindowCapture.csproj
Normal 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>
|
||||
152
Fischless.WindowCapture/Graphics/GraphicsCapture.cs
Normal file
152
Fischless.WindowCapture/Graphics/GraphicsCapture.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
49
Fischless.WindowCapture/Graphics/Helpers/CaptureHelper.cs
Normal file
49
Fischless.WindowCapture/Graphics/Helpers/CaptureHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
115
Fischless.WindowCapture/Graphics/Helpers/Direct3D11Helper.cs
Normal file
115
Fischless.WindowCapture/Graphics/Helpers/Direct3D11Helper.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
111
Fischless.WindowCapture/Graphics/Helpers/WinRT.cs
Normal file
111
Fischless.WindowCapture/Graphics/Helpers/WinRT.cs
Normal 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();
|
||||
}
|
||||
14
Fischless.WindowCapture/IWindowCapture.cs
Normal file
14
Fischless.WindowCapture/IWindowCapture.cs
Normal 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();
|
||||
}
|
||||
44
Fischless.WindowCapture/WindowCaptureBenchmark.cs
Normal file
44
Fischless.WindowCapture/WindowCaptureBenchmark.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
19
Fischless.WindowCapture/WindowCaptureFactory.cs
Normal file
19
Fischless.WindowCapture/WindowCaptureFactory.cs
Normal 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),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user