diff --git a/src/GenshinFishingToy.sln b/src/GenshinFishingToy.sln index db6546e..1b60c56 100644 --- a/src/GenshinFishingToy.sln +++ b/src/GenshinFishingToy.sln @@ -7,6 +7,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenshinFishingToy", "Genshi EndProject Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "GenshinFishingToySetup", "GenshinFishingToySetup\GenshinFishingToySetup.wapproj", "{A80A34DD-ACBC-4D67-A22D-156A0FFC32A8}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WindowsGraphicsCapture", "WindowsGraphicsCapture", "{61B278E1-2049-475B-B200-3F0C3DDA87C2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCapture", "WindowsGraphicsCapture\ScreenCapture\ScreenCapture.csproj", "{AB145A23-0897-4517-8EA6-BEB87CB9D26A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CaptureSampleCore", "WindowsGraphicsCapture\CaptureSampleCore\CaptureSampleCore.csproj", "{D8154C31-38C2-49E2-96A0-69EB76567461}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Composition.WindowsRuntimeHelpers", "WindowsGraphicsCapture\Composition.WindowsRuntimeHelpers\Composition.WindowsRuntimeHelpers.csproj", "{AE6F99F5-AFD5-467E-A082-D4003E3EEECF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -23,10 +31,27 @@ Global {A80A34DD-ACBC-4D67-A22D-156A0FFC32A8}.Release|x64.ActiveCfg = Release|x64 {A80A34DD-ACBC-4D67-A22D-156A0FFC32A8}.Release|x64.Build.0 = Release|x64 {A80A34DD-ACBC-4D67-A22D-156A0FFC32A8}.Release|x64.Deploy.0 = Release|x64 + {AB145A23-0897-4517-8EA6-BEB87CB9D26A}.Debug|x64.ActiveCfg = Debug|x64 + {AB145A23-0897-4517-8EA6-BEB87CB9D26A}.Debug|x64.Build.0 = Debug|x64 + {AB145A23-0897-4517-8EA6-BEB87CB9D26A}.Release|x64.ActiveCfg = Release|x64 + {AB145A23-0897-4517-8EA6-BEB87CB9D26A}.Release|x64.Build.0 = Release|x64 + {D8154C31-38C2-49E2-96A0-69EB76567461}.Debug|x64.ActiveCfg = Debug|x64 + {D8154C31-38C2-49E2-96A0-69EB76567461}.Debug|x64.Build.0 = Debug|x64 + {D8154C31-38C2-49E2-96A0-69EB76567461}.Release|x64.ActiveCfg = Release|x64 + {D8154C31-38C2-49E2-96A0-69EB76567461}.Release|x64.Build.0 = Release|x64 + {AE6F99F5-AFD5-467E-A082-D4003E3EEECF}.Debug|x64.ActiveCfg = Debug|x64 + {AE6F99F5-AFD5-467E-A082-D4003E3EEECF}.Debug|x64.Build.0 = Debug|x64 + {AE6F99F5-AFD5-467E-A082-D4003E3EEECF}.Release|x64.ActiveCfg = Release|x64 + {AE6F99F5-AFD5-467E-A082-D4003E3EEECF}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AB145A23-0897-4517-8EA6-BEB87CB9D26A} = {61B278E1-2049-475B-B200-3F0C3DDA87C2} + {D8154C31-38C2-49E2-96A0-69EB76567461} = {61B278E1-2049-475B-B200-3F0C3DDA87C2} + {AE6F99F5-AFD5-467E-A082-D4003E3EEECF} = {61B278E1-2049-475B-B200-3F0C3DDA87C2} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DFA8F1CD-513D-45D7-856D-378146C4D367} EndGlobalSection diff --git a/src/GenshinFishingToy/App.xaml.cs b/src/GenshinFishingToy/App.xaml.cs index d19c399..68cf57c 100644 --- a/src/GenshinFishingToy/App.xaml.cs +++ b/src/GenshinFishingToy/App.xaml.cs @@ -1,4 +1,5 @@ -using GenshinFishingToy.Core; +using Composition.WindowsRuntimeHelpers; +using GenshinFishingToy.Core; using GenshinFishingToy.ViewModels; using Hardcodet.Wpf.TaskbarNotification; using Microsoft.Toolkit.Uwp.Notifications; @@ -9,11 +10,13 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; +using Windows.System; namespace GenshinFishingToy; public partial class App : Application { + private DispatcherQueueController _controller; public static new App? Current { get; protected set; } = null!; public TaskbarIcon Taskbar { get; protected set; } = null!; public static bool IsElevated { get; } = GetElevated(); @@ -23,6 +26,7 @@ public App() Logger.Info("Startup"); Current = this; Current.DispatcherUnhandledException += (_, e) => e.Handled = true; + _controller = CoreMessagingHelper.CreateDispatcherQueueControllerForCurrentThread(); AppDomain.CurrentDomain.UnhandledException += (s, e) => Logger.Error(e); ToastNotificationManagerCompat.OnActivated += NotifyIconViewModel.OnNotificationActivated; NoticeService.ClearNotice(); diff --git a/src/GenshinFishingToy/Core/ImageCapture.cs b/src/GenshinFishingToy/Core/ImageCapture.cs index 0f8f9a8..ea9d610 100644 --- a/src/GenshinFishingToy/Core/ImageCapture.cs +++ b/src/GenshinFishingToy/Core/ImageCapture.cs @@ -1,6 +1,8 @@ using System; using System.Drawing; +using System.Text.RegularExpressions; using System.Windows; +using WPFCaptureSample; namespace GenshinFishingToy.Core; @@ -11,6 +13,8 @@ internal static class ImageCapture public static int W { get; set; } public static int H { get; set; } + public static ImageCaptureType CaptureType = ImageCaptureType.WindowsGraphicsCapture; + public static void Setup(int x, int y, int w, int h) { X = x; @@ -24,6 +28,11 @@ public static void Setup((int x, int y, int w, int h) rect) Setup(rect.x, rect.y, rect.w, rect.h); } + public static void Teardown() + { + GraphicsCapture.Uncapture(); + } + public static bool IsFullScreen { get; private set; } = false; public static bool IsFullScreenMode(IntPtr hwnd) { @@ -52,11 +61,40 @@ private static int GetCaptionHeight(IntPtr? hwnd = null) public static Bitmap Capture(IntPtr? hwnd = null) { - return ImageExtension.Capture(X, Y - GetCaptionHeight(hwnd), W, H, hwnd); + if (CaptureType == ImageCaptureType.BitBlt) + { + return ImageExtension.Capture(X, Y - GetCaptionHeight(hwnd), W, H, hwnd); + } + else if (CaptureType == ImageCaptureType.WindowsGraphicsCapture) + { + return GraphicsCapture.Capture(X, Y, W, H, hwnd); + } + return null!; } public static Bitmap CaptureLiftingWords(IntPtr? hwnd = null) { - return ImageExtension.Capture(X, Y - GetCaptionHeight(hwnd) + H, W, (int)(H * 2.5d), hwnd); + if (CaptureType == ImageCaptureType.BitBlt) + { + return ImageExtension.Capture(X, Y - GetCaptionHeight(hwnd) + H, W, (int)(H * 2.5d), hwnd); + } + else if (CaptureType == ImageCaptureType.WindowsGraphicsCapture) + { + return GraphicsCapture.Capture(X, Y + H, W, (int)(H * 2.5d), hwnd); + } + return null!; } } + +public enum ImageCaptureType +{ + /// + /// BitBlt (Windows 7 and up) + /// + BitBlt, + + /// + /// Windows 10 (1903 and up) + /// + WindowsGraphicsCapture, +} diff --git a/src/GenshinFishingToy/Core/ImageJigging.cs b/src/GenshinFishingToy/Core/ImageJigging.cs index a8a2588..10f3f40 100644 --- a/src/GenshinFishingToy/Core/ImageJigging.cs +++ b/src/GenshinFishingToy/Core/ImageJigging.cs @@ -29,11 +29,19 @@ internal class ImageJigging public bool IsRunning { get; protected set; } = false; + private Preview previewForm = null!; + public ImageJigging() { timerCapture.Tick += Tick; timerCapture.Interval = Convert.ToInt32(1000d / frameRate); timerCapture.Start(); + previewForm = new(); + + if (Settings.ShowRecognitionCapture.Get()) + { + previewForm.Show(); + } } public async Task StartFishing() @@ -47,6 +55,7 @@ public async Task StopFishing() IsRunning = false; findFishBoxTips = false; isFishingProcess = false; + ImageCapture.Teardown(); } public async void Tick(object? sender, EventArgs e) @@ -102,6 +111,7 @@ public async void Tick(object? sender, EventArgs e) ImageCapture.Setup(rect); using Bitmap captured = ImageCapture.Capture(window.Hwnd); + previewForm.SetImage(captured); rects = ImageRecognition.GetRect(captured, Settings.ShowRecognitionJigging); window.MotionArea.Rects = rects?.ToArray() ?? Array.Empty(); diff --git a/src/GenshinFishingToy/FodyWeavers.xml b/src/GenshinFishingToy/FodyWeavers.xml new file mode 100644 index 0000000..5029e70 --- /dev/null +++ b/src/GenshinFishingToy/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/GenshinFishingToy/FodyWeavers.xsd b/src/GenshinFishingToy/FodyWeavers.xsd new file mode 100644 index 0000000..05e92c1 --- /dev/null +++ b/src/GenshinFishingToy/FodyWeavers.xsd @@ -0,0 +1,141 @@ + + + + + + + + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with line breaks. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with line breaks. + + + + + The order of preloaded assemblies, delimited with line breaks. + + + + + + This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. + + + + + Controls if .pdbs for reference assemblies are also embedded. + + + + + Controls if runtime assemblies are also embedded. + + + + + Controls whether the runtime assemblies are embedded with their full path or only with their assembly name. + + + + + Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. + + + + + As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. + + + + + Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. + + + + + Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with |. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with |. + + + + + The order of preloaded assemblies, delimited with |. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/src/GenshinFishingToy/GenshinFishingToy.csproj b/src/GenshinFishingToy/GenshinFishingToy.csproj index 49088c4..87a716c 100644 --- a/src/GenshinFishingToy/GenshinFishingToy.csproj +++ b/src/GenshinFishingToy/GenshinFishingToy.csproj @@ -1,74 +1,85 @@  - - WinExe - net6.0-windows10.0.18362.0 - enable - true - true - Resources\favicon.ico - app.manifest - x64 - 1.4.1 - 1.4.1 - $(VersionPrefix)1.4.1 - GenshinMatrix - GenshinMatrix - True - - + + WinExe + net48 + enable + 11.0 + True + true + Resources\favicon.ico + app.manifest + x64 + 1.5.0 + 1.5.0 + $(VersionPrefix)1.5.0 + GenshinMatrix + GenshinMatrix + True + + - - 1701;1702;1998; - $(DefineConstants)TRACE; - + + 1701;1702;1998; + $(DefineConstants)TRACE; + - - 1701;1702;1998; - $(DefineConstants)TRACE; - + + 1701;1702;1998; + $(DefineConstants)TRACE; + - - - - - - - - + + + + + + + + - - - + + + - - - - - - - - - - - - - - - - - + + + + all + compile; runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + - - - + + + - - - - - - - + + + + + + + + + + + + + diff --git a/src/GenshinFishingToy/Models/Settings.cs b/src/GenshinFishingToy/Models/Settings.cs index cce15d2..4c99004 100644 --- a/src/GenshinFishingToy/Models/Settings.cs +++ b/src/GenshinFishingToy/Models/Settings.cs @@ -11,7 +11,9 @@ public class Settings public static SettingsDefinition<(int, int, int, int)> JigRect { get; } = new(nameof(JigRect), (100, 100, 450, 100)); public static SettingsDefinition FullScreenWhenSaved { get; } = new(nameof(FullScreenWhenSaved), false); public static SettingsDefinition Lock { get; } = new(nameof(Lock), false); + public static SettingsDefinition CaptureType { get; } = new(nameof(CaptureType), "WindowsGraphicsCapture"); public static SettingsDefinition AutoLifting { get; } = new(nameof(AutoLifting), true); + public static SettingsDefinition ShowRecognitionCapture { get; } = new(nameof(ShowRecognitionCapture), false); public static SettingsDefinition ShowRecognitionJigging { get; } = new(nameof(ShowRecognitionJigging), false); public static SettingsDefinition ShowRecognitionLifting { get; } = new(nameof(ShowRecognitionLifting), false); } diff --git a/src/GenshinFishingToy/Resources/Languages/en-us.xaml b/src/GenshinFishingToy/Resources/Languages/en-us.xaml index 64ca6c0..9b61daf 100644 --- a/src/GenshinFishingToy/Resources/Languages/en-us.xaml +++ b/src/GenshinFishingToy/Resources/Languages/en-us.xaml @@ -19,12 +19,16 @@ Language We only support running on an OS version higher than {0}. Your OS version is {1}. Auto Lifting + Show captupe recognition (Debug) Show jigging recognition (Debug) Show lifting recognition (Debug) Jigging recognition debug Lifting recognition debug Lock fishing region Reset fishing region + Image Capture Type + BitBlt (Windows 7 and up) + Windows 10 (1903 and up) Not started Started Open Config File diff --git a/src/GenshinFishingToy/Resources/Languages/jp.xaml b/src/GenshinFishingToy/Resources/Languages/jp.xaml index dcfca44..f53a0dc 100644 --- a/src/GenshinFishingToy/Resources/Languages/jp.xaml +++ b/src/GenshinFishingToy/Resources/Languages/jp.xaml @@ -19,12 +19,16 @@ 言語 本プログラムはOSバージョンが{0}もしくはそれより高いOSバージョンのみサポートしています。あなたのOSバージョンは{1}です。 自動リフティング + キャプチャ画像を表示(デバッグ) 釣りの識別を表示(デバッグ) リフティングの識別を表示(デバッグ) 釣り識別デバッグ リフティング識別デバッグ 釣り枠をロック 釣り枠をリセット + 画像キャプチャモード + BitBlt (Windows 7 以降) + Windows 10 (1903以降) スタート待ち スタート済み 配置ファイルを開く diff --git a/src/GenshinFishingToy/Resources/Languages/zh-cn.xaml b/src/GenshinFishingToy/Resources/Languages/zh-cn.xaml index 6e40f59..2fd2507 100644 --- a/src/GenshinFishingToy/Resources/Languages/zh-cn.xaml +++ b/src/GenshinFishingToy/Resources/Languages/zh-cn.xaml @@ -19,12 +19,16 @@ 语言 我们仅支持系统版本高于{0}。您的系统版本为{1}。 自动提竿 + 显示采集识别(调试) 显示饵钓识别(调试) 显示提竿识别(调试) 饵钓识别调试 提竿识别调试 锁定钓鱼框 重置钓鱼框 + 图像采集模式 + BitBlt (Windows 7 或更新版本) + Windows 10 (1903或更新版本) 未启动 已启动 打开配置文件 diff --git a/src/GenshinFishingToy/ViewModels/MainViewModel.cs b/src/GenshinFishingToy/ViewModels/MainViewModel.cs index 6194e45..b9d8354 100644 --- a/src/GenshinFishingToy/ViewModels/MainViewModel.cs +++ b/src/GenshinFishingToy/ViewModels/MainViewModel.cs @@ -15,7 +15,7 @@ namespace GenshinFishingToy.ViewModels; -public class MainViewModel : ObservableRecipient +public partial class MainViewModel : ObservableRecipient { public MainWindow Source { get; set; } = null!; internal ImageJigging jigging = new(); @@ -55,6 +55,16 @@ public bool OptionShowRecognitionJigging } } + public bool OptionShowRecognitionCapture + { + get => Settings.ShowRecognitionCapture; + set + { + Settings.ShowRecognitionCapture.Set(value); + SettingsManager.Save(); + } + } + public bool OptionShowRecognitionLifting { get => Settings.ShowRecognitionLifting; @@ -213,4 +223,24 @@ public async Task StopFishing() { await jigging.StopFishing(); } + + [ObservableProperty] + private bool isImageCaptureTypeIsBitBlt = Settings.CaptureType.Get().Equals(nameof(ImageCaptureType.BitBlt)); + + [ObservableProperty] + private bool isImageCaptureTypeIsWindowsGraphicsCapture = Settings.CaptureType.Get().Equals(nameof(ImageCaptureType.WindowsGraphicsCapture)); + + [RelayCommand] + public void SetImageCaptureType(string captureType) + { + if (jigging.IsRunning) + { + IsImageCaptureTypeIsBitBlt = Settings.CaptureType.Get().Equals(nameof(ImageCaptureType.BitBlt)); + IsImageCaptureTypeIsWindowsGraphicsCapture = Settings.CaptureType.Get().Equals(nameof(ImageCaptureType.WindowsGraphicsCapture)); + return; + } + ImageCapture.CaptureType = (ImageCaptureType)Enum.Parse(typeof(ImageCaptureType), captureType); + Settings.CaptureType.Set(ImageCapture.CaptureType.ToString()); + SettingsManager.Save(); + } } diff --git a/src/GenshinFishingToy/ViewModels/NotifyIconViewModel.cs b/src/GenshinFishingToy/ViewModels/NotifyIconViewModel.cs index 1f7e4c0..d5988b6 100644 --- a/src/GenshinFishingToy/ViewModels/NotifyIconViewModel.cs +++ b/src/GenshinFishingToy/ViewModels/NotifyIconViewModel.cs @@ -72,7 +72,7 @@ public static void OnNotificationActivated(ToastNotificationActivatedEventArgsCo foreach (KeyValuePair arg in args) { - (string k, string v) = arg; + (string k, string v) = (arg.Key, arg.Value); if (k == "" && v == "") { diff --git a/src/GenshinFishingToy/Views/MainWindow.xaml b/src/GenshinFishingToy/Views/MainWindow.xaml index 207fcee..c297498 100644 --- a/src/GenshinFishingToy/Views/MainWindow.xaml +++ b/src/GenshinFishingToy/Views/MainWindow.xaml @@ -5,6 +5,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:GenshinFishingToy.Views" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" + xmlns:ui="http://schemas.modernwpf.com/2019" xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" xmlns:main="clr-namespace:GenshinFishingToy" mc:Ignorable="d" Closed="MainWindowClosed" @@ -119,6 +120,11 @@ + + + + + @@ -130,6 +136,26 @@ + + + + + + + + + + + + + + + + diff --git a/src/GenshinFishingToy/Views/PreviewForm.Designer.cs b/src/GenshinFishingToy/Views/PreviewForm.Designer.cs new file mode 100644 index 0000000..0fac44c --- /dev/null +++ b/src/GenshinFishingToy/Views/PreviewForm.Designer.cs @@ -0,0 +1,64 @@ +namespace GenshinFishingToy.Views +{ + partial class Preview + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // pictureBox1 + // + this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.pictureBox1.BackColor = System.Drawing.Color.White; + this.pictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.pictureBox1.Location = new System.Drawing.Point(0, 0); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(564, 200); + this.pictureBox1.TabIndex = 2; + this.pictureBox1.TabStop = false; + // + // Preview + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.ClientSize = new System.Drawing.Size(564, 200); + this.Controls.Add(this.pictureBox1); + this.Name = "Preview"; + this.Text = "Preview"; + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.PictureBox pictureBox1; + } +} \ No newline at end of file diff --git a/src/GenshinFishingToy/Views/PreviewForm.cs b/src/GenshinFishingToy/Views/PreviewForm.cs new file mode 100644 index 0000000..36f061a --- /dev/null +++ b/src/GenshinFishingToy/Views/PreviewForm.cs @@ -0,0 +1,37 @@ +using GenshinFishingToy.Core; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Security.Policy; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Windows.Resources; + +namespace GenshinFishingToy.Views; + +public partial class Preview : Form +{ + public Preview() + { + InitializeComponent(); + + using Stream stream = ResourceUtils.GetStream("pack://application:,,,/GenshinFishingToy;component/Resources/favicon.ico"); + Icon = new Icon(stream); + } + + public void SetImage(Bitmap bitmap) + { + try + { + pictureBox1.Image = bitmap?.Clone() as Bitmap; + } + catch + { + } + } +} diff --git a/src/GenshinFishingToy/app.manifest b/src/GenshinFishingToy/app.manifest index 4f461d9..904c8ca 100644 --- a/src/GenshinFishingToy/app.manifest +++ b/src/GenshinFishingToy/app.manifest @@ -28,19 +28,19 @@ and Windows will automatically select the most compatible environment. --> - + - + - + - + - + diff --git a/src/GenshinFishingToySetup/GenshinFishingToySetup.assets.cache b/src/GenshinFishingToySetup/GenshinFishingToySetup.assets.cache new file mode 100644 index 0000000..f65ec4b Binary files /dev/null and b/src/GenshinFishingToySetup/GenshinFishingToySetup.assets.cache differ diff --git a/src/GenshinFishingToySetup/GenshinFishingToySetup.wapproj b/src/GenshinFishingToySetup/GenshinFishingToySetup.wapproj index 6b07bdd..3b152e8 100644 --- a/src/GenshinFishingToySetup/GenshinFishingToySetup.wapproj +++ b/src/GenshinFishingToySetup/GenshinFishingToySetup.wapproj @@ -105,7 +105,7 @@ - + diff --git a/src/GenshinFishingToySetup/Package.appxmanifest b/src/GenshinFishingToySetup/Package.appxmanifest index df753eb..66673b5 100644 --- a/src/GenshinFishingToySetup/Package.appxmanifest +++ b/src/GenshinFishingToySetup/Package.appxmanifest @@ -4,7 +4,8 @@ xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" - IgnorableNamespaces="uap rescap"> + xmlns:uap6="http://schemas.microsoft.com/appx/manifest/uap/windows10/6" + IgnorableNamespaces="uap rescap uap6"> + diff --git a/src/WindowsGraphicsCapture/.gitattributes b/src/WindowsGraphicsCapture/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/src/WindowsGraphicsCapture/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/src/WindowsGraphicsCapture/.gitignore b/src/WindowsGraphicsCapture/.gitignore new file mode 100644 index 0000000..3c4efe2 --- /dev/null +++ b/src/WindowsGraphicsCapture/.gitignore @@ -0,0 +1,261 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc \ No newline at end of file diff --git a/src/WindowsGraphicsCapture/CaptureSampleCore/BasicCapture.cs b/src/WindowsGraphicsCapture/CaptureSampleCore/BasicCapture.cs new file mode 100644 index 0000000..43241cd --- /dev/null +++ b/src/WindowsGraphicsCapture/CaptureSampleCore/BasicCapture.cs @@ -0,0 +1,206 @@ +// --------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// --------------------------------------------------------------------------------- + +using Composition.WindowsRuntimeHelpers; +using SharpDX; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using System; +using System.Drawing; +using Windows.Graphics; +using Windows.Graphics.Capture; +using Windows.Graphics.DirectX; +using Windows.Graphics.DirectX.Direct3D11; +using Windows.UI.Composition; + +namespace CaptureSampleCore +{ + public class BasicCapture : IDisposable + { + public Bitmap Bitmap => bitmap; + + private GraphicsCaptureItem item; + private Direct3D11CaptureFramePool framePool; + private GraphicsCaptureSession session; + private SizeInt32 lastSize; + + private IDirect3DDevice device; + private SharpDX.Direct3D11.Device d3dDevice; + private SharpDX.DXGI.SwapChain1 swapChain; + public Bitmap bitmap = null!; + + public BasicCapture(IDirect3DDevice d, GraphicsCaptureItem i) + { + item = i; + device = d; + d3dDevice = Direct3D11Helper.CreateSharpDXDevice(device); + + var dxgiFactory = new SharpDX.DXGI.Factory2(); + var description = new SharpDX.DXGI.SwapChainDescription1() + { + Width = item.Size.Width, + Height = item.Size.Height, + Format = SharpDX.DXGI.Format.B8G8R8A8_UNorm, + Stereo = false, + SampleDescription = new SharpDX.DXGI.SampleDescription() + { + Count = 1, + Quality = 0 + }, + Usage = SharpDX.DXGI.Usage.RenderTargetOutput, + BufferCount = 2, + Scaling = SharpDX.DXGI.Scaling.Stretch, + SwapEffect = SharpDX.DXGI.SwapEffect.FlipSequential, + AlphaMode = SharpDX.DXGI.AlphaMode.Premultiplied, + Flags = SharpDX.DXGI.SwapChainFlags.None + }; + swapChain = new SharpDX.DXGI.SwapChain1(dxgiFactory, d3dDevice, ref description); + + framePool = Direct3D11CaptureFramePool.Create( + device, + DirectXPixelFormat.B8G8R8A8UIntNormalized, + 2, + i.Size); + session = framePool.CreateCaptureSession(i); + lastSize = i.Size; + + framePool.FrameArrived += OnFrameArrived; + } + + public void Dispose() + { + session?.Dispose(); + framePool?.Dispose(); + swapChain?.Dispose(); + d3dDevice?.Dispose(); + } + + public void StartCapture() + { + session.StartCapture(); + } + + public ICompositionSurface CreateSurface(Compositor compositor) + { + return compositor.CreateCompositionSurfaceForSwapChain(swapChain); + } + + private void OnFrameArrived(Direct3D11CaptureFramePool sender, object args) + { + var newSize = false; + + using (var frame = sender.TryGetNextFrame()) + { + if (frame.ContentSize.Width != lastSize.Width || + frame.ContentSize.Height != lastSize.Height) + { + // The thing we have been capturing has changed size. + // We need to resize the swap chain first, then blit the pixels. + // After we do that, retire the frame and then recreate the frame pool. + newSize = true; + lastSize = frame.ContentSize; + swapChain.ResizeBuffers( + 2, + lastSize.Width, + lastSize.Height, + SharpDX.DXGI.Format.B8G8R8A8_UNorm, + SharpDX.DXGI.SwapChainFlags.None); + } + + using (var backBuffer = swapChain.GetBackBuffer(0)) + using (var bitmap = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface)) + { + d3dDevice.ImmediateContext.CopyResource(bitmap, backBuffer); + + var sysBitmap = ConvertTexture2DToBitmap(bitmap); + this.bitmap?.Dispose(); + this.bitmap = sysBitmap; + } + + } // Retire the frame. + + swapChain.Present(0, SharpDX.DXGI.PresentFlags.None); + + if (newSize) + { + framePool.Recreate( + device, + DirectXPixelFormat.B8G8R8A8UIntNormalized, + 2, + lastSize); + } + } + + + private Bitmap ConvertTexture2DToBitmap(Texture2D texture) + { + var description = texture.Description; + if (description.Format != SharpDX.DXGI.Format.B8G8R8A8_UNorm) + { + throw new ArgumentException("Texture format must be B8G8R8A8_UNorm", nameof(texture)); + } + + var stagingDescription = new Texture2DDescription + { + Width = description.Width, + Height = description.Height, + MipLevels = 1, + ArraySize = 1, + Format = description.Format, + Usage = ResourceUsage.Staging, + SampleDescription = new SampleDescription(1, 0), + BindFlags = BindFlags.None, + CpuAccessFlags = CpuAccessFlags.Read, + OptionFlags = ResourceOptionFlags.None + }; + + var stagingTexture = new SharpDX.Direct3D11.Texture2D(texture.Device, stagingDescription); + texture.Device.ImmediateContext.CopyResource(texture, stagingTexture); + + DataBox dataBox = texture.Device.ImmediateContext.MapSubresource(stagingTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None); + + var bitmap = new System.Drawing.Bitmap(description.Width, description.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + var bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, description.Width, description.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, bitmap.PixelFormat); + + IntPtr srcPtr = dataBox.DataPointer; + IntPtr destPtr = bitmapData.Scan0; + int bytesWidth = description.Width * 4; + int srcStride = dataBox.RowPitch; + int destStride = bitmapData.Stride; + + for (int row = 0; row < description.Height; row++) + { + Utilities.CopyMemory(destPtr, srcPtr, bytesWidth); + srcPtr = IntPtr.Add(srcPtr, srcStride); + destPtr = IntPtr.Add(destPtr, destStride); + } + + bitmap.UnlockBits(bitmapData); + texture.Device.ImmediateContext.UnmapSubresource(stagingTexture, 0); + stagingTexture.Dispose(); + + return bitmap; + } + } +} diff --git a/src/WindowsGraphicsCapture/CaptureSampleCore/BasicSampleApplication.cs b/src/WindowsGraphicsCapture/CaptureSampleCore/BasicSampleApplication.cs new file mode 100644 index 0000000..f871d4f --- /dev/null +++ b/src/WindowsGraphicsCapture/CaptureSampleCore/BasicSampleApplication.cs @@ -0,0 +1,106 @@ +// --------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// --------------------------------------------------------------------------------- + +using Composition.WindowsRuntimeHelpers; +using System; +using System.Numerics; +using System.Text.RegularExpressions; +using Windows.Graphics.Capture; +using Windows.Graphics.DirectX.Direct3D11; +using Windows.UI.Composition; + +namespace CaptureSampleCore +{ + public class BasicSampleApplication : IDisposable + { + public BasicCapture Capture => capture; + + private Compositor compositor; + private ContainerVisual root; + + private SpriteVisual content; + private CompositionSurfaceBrush brush; + + private IDirect3DDevice device; + private BasicCapture capture; + + public BasicSampleApplication(Compositor c) + { + compositor = c; + device = Direct3D11Helper.CreateDevice(); + + // Setup the root. + root = compositor.CreateContainerVisual(); + root.RelativeSizeAdjustment = Vector2.One; + + // Setup the content. + brush = compositor.CreateSurfaceBrush(); + brush.HorizontalAlignmentRatio = 0.5f; + brush.VerticalAlignmentRatio = 0.5f; + brush.Stretch = CompositionStretch.Uniform; + + var shadow = compositor.CreateDropShadow(); + shadow.Mask = brush; + + content = compositor.CreateSpriteVisual(); + content.AnchorPoint = new Vector2(0.5f); + content.RelativeOffsetAdjustment = new Vector3(0.5f, 0.5f, 0); + content.RelativeSizeAdjustment = Vector2.One; + content.Size = new Vector2(-80, -80); + content.Brush = brush; + content.Shadow = shadow; + root.Children.InsertAtTop(content); + } + + public Visual Visual => root; + + public void Dispose() + { + StopCapture(); + compositor = null; + root.Dispose(); + content.Dispose(); + brush.Dispose(); + device.Dispose(); + } + + public void StartCaptureFromItem(GraphicsCaptureItem item) + { + StopCapture(); + capture = new BasicCapture(device, item); + + var surface = capture.CreateSurface(compositor); + brush.Surface = surface; + + capture.StartCapture(); + } + + public void StopCapture() + { + capture?.Dispose(); + capture = null!; + brush.Surface = null; + } + } +} diff --git a/src/WindowsGraphicsCapture/CaptureSampleCore/CaptureSampleCore.csproj b/src/WindowsGraphicsCapture/CaptureSampleCore/CaptureSampleCore.csproj new file mode 100644 index 0000000..e770579 --- /dev/null +++ b/src/WindowsGraphicsCapture/CaptureSampleCore/CaptureSampleCore.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + 11.0 + x64 + + + + + + + + + + + + + diff --git a/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/CaptureHelper.cs b/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/CaptureHelper.cs new file mode 100644 index 0000000..51f8cf3 --- /dev/null +++ b/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/CaptureHelper.cs @@ -0,0 +1,91 @@ +// --------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// --------------------------------------------------------------------------------- + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Graphics.Capture; + +namespace Composition.WindowsRuntimeHelpers +{ + public static class CaptureHelper + { + 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 = (IInitializeWithWindow)(object)picker; + interop.Initialize(hwnd); + } + + public static GraphicsCaptureItem CreateItemForWindow(IntPtr hwnd) + { + var factory = WindowsRuntimeMarshal.GetActivationFactory(typeof(GraphicsCaptureItem)); + var interop = (IGraphicsCaptureItemInterop)factory; + var temp = typeof(GraphicsCaptureItem); + var itemPointer = interop.CreateForWindow(hwnd, GraphicsCaptureItemGuid); + var item = Marshal.GetObjectForIUnknown(itemPointer) as GraphicsCaptureItem; + Marshal.Release(itemPointer); + + return item; + } + + 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; + } + } +} diff --git a/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/Composition.WindowsRuntimeHelpers.csproj b/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/Composition.WindowsRuntimeHelpers.csproj new file mode 100644 index 0000000..63d6811 --- /dev/null +++ b/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/Composition.WindowsRuntimeHelpers.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + 11.0 + x64 + + + + + + + + + diff --git a/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/CompositionHelper.cs b/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/CompositionHelper.cs new file mode 100644 index 0000000..893c197 --- /dev/null +++ b/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/CompositionHelper.cs @@ -0,0 +1,72 @@ +// --------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// --------------------------------------------------------------------------------- + +using System; +using System.Runtime.InteropServices; +using Windows.UI.Composition; + +namespace Composition.WindowsRuntimeHelpers +{ + public static class CompositionHelper + { + [ComImport] + [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [ComVisible(true)] + interface ICompositorInterop + { + ICompositionSurface CreateCompositionSurfaceForHandle( + IntPtr swapChain); + + ICompositionSurface CreateCompositionSurfaceForSwapChain( + IntPtr swapChain); + + CompositionGraphicsDevice CreateGraphicsDevice( + IntPtr renderingDevice); + } + + [ComImport] + [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [ComVisible(true)] + interface ICompositorDesktopInterop + { + Windows.UI.Composition.Desktop.DesktopWindowTarget CreateDesktopWindowTarget( + IntPtr hwnd, + bool isTopmost); + } + + public static CompositionTarget CreateDesktopWindowTarget(this Compositor compositor, IntPtr hwnd, bool isTopmost) + { + var desktopInterop = (ICompositorDesktopInterop)((object)compositor); + return desktopInterop.CreateDesktopWindowTarget(hwnd, isTopmost); + } + + public static ICompositionSurface CreateCompositionSurfaceForSwapChain(this Compositor compositor, SharpDX.DXGI.SwapChain1 swapChain) + { + var interop = (ICompositorInterop)(object)compositor; + return interop.CreateCompositionSurfaceForSwapChain(swapChain.NativePointer); + } + } +} diff --git a/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/CoreMessagingHelper.cs b/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/CoreMessagingHelper.cs new file mode 100644 index 0000000..8ef41e2 --- /dev/null +++ b/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/CoreMessagingHelper.cs @@ -0,0 +1,83 @@ +// --------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// --------------------------------------------------------------------------------- + +using System; +using System.Runtime.InteropServices; +using Windows.System; + +namespace Composition.WindowsRuntimeHelpers +{ + public static class CoreMessagingHelper + { + enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE + { + DQTAT_COM_NONE = 0, + DQTAT_COM_ASTA = 1, + DQTAT_COM_STA = 2 + } + + enum DISPATCHERQUEUE_THREAD_TYPE + { + DQTYPE_THREAD_DEDICATED = 1, + DQTYPE_THREAD_CURRENT = 2 + } + + struct DispatcherQueueOptions + { + public int dwSize; + public DISPATCHERQUEUE_THREAD_TYPE threadType; + public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType; + } + + [DllImport( + "CoreMessaging.dll", + EntryPoint = "CreateDispatcherQueueController", + SetLastError = true, + CharSet = CharSet.Unicode, + ExactSpelling = true, + CallingConvention = CallingConvention.StdCall + )] + static extern UInt32 CreateDispatcherQueueController(DispatcherQueueOptions options, out IntPtr dispatcherQueueController); + + public static DispatcherQueueController CreateDispatcherQueueControllerForCurrentThread() + { + var options = new DispatcherQueueOptions + { + dwSize = Marshal.SizeOf(), + threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT, + apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_NONE + }; + + DispatcherQueueController controller = null; + uint hr = CreateDispatcherQueueController(options, out IntPtr controllerPointer); + if (hr == 0) + { + controller = Marshal.GetObjectForIUnknown(controllerPointer) as DispatcherQueueController; + Marshal.Release(controllerPointer); + } + + return controller; + } + } +} diff --git a/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/Direct3D11Helper.cs b/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/Direct3D11Helper.cs new file mode 100644 index 0000000..f4f8967 --- /dev/null +++ b/src/WindowsGraphicsCapture/Composition.WindowsRuntimeHelpers/Direct3D11Helper.cs @@ -0,0 +1,138 @@ +// --------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// --------------------------------------------------------------------------------- + +using System; +using System.Runtime.InteropServices; +using Windows.Graphics.DirectX.Direct3D11; + +namespace Composition.WindowsRuntimeHelpers +{ + public static class Direct3D11Helper + { + 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 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() + { + 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()) + { + // Wrap the native device using a WinRT interop object. + uint hr = CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.NativePointer, out IntPtr pUnknown); + + if (hr == 0) + { + device = Marshal.GetObjectForIUnknown(pUnknown) as IDirect3DDevice; + 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()) + { + // 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); + } + } + + 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 = (IDirect3DDxgiInterfaceAccess)surface; + var d3dPointer = access.GetInterface(ID3D11Texture2D); + var d3dSurface = new SharpDX.Direct3D11.Texture2D(d3dPointer); + return d3dSurface; + } + } +} diff --git a/src/WindowsGraphicsCapture/LICENSE b/src/WindowsGraphicsCapture/LICENSE new file mode 100644 index 0000000..87fe877 --- /dev/null +++ b/src/WindowsGraphicsCapture/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Robert Mikhayelyan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/WindowsGraphicsCapture/README.md b/src/WindowsGraphicsCapture/README.md new file mode 100644 index 0000000..5c35707 --- /dev/null +++ b/src/WindowsGraphicsCapture/README.md @@ -0,0 +1,26 @@ +# WPF Screen Capture + +This sample demonstrates how to use [Windows.Graphics.Capture](https://docs.microsoft.com/uwp/api/windows.graphics.capture) APIs for displays and windows in a WPF app. It also shows how to launch the system picker, which can manage window enumeration and capture selection for you. + + ![Capture Selection](Images/WPFCapture.png) + +> NOTE: Minimized windows are enumerated but not captured. + +## Run the sample + +- Visual Studio 2017 or later - [Get a free copy of Visual Studio](http://go.microsoft.com/fwlink/?LinkID=280676) +- .NET Framework 4.7.2 or later +- Windows 10 version 1903 or later +- Windows 10 SDK 18362 or later - [Get the SDK](https://developer.microsoft.com/windows/downloads/windows-10-sdk) + +## Code at at glance + +This sample uses new APIs available in Windows 10 version 1903, SDK 18362: + + - `CreateForWindow` (HWND) and `CreateForMonitor` (HMON) APIs are in the Windows.Graphics.Capture.Interop.h header. + +## See also + +[Main ReadMe for this repo](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples) + +API reference: [Windows.Graphics.Capture Namespace](https://docs.microsoft.com/uwp/api/windows.graphics.capture) diff --git a/src/WindowsGraphicsCapture/ScreenCapture.sln b/src/WindowsGraphicsCapture/ScreenCapture.sln new file mode 100644 index 0000000..f6d5926 --- /dev/null +++ b/src/WindowsGraphicsCapture/ScreenCapture.sln @@ -0,0 +1,79 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.421 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenCapture", "ScreenCapture\ScreenCapture.csproj", "{4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CaptureSampleCore", "CaptureSampleCore\CaptureSampleCore.csproj", "{502B19A1-292A-4E7F-9C19-B16CA42F1F82}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Composition.WindowsRuntimeHelpers", "Composition.WindowsRuntimeHelpers\Composition.WindowsRuntimeHelpers.csproj", "{EC969287-3D35-410A-806C-0BAAA7564911}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Debug|ARM.ActiveCfg = Debug|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Debug|ARM.Build.0 = Debug|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Debug|x64.ActiveCfg = Debug|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Debug|x64.Build.0 = Debug|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Debug|x86.ActiveCfg = Debug|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Debug|x86.Build.0 = Debug|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Release|Any CPU.Build.0 = Release|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Release|ARM.ActiveCfg = Release|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Release|ARM.Build.0 = Release|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Release|x64.ActiveCfg = Release|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Release|x64.Build.0 = Release|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Release|x86.ActiveCfg = Release|Any CPU + {4DDD37B1-E3E8-46C1-BDE9-3039A93DAF27}.Release|x86.Build.0 = Release|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Debug|ARM.ActiveCfg = Debug|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Debug|ARM.Build.0 = Debug|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Debug|x64.ActiveCfg = Debug|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Debug|x64.Build.0 = Debug|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Debug|x86.ActiveCfg = Debug|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Debug|x86.Build.0 = Debug|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Release|Any CPU.Build.0 = Release|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Release|ARM.ActiveCfg = Release|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Release|ARM.Build.0 = Release|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Release|x64.ActiveCfg = Release|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Release|x64.Build.0 = Release|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Release|x86.ActiveCfg = Release|Any CPU + {502B19A1-292A-4E7F-9C19-B16CA42F1F82}.Release|x86.Build.0 = Release|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Debug|ARM.ActiveCfg = Debug|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Debug|ARM.Build.0 = Debug|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Debug|x64.ActiveCfg = Debug|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Debug|x64.Build.0 = Debug|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Debug|x86.ActiveCfg = Debug|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Debug|x86.Build.0 = Debug|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Release|Any CPU.Build.0 = Release|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Release|ARM.ActiveCfg = Release|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Release|ARM.Build.0 = Release|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Release|x64.ActiveCfg = Release|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Release|x64.Build.0 = Release|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Release|x86.ActiveCfg = Release|Any CPU + {EC969287-3D35-410A-806C-0BAAA7564911}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2EBE8D8A-1C43-4C9B-BD8D-CBB938E8F1FE} + EndGlobalSection +EndGlobal diff --git a/src/WindowsGraphicsCapture/ScreenCapture/AssemblyInfo.cs b/src/WindowsGraphicsCapture/ScreenCapture/AssemblyInfo.cs new file mode 100644 index 0000000..eccd10e --- /dev/null +++ b/src/WindowsGraphicsCapture/ScreenCapture/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using System.Windows; + +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: ComVisible(false)] +[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] diff --git a/src/WindowsGraphicsCapture/ScreenCapture/GraphicsCapture.cs b/src/WindowsGraphicsCapture/ScreenCapture/GraphicsCapture.cs new file mode 100644 index 0000000..c256cdb --- /dev/null +++ b/src/WindowsGraphicsCapture/ScreenCapture/GraphicsCapture.cs @@ -0,0 +1,123 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; + +namespace WPFCaptureSample; + +public static class GraphicsCapture +{ + public static MainWindow CaptureWindow; + public static Bitmap CaptureBitmap => CaptureWindow.Sample?.Capture?.Bitmap!; + + public static Bitmap Capture(int x, int y, int w, int h, IntPtr? hwnd = null) + { + if (hwnd == null || hwnd == IntPtr.Zero) + { + return null!; + } + + RECT lpRect = default; + GetWindowRect(hwnd.Value, ref lpRect); + float controlsWidth = Math.Max(lpRect.Right - lpRect.Left, 2); + + try + { + if (CaptureWindow == null) + { + CaptureWindow ??= new(); + CaptureWindow.InitComposition(controlsWidth); + CaptureWindow.StartHwndCapture(hwnd!.Value); + } + if (CaptureWindow != null) + { + if (CaptureWindow.Sample?.Capture == null) + { + CaptureWindow.InitComposition(controlsWidth); + CaptureWindow.StartHwndCapture(hwnd!.Value); + } + if (CaptureWindow.ControlsWidth != controlsWidth) + { + CaptureWindow.StopCapture(); + CaptureWindow.InitComposition(controlsWidth); + } + } + + if (CaptureBitmap != null) + { + return CropBitmap(CaptureBitmap, x, y, w, h); + } + } + catch + { + } + return null!; + } + + public static void Uncapture() + { + try + { + CaptureWindow?.StopCapture(); + } + catch + { + } + } + + private static unsafe Bitmap CropBitmap(Bitmap source, int x, int y, int width, int height) + { + if (x < 0 || x + width > source.Width || y < 0 || y + height > source.Height) + { + return source; + } + + Bitmap target = new(width, height, PixelFormat.Format32bppArgb); + + BitmapData sourceData = null!; + BitmapData targetData = null!; + + try + { + sourceData = source.LockBits(new Rectangle(x, y, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + targetData = target.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + + byte* sourcePtr = (byte*)sourceData.Scan0; + byte* targetPtr = (byte*)targetData.Scan0; + + int bytesPerRow = width * 4; + + for (int i = 0; i < height; i++) + { + byte* sourceRow = sourcePtr + i * sourceData.Stride; + byte* targetRow = targetPtr + i * targetData.Stride; + + for (int j = 0; j < bytesPerRow; j++) + { + targetRow[j] = sourceRow[j]; + } + } + } + finally + { + if (sourceData != null) + source.UnlockBits(sourceData); + if (targetData != null) + target.UnlockBits(targetData); + } + return target; + } + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); + + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } +} diff --git a/src/WindowsGraphicsCapture/ScreenCapture/MainWindow.xaml b/src/WindowsGraphicsCapture/ScreenCapture/MainWindow.xaml new file mode 100644 index 0000000..b3becfd --- /dev/null +++ b/src/WindowsGraphicsCapture/ScreenCapture/MainWindow.xaml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + +