Merge pull request #3 from babalae/user/Lightczx

Win32.Pinvoke instead of Vanara.PInvoke.
Refactor Windows.Graphics.Capture
This commit is contained in:
辉鸭蛋 2023-09-28 17:48:18 +08:00 committed by GitHub
commit a70e25ef97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 931 additions and 1122 deletions

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Remove="NativeMethods.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.46-beta">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,10 @@
{
"$schema": "https://raw.githubusercontent.com/microsoft/CsWin32/main/src/Microsoft.Windows.CsWin32/settings.schema.json",
"allowMarshaling": true,
"useSafeHandles": false,
"emitSingleFile": true,
"public": true,
"comInterop": {
"useIntPtrForComOutPointers": true
}
}

View File

@ -0,0 +1,34 @@
// D3D11
CreateDirect3D11DeviceFromDXGIDevice
CreateDirect3D11SurfaceFromDXGISurface
// GDI32
BitBlt
CreateCompatibleBitmap
CreateCompatibleDC
DeleteDC
GetDeviceCaps
SelectObject
// User32
GetClientRect
GetDC
GetForegroundWindow
GetSystemMetrics
GetWindowDC
GetWindowRect
GetWindowThreadProcessId
mouse_event
PostMessage
ReleaseDC
// COM & WinRT
D3D11CreateDevice
ID3D11Device3
IDirect3DDxgiInterfaceAccess
IGraphicsCaptureItemInterop
// Constants
D3D11_SDK_VERSION
WM_LBUTTONDOWN
WM_LBUTTONUP

View File

@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vision.WindowCapture", "Vis
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vision.Recognition", "Vision.Recognition\Vision.Recognition.csproj", "{97A12A37-613B-4BE3-B01B-BF3411503711}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterGenshinImpact.Win32", "BetterGenshinImpact.Win32\BetterGenshinImpact.Win32.csproj", "{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -27,6 +29,10 @@ Global
{97A12A37-613B-4BE3-B01B-BF3411503711}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97A12A37-613B-4BE3-B01B-BF3411503711}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97A12A37-613B-4BE3-B01B-BF3411503711}.Release|Any CPU.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}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -1,17 +1,11 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Serilog;
using static Vanara.PInvoke.Kernel32;
using System.IO;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using Serilog;
using System;
using System.IO;
using System.Windows;
using Vision.Recognition;
using WinRT;
namespace BetterGenshinImpact
{

View File

@ -10,8 +10,10 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="Config.Net" Version="5.1.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" 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" />

View File

@ -1,29 +1,24 @@
using BetterGenshinImpact.GameTask.AutoSkip.Assets;
using BetterGenshinImpact.GameTask.AutoFishing.Assets;
using BetterGenshinImpact.Utils;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.GameTask.AutoFishing.Assets;
using BetterGenshinImpact.Utils;
using Vision.Recognition;
using Vision.Recognition.Helper.OpenCv;
using Vision.Recognition.Helper.Simulator;
using Vision.Recognition.Task;
using WindowsInput;
using static Vanara.PInvoke.User32;
using Point = OpenCvSharp.Point;
using Serilog.Core;
using Vision.Recognition.Helper.OCR;
using Vision.Recognition.Helper.OpenCv;
using Vision.Recognition.Task;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using WindowsInput;
namespace BetterGenshinImpact.GameTask.AutoFishing
{
public class AutoFishingTrigger : ITaskTrigger
{
private readonly ILogger<AutoFishingTrigger> _logger = App.GetLogger<AutoFishingTrigger>();
private IOcrService _ocrService = OcrFactory.Create(OcrEngineType.WinRT);
private readonly IOcrService _ocrService = OcrFactory.Create(OcrEngineType.WinRT);
public string Name => "自动钓鱼";
public bool IsEnabled { get; set; }
@ -239,7 +234,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
private int _noRectsCount = 0;
private Rect _cur, _left, _right;
private MOUSEEVENTF _prevMouseEvent = 0x0;
private MOUSE_EVENT_FLAGS _prevMouseEvent = 0x0;
private bool _findFishBoxTips;
@ -285,21 +280,21 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
if (_cur.X < _left.X)
{
if (_prevMouseEvent != MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
if (_prevMouseEvent != MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN)
{
simulator.Mouse.LeftButtonDown();
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonDown();
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN;
_prevMouseEvent = MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN;
//Debug.WriteLine("进度不到 左键按下");
}
}
else
{
if (_prevMouseEvent == MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
if (_prevMouseEvent == MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN)
{
simulator.Mouse.LeftButtonUp();
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonUp();
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTUP;
_prevMouseEvent = MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP;
//Debug.WriteLine("进度超出 左键松开");
}
}
@ -314,21 +309,21 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
if (_right.X + _right.Width - (_cur.X + _cur.Width) <= _cur.X - _left.X)
{
if (_prevMouseEvent == MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
if (_prevMouseEvent == MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN)
{
simulator.Mouse.LeftButtonUp();
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonUp();
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTUP;
_prevMouseEvent = MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP;
//Debug.WriteLine("进入框内中间 左键松开");
}
}
else
{
if (_prevMouseEvent != MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
if (_prevMouseEvent != MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN)
{
simulator.Mouse.LeftButtonDown();
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonDown();
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN;
_prevMouseEvent = MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN;
//Debug.WriteLine("未到框内中间 左键按下");
}
}

View File

@ -1,18 +1,14 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
using Vanara.PInvoke;
using Windows.Win32.Foundation;
using static Windows.Win32.PInvoke;
namespace BetterGenshinImpact.GameTask
{
public class SystemControl
{
public static IntPtr FindGenshinImpactHandle()
public static nint FindGenshinImpactHandle()
{
return FindHandleByProcessName("YuanShen", "GenshinImpact", "Genshin Impact Cloud Game");
}
@ -23,7 +19,7 @@ namespace BetterGenshinImpact.GameTask
return name is "YuanShen" or "GenshinImpact" or "Genshin Impact Cloud Game";
}
public static IntPtr FindHandleByProcessName(params string[] names)
public static nint FindHandleByProcessName(params string[] names)
{
foreach (var name in names)
{
@ -34,32 +30,34 @@ namespace BetterGenshinImpact.GameTask
}
}
return IntPtr.Zero;
return 0;
}
public static string? GetActiveProcessName()
public static unsafe string? GetActiveProcessName()
{
try
{
var hWnd = User32.GetForegroundWindow();
User32.GetWindowThreadProcessId(hWnd, out var pid);
var hWnd = GetForegroundWindow();
uint pid = default;
_ = GetWindowThreadProcessId(hWnd, &pid);
var p = Process.GetProcessById((int)pid);
return p.ProcessName;
}
catch
catch (Exception ex)
{
Debug.WriteLine(ex);
return null;
}
}
/// <summary>
/// 获取窗口位置
/// </summary>
/// <param name="hWnd"></param>
/// <returns></returns>
public static RECT GetWindowRect(IntPtr hWnd)
public static RECT GetWindowRect(HWND hWnd)
{
User32.GetWindowRect(hWnd, out var windowRect);
Windows.Win32.PInvoke.GetWindowRect(hWnd, out var windowRect);
return windowRect;
}
@ -68,9 +66,9 @@ namespace BetterGenshinImpact.GameTask
/// </summary>
/// <param name="hWnd"></param>
/// <returns></returns>
public static RECT GetGameScreenRect(IntPtr hWnd)
public static RECT GetGameScreenRect(HWND hWnd)
{
User32.GetClientRect(hWnd, out var clientRect);
Windows.Win32.PInvoke.GetWindowRect(hWnd, out var clientRect);
return clientRect;
}

View File

@ -3,10 +3,12 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Timers;
using System.Threading;
using System.Windows;
using Vision.Recognition.Task;
using Vision.WindowCapture;
using Windows.Win32.Foundation;
namespace BetterGenshinImpact.GameTask
{
@ -34,7 +36,7 @@ namespace BetterGenshinImpact.GameTask
public void Start(CaptureMode mode, int frameRate = 30)
{
IntPtr hWnd = SystemControl.FindGenshinImpactHandle();
HWND hWnd = (HWND)SystemControl.FindGenshinImpactHandle();
if (hWnd == IntPtr.Zero)
{
MessageBox.Show("未找到原神窗口");
@ -46,7 +48,7 @@ namespace BetterGenshinImpact.GameTask
_frameRate = frameRate;
_capture = WindowCaptureFactory.Create(mode);
_capture.Start(hWnd);
_capture.Start((Windows.Win32.Foundation.HWND)hWnd);
_frameIndex = 0;
_timer.Interval = Convert.ToInt32(1000d / frameRate);
@ -117,6 +119,10 @@ namespace BetterGenshinImpact.GameTask
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.StackTrace);
}
finally
{
if (hasLock)

View File

@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask;
using OpenCvSharp;
using System;
using Vision.Recognition.Helper.Simulator;
using Windows.Win32.Foundation;
namespace BetterGenshinImpact.Utils.Extensions
{
@ -18,7 +15,7 @@ namespace BetterGenshinImpact.Utils.Extensions
return point;
}
var rc = SystemControl.GetWindowRect(TaskContext.Instance().GameHandle);
var rc = SystemControl.GetWindowRect((HWND)TaskContext.Instance().GameHandle);
return new Point(rc.X + point.X, rc.Y + point.Y);
}
@ -36,7 +33,7 @@ namespace BetterGenshinImpact.Utils.Extensions
return point;
}
var rc = SystemControl.GetWindowRect(TaskContext.Instance().GameHandle);
var rc = SystemControl.GetWindowRect((HWND)TaskContext.Instance().GameHandle);
return new Point(rc.X + point.X + offsetX, rc.Y + point.Y + offsetY);
}

View File

@ -3,6 +3,7 @@ using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;
using Windows.Win32.Foundation;
using Vision.WindowCapture;
namespace BetterGenshinImpact.View.Test
@ -28,7 +29,7 @@ namespace BetterGenshinImpact.View.Test
_capture = WindowCaptureFactory.Create(captureMode);
_capture.Start(hWnd);
_capture.Start((HWND)hWnd);
CompositionTarget.Rendering += Loop;
}

View File

@ -1,21 +1,17 @@
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.View.Test;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.View.Test;
using OpenCvSharp;
using Vanara.PInvoke;
using Vision.Recognition;
using Vision.Recognition.Helper.Simulator;
using Vision.WindowCapture;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
using static Windows.Win32.PInvoke;
namespace BetterGenshinImpact.ViewModel
{
@ -79,7 +75,8 @@ namespace BetterGenshinImpact.ViewModel
[RelayCommand]
private void OnStopTrigger()
{
if (SelectedMode == null)
HWND hWnd = (HWND)SystemControl.FindGenshinImpactHandle();
if (hWnd == HWND.Null)
{
MessageBox.Show("请选择捕获方式");
return;
@ -96,7 +93,7 @@ namespace BetterGenshinImpact.ViewModel
// return;
// }
// User32.GetWindowRect(hWnd, out var rect);
// GetWindowRect(hWnd, out var rect);
// //var x = rect.X;
// //var y = rect.Y;
// //var w = rect.Width;
@ -126,14 +123,14 @@ namespace BetterGenshinImpact.ViewModel
private void TestMask()
{
var hWnd = SystemControl.FindGenshinImpactHandle();
if (hWnd == IntPtr.Zero)
HWND hWnd = (HWND)SystemControl.FindGenshinImpactHandle();
if (hWnd == HWND.Null)
{
MessageBox.Show("未找到原神窗口");
return;
}
User32.GetWindowRect(hWnd, out var rect);
GetWindowRect(hWnd, out var rect);
var x = rect.X;
var y = rect.Y;
var w = rect.Width;

View File

@ -0,0 +1,19 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace Vision.Recognition.Controls.Adorners;
public class DoubleFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double d = (double)value;
return Math.Round(d);
}
public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return default;
}
}

View File

@ -3,40 +3,43 @@ using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
namespace Vision.Recognition.Controls.Adorners
namespace Vision.Recognition.Controls.Adorners;
public class ResizeRotateAdorner : Adorner
{
public class ResizeRotateAdorner : Adorner
private readonly VisualCollection visuals;
private readonly ResizeRotateChrome chrome;
protected override int VisualChildrenCount
{
private VisualCollection visuals;
private ResizeRotateChrome chrome;
protected override int VisualChildrenCount
get
{
get
{
return this.visuals.Count;
}
}
public ResizeRotateAdorner(ContentControl designerItem)
: base(designerItem)
{
SnapsToDevicePixels = true;
this.chrome = new ResizeRotateChrome();
this.chrome.DataContext = designerItem;
this.visuals = new VisualCollection(this);
this.visuals.Add(this.chrome);
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
this.chrome.Arrange(new Rect(arrangeBounds));
return arrangeBounds;
}
protected override Visual GetVisualChild(int index)
{
return this.visuals[index];
return visuals.Count;
}
}
public ResizeRotateAdorner(ContentControl? designerItem)
: base(designerItem)
{
SnapsToDevicePixels = true;
chrome = new ResizeRotateChrome
{
DataContext = designerItem
};
visuals = new VisualCollection(this)
{
chrome
};
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
chrome.Arrange(new Rect(arrangeBounds));
return arrangeBounds;
}
protected override Visual GetVisualChild(int index)
{
return visuals[index];
}
}

View File

@ -1,13 +1,12 @@
using System.Windows;
using System.Windows.Controls;
namespace Vision.Recognition.Controls.Adorners
namespace Vision.Recognition.Controls.Adorners;
public class ResizeRotateChrome : Control
{
public class ResizeRotateChrome : Control
static ResizeRotateChrome()
{
static ResizeRotateChrome()
{
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(ResizeRotateChrome), new FrameworkPropertyMetadata(typeof(ResizeRotateChrome)));
}
DefaultStyleKeyProperty.OverrideMetadata(typeof(ResizeRotateChrome), new FrameworkPropertyMetadata(typeof(ResizeRotateChrome)));
}
}

View File

@ -3,42 +3,45 @@ using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
namespace Vision.Recognition.Controls.Adorners
namespace Vision.Recognition.Controls.Adorners;
public class SizeAdorner : Adorner
{
public class SizeAdorner : Adorner
private readonly SizeChrome chrome;
private readonly VisualCollection visuals;
private readonly ContentControl designerItem;
protected override int VisualChildrenCount
{
private SizeChrome chrome;
private VisualCollection visuals;
private ContentControl designerItem;
protected override int VisualChildrenCount
get
{
get
{
return this.visuals.Count;
}
}
public SizeAdorner(ContentControl designerItem)
: base(designerItem)
{
this.SnapsToDevicePixels = true;
this.designerItem = designerItem;
this.chrome = new SizeChrome();
this.chrome.DataContext = designerItem;
this.visuals = new VisualCollection(this);
this.visuals.Add(this.chrome);
}
protected override Visual GetVisualChild(int index)
{
return this.visuals[index];
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
this.chrome.Arrange(new Rect(new Point(0.0, 0.0), arrangeBounds));
return arrangeBounds;
return visuals.Count;
}
}
public SizeAdorner(ContentControl designerItem)
: base(designerItem)
{
SnapsToDevicePixels = true;
this.designerItem = designerItem;
chrome = new SizeChrome
{
DataContext = designerItem
};
visuals = new VisualCollection(this)
{
chrome
};
}
protected override Visual GetVisualChild(int index)
{
return visuals[index];
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
chrome.Arrange(new Rect(default, arrangeBounds));
return arrangeBounds;
}
}

View File

@ -1,30 +1,12 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace Vision.Recognition.Controls.Adorners
namespace Vision.Recognition.Controls.Adorners;
public class SizeChrome : Control
{
public class SizeChrome : Control
static SizeChrome()
{
static SizeChrome()
{
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(SizeChrome), new FrameworkPropertyMetadata(typeof(SizeChrome)));
}
DefaultStyleKeyProperty.OverrideMetadata(typeof(SizeChrome), new FrameworkPropertyMetadata(typeof(SizeChrome)));
}
public class DoubleFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double d = (double)value;
return Math.Round(d);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
}

View File

@ -1,12 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Vision.Recognition.Controls;
namespace Vision.Recognition.Controls
public class AreaSettingsControlBuilder
{
public class AreaSettingsControlBuilder
{
}
}

View File

@ -1,98 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows;
using System.Windows.Media;
using Vision.Recognition.Controls.Adorners;
namespace Vision.Recognition.Controls
namespace Vision.Recognition.Controls;
public class DesignerItemDecorator : Control
{
public class DesignerItemDecorator : Control
private Adorner? adorner;
public bool ShowDecorator
{
private Adorner adorner;
get { return (bool)GetValue(ShowDecoratorProperty); }
set { SetValue(ShowDecoratorProperty, value); }
}
public bool ShowDecorator
public static readonly DependencyProperty ShowDecoratorProperty =
DependencyProperty.Register(nameof(ShowDecorator), typeof(bool), typeof(DesignerItemDecorator),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnShowDecoratorChanged)));
public DesignerItemDecorator()
{
Unloaded += OnDesignerItemDecoratorUnloaded;
}
private void HideAdorner()
{
if (adorner is not null)
{
get { return (bool)GetValue(ShowDecoratorProperty); }
set { SetValue(ShowDecoratorProperty, value); }
adorner.Visibility = Visibility.Hidden;
}
}
public static readonly DependencyProperty ShowDecoratorProperty =
DependencyProperty.Register("ShowDecorator", typeof(bool), typeof(DesignerItemDecorator),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(ShowDecoratorProperty_Changed)));
public DesignerItemDecorator()
private void ShowAdorner()
{
if (adorner is null)
{
Unloaded += new RoutedEventHandler(this.DesignerItemDecorator_Unloaded);
}
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
private void HideAdorner()
{
if (this.adorner != null)
if (adornerLayer is not null)
{
this.adorner.Visibility = Visibility.Hidden;
ContentControl? designerItem = DataContext as ContentControl;
// Canvas? canvas = VisualTreeHelper.GetParent(designerItem) as Canvas;
adorner = new ResizeRotateAdorner(designerItem);
adornerLayer.Add(adorner);
adorner.Visibility = ShowDecorator ? Visibility.Visible : Visibility.Hidden;
}
}
private void ShowAdorner()
else
{
if (this.adorner == null)
{
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
if (adornerLayer != null)
{
ContentControl designerItem = this.DataContext as ContentControl;
Canvas canvas = VisualTreeHelper.GetParent(designerItem) as Canvas;
this.adorner = new ResizeRotateAdorner(designerItem);
adornerLayer.Add(this.adorner);
if (this.ShowDecorator)
{
this.adorner.Visibility = Visibility.Visible;
}
else
{
this.adorner.Visibility = Visibility.Hidden;
}
}
}
else
{
this.adorner.Visibility = Visibility.Visible;
}
adorner.Visibility = Visibility.Visible;
}
}
private void DesignerItemDecorator_Unloaded(object sender, RoutedEventArgs e)
private void OnDesignerItemDecoratorUnloaded(object sender, RoutedEventArgs e)
{
if (adorner is not null)
{
if (this.adorner != null)
{
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
if (adornerLayer != null)
{
adornerLayer.Remove(this.adorner);
}
this.adorner = null;
}
AdornerLayer.GetAdornerLayer(this)?.Remove(adorner);
adorner = null;
}
}
private static void ShowDecoratorProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
private static void OnShowDecoratorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DesignerItemDecorator decorator = (DesignerItemDecorator)d;
bool showDecorator = (bool)e.NewValue;
if (showDecorator)
{
DesignerItemDecorator decorator = (DesignerItemDecorator)d;
bool showDecorator = (bool)e.NewValue;
if (showDecorator)
{
decorator.ShowAdorner();
}
else
{
decorator.HideAdorner();
}
decorator.ShowAdorner();
}
else
{
decorator.HideAdorner();
}
}
}

View File

@ -3,43 +3,42 @@ using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace Vision.Recognition.Controls
namespace Vision.Recognition.Controls;
public class MoveThumb : Thumb
{
public class MoveThumb : Thumb
private RotateTransform? rotateTransform;
private ContentControl? designerItem;
public MoveThumb()
{
private RotateTransform rotateTransform;
private ContentControl designerItem;
DragStarted += OnMoveThumbDragStarted;
DragDelta += OnMoveThumbDragDelta;
}
public MoveThumb()
private void OnMoveThumbDragStarted(object sender, DragStartedEventArgs e)
{
designerItem = DataContext as ContentControl;
if (designerItem != null)
{
DragStarted += new DragStartedEventHandler(this.MoveThumb_DragStarted);
DragDelta += new DragDeltaEventHandler(this.MoveThumb_DragDelta);
rotateTransform = designerItem.RenderTransform as RotateTransform;
}
}
private void MoveThumb_DragStarted(object sender, DragStartedEventArgs e)
private void OnMoveThumbDragDelta(object sender, DragDeltaEventArgs e)
{
if (designerItem is not null)
{
this.designerItem = DataContext as ContentControl;
Point dragDelta = new(e.HorizontalChange, e.VerticalChange);
if (this.designerItem != null)
if (rotateTransform is not null)
{
this.rotateTransform = this.designerItem.RenderTransform as RotateTransform;
dragDelta = rotateTransform.Transform(dragDelta);
}
}
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
if (this.designerItem != null)
{
Point dragDelta = new Point(e.HorizontalChange, e.VerticalChange);
if (this.rotateTransform != null)
{
dragDelta = this.rotateTransform.Transform(dragDelta);
}
Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) + dragDelta.X);
Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) + dragDelta.Y);
}
Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + dragDelta.X);
Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) + dragDelta.Y);
}
}
}

View File

@ -7,114 +7,108 @@ using System.Windows.Media;
using System.Collections.Generic;
using Vision.Recognition.Controls.Adorners;
namespace Vision.Recognition.Controls
namespace Vision.Recognition.Controls;
public class ResizeThumb : Thumb
{
public class ResizeThumb : Thumb
private RotateTransform? rotateTransform;
private double angle;
private Adorner? adorner;
private Point transformOrigin;
private ContentControl? designerItem;
private Canvas? canvas;
public ResizeThumb()
{
private RotateTransform rotateTransform;
private double angle;
private Adorner adorner;
private Point transformOrigin;
private ContentControl designerItem;
private Canvas canvas;
DragStarted += OnResizeThumbDragStarted;
DragDelta += OnResizeThumbDragDelta;
DragCompleted += OnResizeThumbDragCompleted;
}
public ResizeThumb()
private void OnResizeThumbDragStarted(object sender, DragStartedEventArgs e)
{
designerItem = DataContext as ContentControl;
if (designerItem is not null)
{
DragStarted += new DragStartedEventHandler(this.ResizeThumb_DragStarted);
DragDelta += new DragDeltaEventHandler(this.ResizeThumb_DragDelta);
DragCompleted += new DragCompletedEventHandler(this.ResizeThumb_DragCompleted);
}
canvas = VisualTreeHelper.GetParent(designerItem) as Canvas;
private void ResizeThumb_DragStarted(object sender, DragStartedEventArgs e)
{
this.designerItem = this.DataContext as ContentControl;
if (this.designerItem != null)
if (canvas is not null)
{
this.canvas = VisualTreeHelper.GetParent(this.designerItem) as Canvas;
transformOrigin = designerItem.RenderTransformOrigin;
if (this.canvas != null)
rotateTransform = designerItem.RenderTransform as RotateTransform;
if (rotateTransform is not null)
{
this.transformOrigin = this.designerItem.RenderTransformOrigin;
this.rotateTransform = this.designerItem.RenderTransform as RotateTransform;
if (this.rotateTransform != null)
{
this.angle = this.rotateTransform.Angle * Math.PI / 180.0;
}
else
{
this.angle = 0.0d;
}
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.canvas);
if (adornerLayer != null)
{
this.adorner = new SizeAdorner(this.designerItem);
adornerLayer.Add(this.adorner);
}
angle = rotateTransform.Angle * Math.PI / 180.0;
}
}
}
private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
if (this.designerItem != null)
{
double deltaVertical, deltaHorizontal;
switch (VerticalAlignment)
else
{
case System.Windows.VerticalAlignment.Bottom:
deltaVertical = Math.Min(-e.VerticalChange, this.designerItem.ActualHeight - this.designerItem.MinHeight);
Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) + (this.transformOrigin.Y * deltaVertical * (1 - Math.Cos(-this.angle))));
Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) - deltaVertical * this.transformOrigin.Y * Math.Sin(-this.angle));
this.designerItem.Height -= deltaVertical;
break;
case System.Windows.VerticalAlignment.Top:
deltaVertical = Math.Min(e.VerticalChange, this.designerItem.ActualHeight - this.designerItem.MinHeight);
Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) + deltaVertical * Math.Cos(-this.angle) + (this.transformOrigin.Y * deltaVertical * (1 - Math.Cos(-this.angle))));
Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) + deltaVertical * Math.Sin(-this.angle) - (this.transformOrigin.Y * deltaVertical * Math.Sin(-this.angle)));
this.designerItem.Height -= deltaVertical;
break;
default:
break;
angle = 0.0d;
}
switch (HorizontalAlignment)
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(canvas);
if (adornerLayer is not null)
{
case System.Windows.HorizontalAlignment.Left:
deltaHorizontal = Math.Min(e.HorizontalChange, this.designerItem.ActualWidth - this.designerItem.MinWidth);
Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) + deltaHorizontal * Math.Sin(this.angle) - this.transformOrigin.X * deltaHorizontal * Math.Sin(this.angle));
Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) + deltaHorizontal * Math.Cos(this.angle) + (this.transformOrigin.X * deltaHorizontal * (1 - Math.Cos(this.angle))));
this.designerItem.Width -= deltaHorizontal;
break;
case System.Windows.HorizontalAlignment.Right:
deltaHorizontal = Math.Min(-e.HorizontalChange, this.designerItem.ActualWidth - this.designerItem.MinWidth);
Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) - this.transformOrigin.X * deltaHorizontal * Math.Sin(this.angle));
Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) + (deltaHorizontal * this.transformOrigin.X * (1 - Math.Cos(this.angle))));
this.designerItem.Width -= deltaHorizontal;
break;
default:
break;
adorner = new SizeAdorner(designerItem);
adornerLayer.Add(adorner);
}
}
e.Handled = true;
}
private void ResizeThumb_DragCompleted(object sender, DragCompletedEventArgs e)
{
if (this.adorner != null)
{
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.canvas);
if (adornerLayer != null)
{
adornerLayer.Remove(this.adorner);
}
this.adorner = null;
}
}
}
private void OnResizeThumbDragDelta(object sender, DragDeltaEventArgs e)
{
if (designerItem is not null)
{
double deltaVertical, deltaHorizontal;
switch (VerticalAlignment)
{
case VerticalAlignment.Bottom:
deltaVertical = Math.Min(-e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);
Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) + (transformOrigin.Y * deltaVertical * (1 - Math.Cos(-angle))));
Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) - deltaVertical * transformOrigin.Y * Math.Sin(-angle));
designerItem.Height -= deltaVertical;
break;
case VerticalAlignment.Top:
deltaVertical = Math.Min(e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);
Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) + deltaVertical * Math.Cos(-angle) + (transformOrigin.Y * deltaVertical * (1 - Math.Cos(-angle))));
Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + deltaVertical * Math.Sin(-angle) - (transformOrigin.Y * deltaVertical * Math.Sin(-angle)));
designerItem.Height -= deltaVertical;
break;
default:
break;
}
switch (HorizontalAlignment)
{
case HorizontalAlignment.Left:
deltaHorizontal = Math.Min(e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth);
Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) + deltaHorizontal * Math.Sin(angle) - transformOrigin.X * deltaHorizontal * Math.Sin(angle));
Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + deltaHorizontal * Math.Cos(angle) + (transformOrigin.X * deltaHorizontal * (1 - Math.Cos(angle))));
designerItem.Width -= deltaHorizontal;
break;
case HorizontalAlignment.Right:
deltaHorizontal = Math.Min(-e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth);
Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) - transformOrigin.X * deltaHorizontal * Math.Sin(angle));
Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + (deltaHorizontal * transformOrigin.X * (1 - Math.Cos(angle))));
designerItem.Width -= deltaHorizontal;
break;
default:
break;
}
}
e.Handled = true;
}
private void OnResizeThumbDragCompleted(object sender, DragCompletedEventArgs e)
{
if (adorner is not null)
{
AdornerLayer.GetAdornerLayer(canvas)?.Remove(adorner);
adorner = null;
}
}
}

View File

@ -5,68 +5,67 @@ using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
namespace Vision.Recognition.Controls
namespace Vision.Recognition.Controls;
public class RotateThumb : Thumb
{
public class RotateThumb : Thumb
private double initialAngle;
private RotateTransform? rotateTransform;
private Vector startVector;
private Point centerPoint;
private ContentControl? designerItem;
private Canvas? canvas;
public RotateThumb()
{
private double initialAngle;
private RotateTransform rotateTransform;
private Vector startVector;
private Point centerPoint;
private ContentControl designerItem;
private Canvas canvas;
DragDelta += OnRotateThumbDragDelta;
DragStarted += OnRotateThumbDragStarted;
}
public RotateThumb()
private void OnRotateThumbDragStarted(object sender, DragStartedEventArgs e)
{
designerItem = DataContext as ContentControl;
if (designerItem is not null)
{
DragDelta += new DragDeltaEventHandler(this.RotateThumb_DragDelta);
DragStarted += new DragStartedEventHandler(this.RotateThumb_DragStarted);
}
canvas = VisualTreeHelper.GetParent(designerItem) as Canvas;
private void RotateThumb_DragStarted(object sender, DragStartedEventArgs e)
{
this.designerItem = DataContext as ContentControl;
if (this.designerItem != null)
if (canvas is not null)
{
this.canvas = VisualTreeHelper.GetParent(this.designerItem) as Canvas;
centerPoint = designerItem.TranslatePoint(
new Point(designerItem.Width * designerItem.RenderTransformOrigin.X,
designerItem.Height * designerItem.RenderTransformOrigin.Y),
canvas);
if (this.canvas != null)
Point startPoint = Mouse.GetPosition(canvas);
startVector = Point.Subtract(startPoint, centerPoint);
rotateTransform = designerItem.RenderTransform as RotateTransform;
if (rotateTransform is null)
{
this.centerPoint = this.designerItem.TranslatePoint(
new Point(this.designerItem.Width * this.designerItem.RenderTransformOrigin.X,
this.designerItem.Height * this.designerItem.RenderTransformOrigin.Y),
this.canvas);
Point startPoint = Mouse.GetPosition(this.canvas);
this.startVector = Point.Subtract(startPoint, this.centerPoint);
this.rotateTransform = this.designerItem.RenderTransform as RotateTransform;
if (this.rotateTransform == null)
{
this.designerItem.RenderTransform = new RotateTransform(0);
this.initialAngle = 0;
}
else
{
this.initialAngle = this.rotateTransform.Angle;
}
designerItem.RenderTransform = new RotateTransform(0);
initialAngle = 0;
}
else
{
initialAngle = rotateTransform.Angle;
}
}
}
}
private void RotateThumb_DragDelta(object sender, DragDeltaEventArgs e)
private void OnRotateThumbDragDelta(object sender, DragDeltaEventArgs e)
{
if (designerItem is not null && canvas is not null)
{
if (this.designerItem != null && this.canvas != null)
{
Point currentPoint = Mouse.GetPosition(this.canvas);
Vector deltaVector = Point.Subtract(currentPoint, this.centerPoint);
Point currentPoint = Mouse.GetPosition(canvas);
Vector deltaVector = Point.Subtract(currentPoint, centerPoint);
double angle = Vector.AngleBetween(this.startVector, deltaVector);
double angle = Vector.AngleBetween(startVector, deltaVector);
RotateTransform rotateTransform = this.designerItem.RenderTransform as RotateTransform;
rotateTransform.Angle = this.initialAngle + Math.Round(angle, 0);
this.designerItem.InvalidateMeasure();
}
RotateTransform? rotateTransform = designerItem.RenderTransform as RotateTransform;
rotateTransform!.Angle = initialAngle + Math.Round(angle, 0);
designerItem.InvalidateMeasure();
}
}
}

View File

@ -7,97 +7,96 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Vision.Recognition
namespace Vision.Recognition;
public class DrawContent
{
public class DrawContent
/// <summary>
/// 在遮罩窗口上绘制的矩形
/// </summary>
public ConcurrentDictionary<string, RectDrawable> RectList { get; set; } = new();
/// <summary>
/// 在遮罩窗口上绘制的文本
/// </summary>
public ConcurrentDictionary<string, TextDrawable> TextList { get; set; } = new();
public void PutRect(string key, RectDrawable newRect)
{
/// <summary>
/// 在遮罩窗口上绘制的矩形
/// </summary>
public ConcurrentDictionary<string, RectDrawable> RectList { get; set; } = new();
/// <summary>
/// 在遮罩窗口上绘制的文本
/// </summary>
public ConcurrentDictionary<string, TextDrawable> TextList { get; set; } = new();
public void PutRect(string key, RectDrawable newRect)
if (RectList.TryGetValue(key, out var prevRect))
{
if (RectList.TryGetValue(key, out var prevRect))
if (newRect.Equals(prevRect))
{
if (newRect.Equals(prevRect))
{
return;
}
return;
}
RectList[key] = newRect;
MaskWindow.Instance().Refresh();
}
public void PutOrRemoveRectList(List<(string, RectDrawable)> list)
RectList[key] = newRect;
MaskWindow.Instance().Refresh();
}
public void PutOrRemoveRectList(List<(string, RectDrawable)> list)
{
bool changed = false;
list.ForEach(item =>
{
bool changed = false;
list.ForEach(item =>
var newRect = item.Item2;
if (newRect.IsEmpty)
{
var newRect = item.Item2;
if (newRect.IsEmpty)
if (RectList.TryGetValue(item.Item1, out _))
{
if (RectList.TryGetValue(item.Item1, out _))
{
RectList.TryRemove(item.Item1, out _);
changed = true;
}
}
else
{
if (RectList.TryGetValue(item.Item1, out var prevRect))
{
if (newRect.Equals(prevRect))
{
return;
}
}
RectList[item.Item1] = newRect;
RectList.TryRemove(item.Item1, out _);
changed = true;
}
});
if (changed)
{
MaskWindow.Instance().Refresh();
}
}
public void RemoveRect(string key)
{
if (RectList.TryGetValue(key, out _))
else
{
RectList.TryRemove(key, out _);
MaskWindow.Instance().Refresh();
}
}
public void PutText(string key, TextDrawable newText)
{
if (TextList.TryGetValue(key, out var prevText))
{
if (newText.Equals(prevText))
if (RectList.TryGetValue(item.Item1, out var prevRect))
{
return;
if (newRect.Equals(prevRect))
{
return;
}
}
RectList[item.Item1] = newRect;
changed = true;
}
TextList[key] = newText;
});
if (changed)
{
MaskWindow.Instance().Refresh();
}
}
public void RemoveText(string key)
public void RemoveRect(string key)
{
if (RectList.TryGetValue(key, out _))
{
if (TextList.TryGetValue(key, out _))
RectList.TryRemove(key, out _);
MaskWindow.Instance().Refresh();
}
}
public void PutText(string key, TextDrawable newText)
{
if (TextList.TryGetValue(key, out var prevText))
{
if (newText.Equals(prevText))
{
TextList.TryRemove(key, out _);
MaskWindow.Instance().Refresh();
return;
}
}
TextList[key] = newText;
MaskWindow.Instance().Refresh();
}
public void RemoveText(string key)
{
if (TextList.TryGetValue(key, out _))
{
TextList.TryRemove(key, out _);
MaskWindow.Instance().Refresh();
}
}
}

View File

@ -5,23 +5,22 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Vision.Recognition.Helper
namespace Vision.Recognition.Helper;
public class Log
{
public class Log
public static void LogInformation(string? message, params object?[] args)
{
public static void LogInformation(string? message, params object?[] args)
{
VisionContext.Instance().Log?.LogInformation(message, args);
}
VisionContext.Instance().Log?.LogInformation(message, args);
}
public static void LogWarning(string? message, params object?[] args)
{
VisionContext.Instance().Log?.LogWarning(message, args);
}
public static void LogWarning(string? message, params object?[] args)
{
VisionContext.Instance().Log?.LogWarning(message, args);
}
public static void LogError(string? message, params object?[] args)
{
VisionContext.Instance().Log?.LogError(message, args);
}
public static void LogError(string? message, params object?[] args)
{
VisionContext.Instance().Log?.LogError(message, args);
}
}

View File

@ -1,56 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Threading;
using System.Windows;
using Vanara.PInvoke;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using static Windows.Win32.PInvoke;
namespace Vision.Recognition.Helper.Simulator
namespace Vision.Recognition.Helper.Simulator;
public class MouseEventSimulator
{
public class MouseEventSimulator
public static void Move(int x, int y)
{
public static void Move(int x, int y)
{
User32.mouse_event(User32.MOUSEEVENTF.MOUSEEVENTF_ABSOLUTE | User32.MOUSEEVENTF.MOUSEEVENTF_MOVE,
x * 65535 / PrimaryScreen.DESKTOP.Width, y * 65535 / PrimaryScreen.DESKTOP.Height,
0, 0);
}
mouse_event(
MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE|MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE,
x * 65535 / PrimaryScreen.DESKTOP.Width,
y * 65535 / PrimaryScreen.DESKTOP.Height,
0,
0);
}
public static void LeftButtonDown()
{
User32.mouse_event(User32.MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
}
public static void LeftButtonDown()
{
mouse_event(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
}
public static void LeftButtonUp()
{
User32.mouse_event(User32.MOUSEEVENTF.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
public static void LeftButtonUp()
{
mouse_event(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
public static bool Click(int x, int y)
public static bool Click(int x, int y)
{
if (x == 0 && y == 0)
{
if (x == 0 && y == 0)
{
return false;
}
Move(x, y);
LeftButtonDown();
Thread.Sleep(20);
LeftButtonUp();
return true;
return false;
}
Move(x, y);
LeftButtonDown();
Thread.Sleep(20);
LeftButtonUp();
return true;
}
public static bool Click(Point point)
{
return Click((int)point.X, (int)point.Y);
}
public static bool Click(Point point)
{
return Click((int)point.X, (int)point.Y);
}
public static bool DoubleClick(Point point)
{
Click(point);
Thread.Sleep(200);
return Click(point);
}
public static bool DoubleClick(Point point)
{
Click(point);
Thread.Sleep(200);
return Click(point);
}
}

View File

@ -1,54 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Interop;
using Vanara.PInvoke;
using System.Threading;
using Windows.Win32.Foundation;
using static Windows.Win32.PInvoke;
namespace Vision.Recognition.Helper.Simulator
namespace Vision.Recognition.Helper.Simulator;
public class PostMessageSimulator
{
public class PostMessageSimulator
private readonly HWND _hWnd;
public PostMessageSimulator(HWND hWnd)
{
public static readonly uint WM_LBUTTONDOWN = 0x201; //按下鼠标左键
_hWnd = hWnd;
}
public static readonly uint WM_LBUTTONUP = 0x202; //释放鼠标左键
/// <summary>
/// 指定位置并按下左键
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public void LeftButtonClick(int x, int y)
{
LPARAM p = (y << 16) | x;
PostMessage(_hWnd, WM_LBUTTONDOWN, default, p);
Thread.Sleep(100);
PostMessage(_hWnd, WM_LBUTTONUP, default, p);
}
private readonly IntPtr _hWnd;
/// <summary>
/// 默认位置左键按下
/// </summary>
public void LeftButtonDown()
{
PostMessage(_hWnd, WM_LBUTTONDOWN, default, default);
}
public PostMessageSimulator(IntPtr hWnd)
{
this._hWnd = hWnd;
}
/// <summary>
/// 指定位置并按下左键
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public void LeftButtonClick(int x, int y)
{
IntPtr p = (y << 16) | x;
User32.PostMessage(_hWnd, WM_LBUTTONDOWN, IntPtr.Zero, p);
Thread.Sleep(100);
User32.PostMessage(_hWnd, WM_LBUTTONUP, IntPtr.Zero, p);
}
/// <summary>
/// 默认位置左键按下
/// </summary>
public void LeftButtonDown()
{
User32.PostMessage(_hWnd, WM_LBUTTONDOWN, IntPtr.Zero);
}
/// <summary>
/// 默认位置左键释放
/// </summary>
public void LeftButtonUp()
{
User32.PostMessage(_hWnd, WM_LBUTTONUP, IntPtr.Zero);
}
/// <summary>
/// 默认位置左键释放
/// </summary>
public void LeftButtonUp()
{
PostMessage(_hWnd, WM_LBUTTONUP, default, default);
}
}

View File

@ -1,8 +1,9 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Vanara.PInvoke;
using static Vanara.PInvoke.Gdi32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using static Windows.Win32.PInvoke;
namespace Vision.Recognition.Helper.Simulator
{
@ -15,13 +16,13 @@ namespace Vision.Recognition.Helper.Simulator
{
get
{
var hdc = User32.GetDC(IntPtr.Zero);
var hdc = GetDC(default);
var size = new Size
{
Width = Gdi32.GetDeviceCaps(hdc, DeviceCap.HORZRES),
Height = Gdi32.GetDeviceCaps(hdc, DeviceCap.VERTRES)
Width = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.HORZRES),
Height = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.VERTRES)
};
User32.ReleaseDC(IntPtr.Zero, hdc);
ReleaseDC(default, hdc);
return size;
}
}
@ -32,9 +33,9 @@ namespace Vision.Recognition.Helper.Simulator
{
get
{
var hdc = User32.GetDC(IntPtr.Zero);
var dpiX = Gdi32.GetDeviceCaps(hdc, DeviceCap.LOGPIXELSX);
User32.ReleaseDC(IntPtr.Zero, hdc);
var hdc = GetDC(default);
var dpiX = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.LOGPIXELSX);
ReleaseDC(default, hdc);
return dpiX;
}
}
@ -45,9 +46,9 @@ namespace Vision.Recognition.Helper.Simulator
{
get
{
var hdc = User32.GetDC(IntPtr.Zero);
var dpiX = Gdi32.GetDeviceCaps(hdc, DeviceCap.LOGPIXELSY);
User32.ReleaseDC(IntPtr.Zero, hdc);
var hdc = GetDC(default);
var dpiX = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.LOGPIXELSY);
ReleaseDC(default, hdc);
return dpiX;
}
}
@ -58,13 +59,13 @@ namespace Vision.Recognition.Helper.Simulator
{
get
{
var hdc = User32.GetDC(IntPtr.Zero);
var hdc = GetDC(default);
var size = new Size
{
Width = Gdi32.GetDeviceCaps(hdc, DeviceCap.DESKTOPHORZRES),
Height = Gdi32.GetDeviceCaps(hdc, DeviceCap.DESKTOPVERTRES)
Width = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.DESKTOPHORZRES),
Height = GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.DESKTOPVERTRES)
};
User32.ReleaseDC(IntPtr.Zero, hdc);
ReleaseDC(default, hdc);
return size;
}
}
@ -76,9 +77,9 @@ namespace Vision.Recognition.Helper.Simulator
{
get
{
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);
var hdc = GetDC(default);
var scaleX = (float)GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.DESKTOPHORZRES) / GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.HORZRES);
ReleaseDC(default, hdc);
return scaleX;
}
}
@ -89,9 +90,9 @@ namespace Vision.Recognition.Helper.Simulator
{
get
{
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);
var hdc = GetDC(default);
var scaleY = (float)GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.DESKTOPVERTRES) / GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.VERTRES);
ReleaseDC(default, hdc);
return scaleY;
}
}

View File

@ -1,16 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Win32.Foundation;
namespace Vision.Recognition.Helper.Simulator
namespace Vision.Recognition.Helper.Simulator;
public class Simulator
{
public class Simulator
public static PostMessageSimulator PostMessage(HWND hWnd)
{
public static PostMessageSimulator PostMessage(IntPtr hWnd)
{
return new PostMessageSimulator(hWnd);
}
return new PostMessageSimulator(hWnd);
}
}

View File

@ -6,21 +6,20 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Vision.Recognition.Task
namespace Vision.Recognition.Task;
/// <summary>
/// 和触发器的区别:任务不需要持续捕获游戏图像
/// </summary>
public class BaseTaskThread
{
/// <summary>
/// 和触发器的区别:任务不需要持续捕获游戏图像
/// </summary>
public class BaseTaskThread
protected ILogger<BaseTaskThread> Log;
protected CancellationTokenSource Cancellation;
public BaseTaskThread(ILogger<BaseTaskThread> logger, CancellationTokenSource cts)
{
protected ILogger<BaseTaskThread> Log;
protected CancellationTokenSource Cancellation;
public BaseTaskThread(ILogger<BaseTaskThread> logger, CancellationTokenSource cts)
{
Log = logger;
Cancellation = cts;
}
Log = logger;
Cancellation = cts;
}
}

View File

@ -1,70 +1,65 @@
using System;
using System.Collections.Generic;
using OpenCvSharp;
using System;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;
using Vision.Recognition.Helper.OpenCv;
namespace Vision.Recognition.Task
namespace Vision.Recognition.Task;
/// <summary>
/// 捕获的内容
/// 以及一些多个trigger会用到的内容
/// </summary>
public class CaptureContent
{
/// <summary>
/// 捕获的内容
/// 以及一些多个trigger会用到的内容
/// </summary>
public class CaptureContent
public static readonly int MaxFrameIndexSecond = 60;
public Bitmap SrcBitmap { get; }
public int FrameIndex { get; private set; }
public int FrameRate { get; private set; }
public CaptureContent(Bitmap srcBitmap, int frameIndex, int frameRate)
{
public static readonly int MaxFrameIndexSecond = 60;
SrcBitmap = srcBitmap;
FrameIndex = frameIndex;
FrameRate = frameRate;
}
public Bitmap SrcBitmap { get; }
public int FrameIndex { get; private set; }
public int FrameRate { get; private set; }
private Mat? _srcMat;
public CaptureContent(Bitmap srcBitmap, int frameIndex, int frameRate)
public Mat SrcMat
{
get
{
SrcBitmap = srcBitmap;
FrameIndex = frameIndex;
FrameRate = frameRate;
}
private Mat? _srcMat;
public Mat SrcMat
{
get
{
_srcMat ??= SrcBitmap.ToMat();
return _srcMat;
}
}
private Mat? _srcGreyMat;
public Mat SrcGreyMat
{
get
{
_srcGreyMat ??= new Mat();
Cv2.CvtColor(SrcMat, _srcGreyMat, ColorConversionCodes.BGR2GRAY);
return _srcGreyMat;
}
}
/// <summary>
/// 达到了什么时间间隔
/// 最大MaxFrameIndexSecond秒
/// </summary>
/// <param name="interval"></param>
/// <returns></returns>
public bool IsReachInterval(TimeSpan interval)
{
if (interval.TotalSeconds > MaxFrameIndexSecond)
{
throw new ArgumentException($"时间间隔不能超过{MaxFrameIndexSecond}s");
}
return FrameIndex % (FrameRate * interval.TotalSeconds) == 0;
_srcMat ??= SrcBitmap.ToMat();
return _srcMat;
}
}
private Mat? _srcGreyMat;
public Mat SrcGreyMat
{
get
{
_srcGreyMat ??= new Mat();
Cv2.CvtColor(SrcMat, _srcGreyMat, ColorConversionCodes.BGR2GRAY);
return _srcGreyMat;
}
}
/// <summary>
/// 达到了什么时间间隔
/// 最大MaxFrameIndexSecond秒
/// </summary>
/// <param name="interval"></param>
/// <returns></returns>
public bool IsReachInterval(TimeSpan interval)
{
if (interval.TotalSeconds > MaxFrameIndexSecond)
{
throw new ArgumentException($"时间间隔不能超过{MaxFrameIndexSecond}s");
}
return FrameIndex % (FrameRate * interval.TotalSeconds) == 0;
}
}

View File

@ -4,12 +4,11 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Vision.Recognition.Task.Config
{
public class TaskSettings
{
string Name { get; set; }
namespace Vision.Recognition.Task.Config;
bool IsEnabled { get; set; }
}
public class TaskSettings
{
string Name { get; set; }
bool IsEnabled { get; set; }
}

View File

@ -5,36 +5,35 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace Vision.Recognition
namespace Vision.Recognition;
[Serializable]
public class TextDrawable
{
[Serializable]
public class TextDrawable
public string Text { get; set; }
public Point Point { get; set; }
public TextDrawable(string text, Point point)
{
public string Text { get; set; }
public Point Point { get; set; }
public TextDrawable(string text, Point point)
{
Text = text;
Point = point;
}
public override bool Equals(object? obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
var other = (TextDrawable)obj;
return Point.Equals(other.Point);
}
public override int GetHashCode()
{
return Point.GetHashCode();
}
public bool IsEmpty => Point is { X: 0, Y: 0 };
Text = text;
Point = point;
}
public override bool Equals(object? obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
var other = (TextDrawable)obj;
return Point.Equals(other.Point);
}
public override int GetHashCode()
{
return Point.GetHashCode();
}
public bool IsEmpty => Point is { X: 0, Y: 0 };
}

View File

@ -13,7 +13,10 @@
<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="Vanara.PInvoke.User32" Version="3.4.16" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BetterGenshinImpact.Win32\BetterGenshinImpact.Win32.csproj" />
</ItemGroup>
<ItemGroup>

View File

@ -1,23 +1,18 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Vanara.Extensions;
using Vanara.PInvoke;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using static Windows.Win32.PInvoke;
namespace Vision.WindowCapture.BitBlt
{
public class BitBltCapture : IWindowCapture
{
private IntPtr _hWnd;
private HWND _hWnd;
public bool IsCapturing { get; private set; }
public void Start(IntPtr hWnd)
public void Start(HWND hWnd)
{
_hWnd = hWnd;
IsCapturing = true;
@ -32,21 +27,21 @@ namespace Vision.WindowCapture.BitBlt
try
{
User32.GetWindowRect(_hWnd, out var windowRect);
GetWindowRect(_hWnd, out var windowRect);
var width = windowRect.Width;
var height = windowRect.Height;
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);
var hdcSrc = GetWindowDC(_hWnd);
var hdcDest = CreateCompatibleDC(hdcSrc);
var hBitmap = CreateCompatibleBitmap(hdcSrc, width, height);
var hOld = SelectObject(hdcDest, hBitmap);
Windows.Win32.PInvoke.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, ROP_CODE.SRCCOPY);
SelectObject(hdcDest, hOld);
DeleteDC(hdcDest);
ReleaseDC(_hWnd, hdcSrc);
var bitmap = Image.FromHbitmap(hBitmap);
DeleteObject(hBitmap);
return bitmap;
}
catch (Exception e)
@ -58,7 +53,7 @@ namespace Vision.WindowCapture.BitBlt
public void Stop()
{
_hWnd = IntPtr.Zero;
_hWnd = HWND.Null;
IsCapturing = false;
}
}

View File

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

View File

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

View File

@ -1,32 +1,22 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using Vision.WindowCapture.GraphicsCapture.Helpers;
using Windows.Devices.HumanInterfaceDevice;
using Windows.Graphics.Capture;
using Windows.Graphics.DirectX;
using Windows.Graphics.DirectX.Direct3D11;
using SharpDX.Direct3D11;
using WinRT.Interop;
using Windows.Win32.Foundation;
namespace Vision.WindowCapture.GraphicsCapture
{
public class GraphicsCapture : IWindowCapture
{
private IntPtr _hWnd;
private HWND _hWnd;
private Direct3D11CaptureFramePool _captureFramePool;
private GraphicsCaptureItem _captureItem;
private GraphicsCaptureSession _captureSession;
private Direct3D11CaptureFramePool? _captureFramePool;
private GraphicsCaptureItem? _captureItem;
private GraphicsCaptureSession? _captureSession;
public bool IsCapturing { get; private set; }
public void Start(IntPtr hWnd)
public void Start(HWND hWnd)
{
_hWnd = hWnd;
IsCapturing = true;
@ -47,7 +37,7 @@ namespace Vision.WindowCapture.GraphicsCapture
throw new InvalidOperationException("Failed to create capture item.");
}
_captureItem.Closed += CaptureItemOnClosed;
_captureItem.Closed += OnCaptureItemClosed;
var device = Direct3D11Helper.CreateDevice();
@ -72,15 +62,15 @@ namespace Vision.WindowCapture.GraphicsCapture
{
_captureSession?.Dispose();
_captureFramePool?.Dispose();
_captureSession = null;
_captureFramePool = null;
_captureItem = null;
_captureSession = default;
_captureFramePool = default;
_captureItem = default;
_hWnd = IntPtr.Zero;
_hWnd = HWND.Null;
IsCapturing = false;
}
private void CaptureItemOnClosed(GraphicsCaptureItem sender, object args)
private void OnCaptureItemClosed(GraphicsCaptureItem sender, object args)
{
Stop();
}

View File

@ -22,67 +22,40 @@
// THE SOFTWARE.
// ---------------------------------------------------------------------------------
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.WinRT.Graphics.Capture;
using Windows.Graphics.Capture;
using WinRT;
using WinRT.Interop;
namespace Vision.WindowCapture.GraphicsCapture.Helpers
namespace Vision.WindowCapture.GraphicsCapture.Helpers;
public static class CaptureHelper
{
public static class CaptureHelper
static readonly Guid GraphicsCaptureItemGuid = Guid.Parse("79C3F95B-31F7-4EC2-A464-632EF5D30760");
public static void SetWindow(this GraphicsCapturePicker picker, IntPtr hwnd)
{
static readonly Guid GraphicsCaptureItemGuid = new Guid("79C3F95B-31F7-4EC2-A464-632EF5D30760");
[ComImport]
[Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IInitializeWithWindow
{
void Initialize(
IntPtr hwnd);
}
[ComImport]
[Guid("3628E81B-3CAC-4C60-B7F4-23CE0E0C3356")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IGraphicsCaptureItemInterop
{
IntPtr CreateForWindow(
[In] IntPtr window,
[In] ref Guid iid);
IntPtr CreateForMonitor(
[In] IntPtr monitor,
[In] ref Guid iid);
}
public static void SetWindow(this GraphicsCapturePicker picker, IntPtr hwnd)
{
var interop = picker.As<IInitializeWithWindow>();
interop.Initialize(hwnd);
}
public static GraphicsCaptureItem CreateItemForWindow(IntPtr hwnd)
{
var factory = WinrtModule.GetActivationFactory("Windows.Graphics.Capture.GraphicsCaptureItem");
var interop = factory.AsInterface<IGraphicsCaptureItemInterop>();
var itemPointer = interop.CreateForWindow(hwnd, GraphicsCaptureItemGuid);
return GraphicsCaptureItem.FromAbi(itemPointer);
}
//public static GraphicsCaptureItem CreateItemForMonitor(IntPtr hmon)
//{
// var factory = WindowsRuntimeMarshal.GetActivationFactory(typeof(GraphicsCaptureItem));
// var interop = (IGraphicsCaptureItemInterop)factory;
// var temp = typeof(GraphicsCaptureItem);
// var itemPointer = interop.CreateForMonitor(hmon, GraphicsCaptureItemGuid);
// var item = Marshal.GetObjectForIUnknown(itemPointer) as GraphicsCaptureItem;
// Marshal.Release(itemPointer);
// return item;
//}
InitializeWithWindow.Initialize(picker, hwnd);
}
public static GraphicsCaptureItem CreateItemForWindow(HWND hwnd)
{
GraphicsCaptureItem
.As<IGraphicsCaptureItemInterop>()
.CreateForWindow(hwnd, GraphicsCaptureItemGuid, out nint thisPtr);
return GraphicsCaptureItem.FromAbi(thisPtr);
}
//public static GraphicsCaptureItem CreateItemForMonitor(IntPtr hmon)
//{
// var factory = WindowsRuntimeMarshal.GetActivationFactory(typeof(GraphicsCaptureItem));
// var interop = (IGraphicsCaptureItemInterop)factory;
// var temp = typeof(GraphicsCaptureItem);
// var itemPointer = interop.CreateForMonitor(hmon, GraphicsCaptureItemGuid);
// var item = Marshal.GetObjectForIUnknown(itemPointer) as GraphicsCaptureItem;
// Marshal.Release(itemPointer);
// return item;
//}
}

View File

@ -22,11 +22,15 @@
// THE SOFTWARE.
// ---------------------------------------------------------------------------------
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Windows.Graphics.DirectX.Direct3D11;
using Windows.Win32;
using Windows.Win32.Graphics.Direct3D;
using Windows.Win32.Graphics.Direct3D11;
using Windows.Win32.Graphics.Dxgi;
using Windows.Win32.System.WinRT.Direct3D11;
using WinRT;
using static Windows.Win32.PInvoke;
namespace Vision.WindowCapture.GraphicsCapture.Helpers
{
@ -35,106 +39,71 @@ namespace Vision.WindowCapture.GraphicsCapture.Helpers
static Guid IInspectable = new Guid("AF86E2E0-B12D-4c6a-9C5A-D7AA65101E90");
static Guid ID3D11Resource = new Guid("dc8e63f3-d12b-4952-b47b-5e45026a862d");
static Guid IDXGIAdapter3 = new Guid("645967A4-1392-4310-A798-8053CE3E93FD");
static Guid ID3D11Device = new Guid("db6f6ddb-ac77-4e88-8253-819df9bbf140");
//static Guid ID3D11Device = new Guid("db6f6ddb-ac77-4e88-8253-819df9bbf140");
static Guid ID3D11Texture2D = new Guid("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
[ComImport]
[Guid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IDirect3DDxgiInterfaceAccess
{
IntPtr GetInterface([In] ref Guid iid);
};
[DllImport(
"d3d11.dll",
EntryPoint = "CreateDirect3D11DeviceFromDXGIDevice",
SetLastError = true,
CharSet = CharSet.Unicode,
ExactSpelling = true,
CallingConvention = CallingConvention.StdCall
)]
static extern UInt32 CreateDirect3D11DeviceFromDXGIDevice(IntPtr dxgiDevice, out IntPtr graphicsDevice);
[DllImport(
"d3d11.dll",
EntryPoint = "CreateDirect3D11SurfaceFromDXGISurface",
SetLastError = true,
CharSet = CharSet.Unicode,
ExactSpelling = true,
CallingConvention = CallingConvention.StdCall
)]
static extern UInt32 CreateDirect3D11SurfaceFromDXGISurface(IntPtr dxgiSurface, out IntPtr graphicsSurface);
public static IDirect3DDevice CreateDevice()
public static IDirect3DDevice? CreateDevice()
{
return CreateDevice(false);
}
public static IDirect3DDevice CreateDevice(bool useWARP)
public static unsafe 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;
D3D_DRIVER_TYPE driverType = useWARP ? D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_SOFTWARE : D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_HARDWARE;
D3D11CreateDevice(
default,
driverType,
default,
D3D11_CREATE_DEVICE_FLAG.D3D11_CREATE_DEVICE_BGRA_SUPPORT,
default,
D3D11_SDK_VERSION,
out ID3D11Device? d3d11Device,
default,
out _);
return CreateDirect3DDeviceFromD3D11Device(d3d11Device);
}
public static IDirect3DDevice CreateDirect3DDeviceFromSharpDXDevice(SharpDX.Direct3D11.Device d3dDevice)
public static IDirect3DDevice? CreateDirect3DDeviceFromD3D11Device(ID3D11Device d3d11Device)
{
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.
if (CreateDirect3D11DeviceFromDXGIDevice(d3d11Device.As<IDXGIDevice>(), out Windows.Win32.System.WinRT.IInspectable iInspectable).Succeeded)
{
// Wrap the native device using a WinRT interop object.
uint hr = CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.NativePointer, out IntPtr pUnknown);
if (hr == 0)
{
device = MarshalInterface<IDirect3DDevice>.FromAbi(pUnknown);
Marshal.Release(pUnknown);
}
nint pointer = Marshal.GetIUnknownForObject(iInspectable);
return MarshalInterface<IDirect3DDevice>.FromAbi(pointer);
}
return device;
return default;
}
public static IDirect3DSurface CreateDirect3DSurfaceFromSharpDXTexture(SharpDX.Direct3D11.Texture2D texture)
public static IDirect3DSurface? CreateDirect3DSurfaceFromSharpDXTexture(ID3D11Texture2D 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.
if (CreateDirect3D11SurfaceFromDXGISurface(texture.As<IDXGISurface>(), out Windows.Win32.System.WinRT.IInspectable iInspectable).Succeeded)
{
// Wrap the native device using a WinRT interop object.
uint hr = CreateDirect3D11SurfaceFromDXGISurface(dxgiSurface.NativePointer, out IntPtr pUnknown);
if (hr == 0)
{
surface = Marshal.GetObjectForIUnknown(pUnknown) as IDirect3DSurface;
Marshal.Release(pUnknown);
}
nint pointer = Marshal.GetIUnknownForObject(iInspectable);
return MarshalInterface<IDirect3DSurface>.FromAbi(pointer);
}
return surface;
return default;
}
public static SharpDX.Direct3D11.Device CreateSharpDXDevice(IDirect3DDevice device)
public static ID3D11Device CreateD3D11Device(IDirect3DDevice device)
{
var access = (IDirect3DDxgiInterfaceAccess)device;
var d3dPointer = access.GetInterface(ID3D11Device);
var d3dDevice = new SharpDX.Direct3D11.Device(d3dPointer);
return d3dDevice;
device
.As<IDirect3DDxgiInterfaceAccess>()
.GetInterface(typeof(ID3D11Device).GUID, out var d3dDevice);
return MarshalInterface<ID3D11Device>.FromAbi(d3dDevice);
}
public static SharpDX.Direct3D11.Texture2D CreateSharpDXTexture2D(IDirect3DSurface surface)
public static ID3D11Texture2D CreateD3D11Texture2D(IDirect3DSurface surface)
{
var access = surface.As<IDirect3DDxgiInterfaceAccess>();
var d3dPointer = access.GetInterface(ID3D11Texture2D);
var d3dSurface = new SharpDX.Direct3D11.Texture2D(d3dPointer);
return d3dSurface;
surface
.As<IDirect3DDxgiInterfaceAccess>()
.GetInterface(typeof(ID3D11Texture2D).GUID, out var d3dSurface);
return MarshalInterface<ID3D11Texture2D>.FromAbi(d3dSurface);
}
}
}

View File

@ -1,65 +1,51 @@
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Windows.Graphics.Capture;
using Windows.Graphics.DirectX.Direct3D11;
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;
using Vision.WindowCapture.GraphicsCapture.Helpers;
using Windows.Win32;
using Windows.Win32.Graphics.Direct3D11;
using Windows.Win32.Graphics.Dxgi.Common;
using WinRT;
namespace Vision.WindowCapture.GraphicsCapture.Helpers
{
public static class Texture2DExtensions
{
public static Bitmap? ToBitmap(this Direct3D11CaptureFrame frame)
public static unsafe Bitmap ToBitmap(this Direct3D11CaptureFrame frame)
{
var texture2dBitmap = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface);
var texture2dBitmap = Direct3D11Helper.CreateD3D11Texture2D(frame.Surface);
var d3dDevice = texture2dBitmap.Device;
texture2dBitmap.GetDevice(out var d3dDevice);
texture2dBitmap.GetDesc(out D3D11_TEXTURE2D_DESC desc);
D3D11_TEXTURE2D_DESC newDesc = new()
{
Width = (uint)frame.ContentSize.Width,
Height = (uint)frame.ContentSize.Height,
MipLevels = 1U,
ArraySize = 1U,
Format = desc.Format,
Usage = D3D11_USAGE.D3D11_USAGE_STAGING,
SampleDesc = new DXGI_SAMPLE_DESC() { Count = 1 },
CPUAccessFlags = D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ,
};
// 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
});
d3dDevice.CreateTexture2D(newDesc, default, out ID3D11Texture2D staging);
d3dDevice.GetImmediateContext(out ID3D11DeviceContext context);
try
{
// Copy data
d3dDevice.ImmediateContext.CopyResource(texture2dBitmap, staging);
// Copy data
context.CopyResource(texture2dBitmap, staging);
D3D11_MAPPED_SUBRESOURCE subResource = default;
context.Map(staging, default, D3D11_MAP.D3D11_MAP_READ, default, &subResource);
var dataBox = d3dDevice.ImmediateContext.MapSubresource(staging, 0, 0, MapMode.Read,
SharpDX.Direct3D11.MapFlags.None,
out DataStream stream);
staging.GetDesc(out var stagingDesc);
var bitmap = new Bitmap(
(int)stagingDesc.Width,
(int)stagingDesc.Height,
(int)subResource.RowPitch,
PixelFormat.Format32bppArgb,
(nint)subResource.pData);
var bitmap = new Bitmap(staging.Description.Width, staging.Description.Height, dataBox.RowPitch,
PixelFormat.Format32bppArgb, dataBox.DataPointer);
return bitmap;
}
finally
{
staging.Dispose();
}
return bitmap;
}
//public static Stream ToBitmapStream(this Direct3D11CaptureFrame frame)

View File

@ -1,115 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using WinRT;
namespace Vision.WindowCapture.GraphicsCapture.Helpers
{
[ComImport]
[Guid( "3628E81B-3CAC-4C60-B7F4-23CE0E0C3356" )]
[InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
internal interface IGraphicsCaptureItemInterop
{
int CreateForWindow( [In] IntPtr window, [In] ref Guid iid, out IntPtr result );
int CreateForMonitor( [In] IntPtr monitor, [In] ref Guid iid, out IntPtr result );
}
[Guid( "00000035-0000-0000-C000-000000000046" )]
internal unsafe struct IActivationFactoryVftbl
{
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
public readonly IInspectable.Vftbl IInspectableVftbl;
private readonly void* _ActivateInstance;
#pragma warning restore
public delegate* unmanaged[Stdcall]< IntPtr, IntPtr*, int > ActivateInstance => (delegate* unmanaged[Stdcall]< IntPtr, IntPtr*, int >)_ActivateInstance;
}
internal class Platform
{
[DllImport( "api-ms-win-core-com-l1-1-0.dll" )]
internal static extern int CoDecrementMTAUsage( IntPtr cookie );
[DllImport( "api-ms-win-core-com-l1-1-0.dll" )]
internal static extern unsafe int CoIncrementMTAUsage( IntPtr* cookie );
[DllImport( "api-ms-win-core-winrt-l1-1-0.dll" )]
internal static extern unsafe int RoGetActivationFactory( IntPtr runtimeClassId, ref Guid iid, IntPtr* factory );
}
/// <summary>
/// https://github.com/zlatanov/windows-screen-recorder
/// </summary>
internal static class WinrtModule
{
private static readonly Dictionary<string, ObjectReference<IActivationFactoryVftbl>> Cache = new Dictionary<string, ObjectReference<IActivationFactoryVftbl>>();
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 IntPtr GetActivationFactory( IntPtr hstrRuntimeClassId )
{
if ( s_cookie == IntPtr.Zero )
{
lock ( s_lock )
{
if ( s_cookie == IntPtr.Zero )
{
IntPtr cookie;
Marshal.ThrowExceptionForHR( Platform.CoIncrementMTAUsage( &cookie ) );
s_cookie = cookie;
}
}
}
Guid iid = typeof( IActivationFactoryVftbl ).GUID;
IntPtr 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 IntPtr s_cookie;
private static readonly object s_lock = new object();
}
}

View File

@ -1,15 +1,15 @@
using System.Drawing;
using Windows.Win32.Foundation;
namespace Vision.WindowCapture
namespace Vision.WindowCapture;
public interface IWindowCapture
{
public interface IWindowCapture
{
bool IsCapturing { get; }
void Start(IntPtr hWnd);
bool IsCapturing { get; }
void Start(HWND hWnd);
Bitmap? Capture();
Bitmap? Capture();
void Stop();
}
void Stop();
}

View File

@ -8,11 +8,16 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
<None Remove="README.md" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="Vanara.PInvoke.Gdi32" Version="3.4.16" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.4.16" />
<PackageReference Include="Vanara.Windows.Extensions" Version="3.4.16" />
<PackageReference Include="Vortice.Direct3D11" Version="3.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BetterGenshinImpact.Win32\BetterGenshinImpact.Win32.csproj" />
</ItemGroup>
</Project>

View File

@ -1,44 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Vision.WindowCapture;
namespace Vision.WindowCapture
public class WindowCaptureFactory
{
public enum CaptureMode
public static string[] ModeNames()
{
BitBlt,
WindowsGraphicsCapture
return Enum.GetNames(typeof(CaptureMode));
}
public static class CaptureModeExtensions
public static IWindowCapture Create(CaptureMode mode)
{
public static CaptureMode ToCaptureMode(this string modeName)
switch (mode)
{
return (CaptureMode) Enum.Parse(typeof(CaptureMode), modeName);
}
}
public class WindowCaptureFactory
{
public static string[] ModeNames()
{
return Enum.GetNames(typeof(CaptureMode));
}
public static IWindowCapture Create(CaptureMode mode)
{
switch (mode)
{
case CaptureMode.BitBlt:
return new BitBlt.BitBltCapture();
case CaptureMode.WindowsGraphicsCapture:
return new GraphicsCapture.GraphicsCapture();
default:
throw new ArgumentOutOfRangeException(nameof(mode), mode, null);
}
case CaptureMode.BitBlt:
return new BitBlt.BitBltCapture();
case CaptureMode.WindowsGraphicsCapture:
return new GraphicsCapture.GraphicsCapture();
default:
throw new ArgumentOutOfRangeException(nameof(mode), mode, null);
}
}
}