Rehber C# Lowlevel inject kütüphanesi Winjoys nasıl kullanılır?

Windows'taki user32.dll kütüphanesinden yararlanarak C# ile kernel level inject yapabilmemizi sağlayan Winjoys artık public! Keyboard, Mouse, (WinRT: Gamepad, Touch, Pen) inputları göndermemize yarar.

Nasıl kullanılır?​

GitHub - BayramReisbirligi/Winjoys: Winjoys Adresinden Winjoys'u indiriyoruz.
Ben size göstermek amaçlı .NET 9 C# WinUI 3 oluşturuyorum.
Öncelikle bu projenin .NET 9 ve Minimum Windows 19041 sürümü gerektiğini size bildirmek isterim. İsterseniz kendiniz fork atıp eski versiyonlara da uyumlu hâle getirebilirsiniz. Ben yenilikçi olduğumdan .NET 9'u tercih ettim. Bazı özellikler 19041 ve .NET 9 altında da çalışabilir.
1759434142456.png

Winjoys kütüphanesini ekliyoruz:
1759434204499.png

NOT: Press ve Click olayları için Windelay kütüphanesi gerekebilir. İlgili rehber için: Rehber: C#: Hassas bekleme kütüphanesi Windelay nasıl kullanılır?
Sonuç:
1759434000346.png

MainWindow.xaml kodları:
XML:
<Window
    x:Class="DllTester.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DllTester"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Title="Winhook WinUI 3 Test">
    <Window.SystemBackdrop>
        <MicaBackdrop />
    </Window.SystemBackdrop>
    <Grid>
        <ListBox x:Name="MyListBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
    </Grid>
</Window>
MainWindow.xaml.cs kodları:
C#:
using static ReisProduction.Windelay.Models.DelayExecutor;
using ReisProduction.Windelay.Utilities.Enums;
using ReisProduction.Winhook.Utilities.Enums;
using ReisProduction.Windelay.Utilities;
using ReisProduction.Winhook.Utilities;
using ReisProduction.Winjoys.Utilities;
using System.Runtime.InteropServices;
using ReisProduction.Winhook.Models;
using ReisProduction.Winjoys.Models;
using ReisProduction.Windelay.Models;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System.Threading.Tasks;
using System.Diagnostics;
using Microsoft.UI.Xaml;
using System.Threading;
using Windows.System;
using WinRT.Interop;
using System;
namespace DllTester;
public sealed partial class MainWindow : Window
{
    private readonly Winhook _winhook = new();
    public MainWindow()
    {
        InitializeComponent();
        MakeWindowAlwaysOnTop();
        InitializeInputHook();
        Task.Run(StartTest);
    }
    private static async void StartTest()
    {
        Random rnd = new();
        CancellationTokenSource cts = new();
        Debug.WriteLine("HybridDelay Test Başladı\n");
        //SpinWaitIterations = 10; // İsterseniz daha hassas yapabilirsiniz fakat gereksiz olur. Zaten 25 çok agresif bir hassaslık.
        // SpinWait için iterasyon sayısı (Varsayılan: İşlemci çekirdeğine göre değişir.)
        // public static int SpinWaitIterations { get; set; } = Math.Clamp(200 / Environment.ProcessorCount, 25, 100);
        //SpinAheadMilisecond = 500; // İsterseniz daha hassas yapabilirsiniz fakat iyi bilgisayarlar için gereksiz olur. Kötü bilgisayarlar için ise max 500ms yeterlidir.
        // HybridDelay için SpinAhead süresi (Varsayılan: 200ms)
        //public static int SpinAheadMilisecond { get; set; } = 200;
        for (int i = 0; i < 20; i++)
        {
            int ms = rnd.Next(200, 2000); // 200ms - 1000ms arası random süre
            DelayAction delay = new
            (
                ms,
                cts.Token, // İptal edebilmek için
                DelayType.HybridDelay // Delay türü (Burada HybridDelay kullanıldı) Sistemi en az yorarak en doğru sonucu verir.
                                      // Ancak DelayType vermek, sadece HandleDelay() methodunda geçerlidir.
            );

            Stopwatch sw = Stopwatch.StartNew();
            //HandleDelay(delay); // DelayType parametresi verilmezse, default olarak HybridDelay kullanılır.
            await HybridDelay(delay); // En sağlıklı ve önerilen yöntem budur.
            sw.Stop();

            double elapsed = sw.Elapsed.TotalMilliseconds,
                      diff = elapsed - ms;

            Debug.WriteLine($"Hedef: {ms,4:0000} MS | Beklenen: {elapsed,8:0000.00} MS | Sapma: {diff,8:+0000.00;-0000.00;+0000.00} MS");
        }
        Debug.WriteLine("\nTest Bitti.\nHighResSpin Test Başladı");
        for (int i = 0; i < 20; i++)
        {
            int ms = rnd.Next(10, 1000); // 10ms - 1000ms arası random süre
            DelayAction delay = new
            (
                ms,
                cts.Token, // İptal edebilmek için
                DelayType.HybridDelay // Delay türü (Burada HybridDelay kullanıldı) Sistemi en az yorarak en doğru sonucu verir.
                                      // Ancak DelayType vermek, sadece HandleDelay() methodunda geçerlidir.
            );

            Stopwatch sw = Stopwatch.StartNew();
            //HandleDelay(delay); // DelayType parametresi verilmezse, default olarak HybridDelay kullanılır.
            HighResSpin(delay); // En sağlıklı ve önerilen yöntem budur.
            sw.Stop();

            double elapsed = sw.Elapsed.TotalMilliseconds,
                      diff = elapsed - ms;

            Debug.WriteLine($"Hedef: {ms,4:0000} MS | Beklenen: {elapsed,8:0000.00} MS | Sapma: {diff,8:+0000.00;-0000.00;+0000.00} MS");
        }
        Debug.WriteLine("\nTest Bitti.");
    }
    [DllImport("user32.dll")]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "SYSLIB1054:Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time", Justification = "<Pending>")]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "<Pending>")]
    private static extern bool SetWindowPos(nint hWnd, nint hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
    private static readonly nint HWND_TOPMOST = new(-1);
    private const uint
        SWP_NOSIZE = 0x0001,
        SWP_NOMOVE = 0x0002;
    private void MakeWindowAlwaysOnTop()
    {
        var hWnd = WindowNative.GetWindowHandle(this);
        SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
    }
    private static async void WindowsJoystick()
    {
        KybdAction<VirtualKey> action = new(
            [VirtualKey.A, VirtualKey.B],
            [true, false]
        ); // A bas, B bırak gibi...

        await InputInjector.HandleKeys(action);

        DelayExecutor.HighResSpin(new(0, new()));

        var action2 = new KybdAction<VirtualKey>(
            Keys: [VirtualKey.A, VirtualKey.B],
            States: [false, true]
        ); // A bırak, B bak gibi...

        await InputInjector.HandleKeys(action2);
    }
    private void InitializeInputHook()
    {
        _winhook.FileCreated += (args) => AddMessage($"[FileCreated] Name: {args.Name}   |   Path:{args.FullPath}   |  Change Type: {args.ChangeType}");
        _winhook.FileDeleted += (args) => AddMessage($"[FileDeleted] Name: {args.Name}   |   Path:{args.FullPath}   |  Change Type: {args.ChangeType}");
        _winhook.FileRenamed += (args) => AddMessage($"[FileRenamed] Name: {args.Name}   |   Path:{args.FullPath}   |  Change Type: {args.ChangeType}");
        _winhook.FileChanged += (args) => AddMessage($"[FileChanged] Name: {args.Name}   |   Path:{args.FullPath}   |  Change Type: {args.ChangeType}");
        _winhook.FileWatcherError += (ex) => AddMessage($"[FileWatcherError] {ex.GetException().Message}");
        _winhook.MovementThreshold = 500; // Fare hareketi için eşik değeri (Move eventi tetiklenmesi için) (Varsayılan: 1)
        _winhook.AcceptNoneInput = true; // Klavye ve fareden gelen tüm girdileri kabul et (Varsayılan: false) (Yani sadece InputType enumunda olanlar) IsValid mantığı.
        _winhook.FilterKeys // Engellemek istediğiniz tuşları buraya ekleyin.
        (
            VirtualKey.V,
            VirtualKey.W,
            VirtualKey.E,
            VirtualKey.C,
            VirtualKey.Tab,
            VirtualKey.RightWindows,
            VirtualKey.Escape,
            VirtualKey.Space,
            VirtualKey.Left,
            VirtualKey.Up,
            VirtualKey.Right,
            VirtualKey.Down
        );
        _winhook.FilterMice // Engellemek istediğiniz fare tiplerini buraya ekleyin.
        (
            MouseType.MiddleButton,
            MouseType.XButton1,
            MouseType.XButton2,
            MouseType.MouseScrollLeft,
            MouseType.MouseScrollRight,
            MouseType.MouseScrollUp,
            MouseType.MouseScrollDown
        );
        _winhook.InputDown += k => AddMessage($"[InputDown] {k} (0x{(int)k:X})");
        _winhook.MouseHold += (k) => AddMessage($"[MouseHold] {k} (0x{(int)k:X})");
        _winhook.InputUp += k => AddMessage($"[InputUp] {k} (0x{(int)k:X})");
        _winhook.MouseMove += (x, y) =>
        {
            var pos = ReisProduction.Winhook.Services.Interop.GetCursorPos();
            AddMessage($"[MouseMove] X: {x}, Y: {y} - Mouse POS: ({pos.X}x{pos.Y})");
        };
        // Başlatmak için:
        _winhook.StartOrStopHooks([new KeyboardHook(true), new MouseHook(true), new FileHook(Paths: Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\Technopat")]);
        AddMessage("Winhook initialized and running...");
        // Winjoys test için:
        WindowsJoystick();
        // Sonlandırmak için:
        //_winhook.StartOrStopHooks([new KeyboardHook(false), new MouseHook(false), new FileHook(false, Paths: Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\Technopat")]);
    }
    private void AddMessage(string msg)
    {
        DispatcherQueue.TryEnqueue(() => // Main (UI) thread üzerinde çalıştır
        {
            MyListBox.Items.Add(msg);
            if (FindScrollViewer(MyListBox) is ScrollViewer sv)
                sv.ChangeView(null, sv.ExtentHeight, null);
        });
    }
    private static ScrollViewer? FindScrollViewer(DependencyObject obj)
    {
        if (obj is ScrollViewer sv) return sv;
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            if (FindScrollViewer(VisualTreeHelper.GetChild(obj, i)) is ScrollViewer found) return found;
        return null;
    }
}

Esneklik açısından bir sürü method yaptım ama sadece HandleKey() gösterebildim. Siz kendiniz inceleyip kullanabilirsiniz veya sorabilirsiniz.​

Methodlar:​

C#:
    public static async Task HandleInputs(params IInputAction[] actions)
    {
        foreach (var action in actions)
            switch (action)
            {
                case IKybdAction<VirtualKey> kybd:
                    await HandleKeys((KybdAction<VirtualKey>)(object)kybd);
                    break;
                case IKybdAction<WinRTKey> kybd:
                    await HandleKeys((KybdAction<WinRTKey>)(object)kybd);
                    break;
                case ButtonAction btn:
                    await HandleButtons(btn);
                    break;
                case ScrollAction scr:
                    await HandleScrolls(scr);
                    break;
                case MoveAction move:
                    await HandleMoves(move);
                    break;
#if WINUI || WINDOWS_APP || WINRT
                case MouseAction mouse:
                    await HandleMouse(mouse);
                    break;
                case GamepadAction gp:
                    await HandleGamepad(gp);
                    break;
                case TouchAction touch:
                    await HandleTouch(touch);
                    break;
                case PenAction pen:
                    await HandlePen(pen);
                    break;
#endif
                default:
                    throw new NotSupportedException("The provided action type is not supported.");
            }
    }
    public static async Task HandleKeys<T>(KybdAction<T> kybdAction)
    {
        if (typeof(T) == typeof(VirtualKey))
            ActionHandler((KybdAction<VirtualKey>)(object)kybdAction);
#if WINUI || WINDOWS_APP || WINRT
        else if (typeof(T) == typeof(WinRTKey))
            SendWinRTKeys((KybdAction<WinRTKey>)(object)kybdAction);
#endif
        else
            throw new NotSupportedException("The provided key type is not supported.");
        await Task.CompletedTask;
    }
    public static async Task HandleButtons(ButtonAction buttonAction)
    {
        for (int i = 0; i < buttonAction.Buttons.Length; i++)
            switch (buttonAction.Buttons[i])
            {
                case ButtonType.LeftButton:
                    MouseClick(true, buttonAction.States[i]);
                    break;
                case ButtonType.RightButton:
                    MouseClick(false, buttonAction.States[i]);
                    break;
                case ButtonType.MiddleButton:
                    MouseMiddleClick(buttonAction.States[i]);
                    break;
                case ButtonType.XButton1:
                    MouseXButtonClick(true, buttonAction.States[i]);
                    break;
                case ButtonType.XButton2:
                    MouseXButtonClick(false, buttonAction.States[i]);
                    break;
            }
        await Task.CompletedTask;
    }
    public static async Task HandleScrolls(ScrollAction scrollAction)
    {
        for (int i = 0; i < scrollAction.ScrollTypes.Length; i++)
            switch (scrollAction.ScrollTypes[i])
            {
                case ScrollType.MouseScrollLeft:
                    MouseWheelRight(-scrollAction.ScrollAmount[i]);
                    break;
                case ScrollType.MouseScrollRight:
                    MouseWheelRight(scrollAction.ScrollAmount[i]);
                    break;
                case ScrollType.MouseScrollUp:
                    MouseWheelDown(-scrollAction.ScrollAmount[i]);
                    break;
                case ScrollType.MouseScrollDown:
                    MouseWheelDown(scrollAction.ScrollAmount[i]);
                    break;
            }
        await Task.CompletedTask;
    }
    public static async Task HandleMoves(MoveAction moveAction)
    {
        for (int i = 0; i < moveAction.Moves.Length; i++)
            switch (moveAction.Moves[i])
            {
                case MoveType.MouseNavigateUp:
                    MouseMoveRelative(0, moveAction.CursorPoints[i].X);
                    break;
                case MoveType.MouseNavigateUpSmooth:
                    await MouseMoveRelativeSmooth(0, -moveAction.CursorPoints[i].Y);
                    break;
                case MoveType.MouseNavigateDown:
                    MouseMoveRelative(0, moveAction.CursorPoints[i].Y);
                    break;
                case MoveType.MouseNavigateDownSmooth:
                    await MouseMoveRelativeSmooth(0, moveAction.CursorPoints[i].Y);
                    break;
                case MoveType.MouseNavigateLeft:
                    MouseMoveRelative(-moveAction.CursorPoints[i].X, 0);
                    break;
                case MoveType.MouseNavigateLeftSmooth:
                    await MouseMoveRelativeSmooth(-moveAction.CursorPoints[i].X, 0);
                    break;
                case MoveType.MouseNavigateRight:
                    MouseMoveRelative(moveAction.CursorPoints[i].X, 0);
                    break;
                case MoveType.MouseNavigateRightSmooth:
                    await MouseMoveRelativeSmooth(moveAction.CursorPoints[i].X, 0);
                    break;
                case MoveType.MouseNavigateToXY:
                    MouseMoveTo(moveAction.CursorPoints[i].X, moveAction.CursorPoints[i].Y);
                    break;
                case MoveType.MouseNavigateToXYSmooth:
                    await MouseMoveToSmooth(moveAction.CursorPoints[i].X, moveAction.CursorPoints[i].Y);
                    break;
            }
    }
#if WINUI || WINDOWS_APP || WINRT
    public static async Task HandleMouse(MouseAction mouseAction) => await Task.Run(() => SendMouseInput(mouseAction));
    public static async Task HandleGamepad(GamepadAction gamepadAction) => await Task.Run(() => SendGamepadInput(gamepadAction));
    public static async Task HandleTouch(TouchAction touchAction) => await Task.Run(() => SendTouchInput(touchAction));
    public static async Task HandlePen(PenAction penAction) => await Task.Run(() => SendPenInput(penAction));
#endif
    public static async Task PressKeys<T>(KybdAction<T> kybdAction, DelayAction delayAction)
    {
        var states = new bool[kybdAction.Keys.Length];
        Array.Fill(states, true);
        await HandleKeys(kybdAction);
        await HybridDelay(delayAction);
        Array.Fill(states, false);
        await HandleKeys(kybdAction);
    }
    public static async Task PressButtons(ButtonAction buttonAction, DelayAction delayAction)
    {
        Array.Fill(buttonAction.States, true);
        await HandleButtons(buttonAction);
        await HybridDelay(delayAction);
        Array.Fill(buttonAction.States, false);
        await HandleButtons(buttonAction);
    }
    public static async Task HandleInputSequence(InputSequence sequence)
    {
        foreach (var step in sequence.Steps)
            await HandleInputs(step.Action);
    }

API Method yönlendirmesi:

C#:
#if WINUI || WINDOWS_APP || WINRT
using Injector = Windows.UI.Input.Preview.Injection.InputInjector;
using Windows.UI.Input.Preview.Injection;
#endif
using static ReisProduction.Winjoys.Utilities.Constants;
using static ReisProduction.Winjoys.Services.Interop;
using ReisProduction.Winjoys.Utilities.Structs;
using ReisProduction.Winjoys.Utilities;
using System.Runtime.InteropServices;
using Windows.System;
namespace ReisProduction.Winjoys.Models;
public static partial class InputInjector
{
#if WINUI || WINDOWS_APP || WINRT
    /// <summary>
    /// Windows.UI.Input.Preview.Injection.InputInjector instance.
    /// </summary>
    public static Injector WinRTInjector { get; set; } = null!;
    /// <summary>
    /// Performance count for touch and pen input. Default is 1.
    /// </summary>
    public static ulong PerformanceCount { get; set; } = 1;
    /// <summary>
    /// Time offset in milliseconds for touch and pen input. Default is 0.
    /// </summary>
    public static uint TimeOffsetInMilliseconds { get; set; } = 0;
    /// <summary>
    /// Pressure for touch input. Default is 32000.
    /// </summary>
    public static int Pressure { get; set; } = 32000;
    /// <summary>
    /// Orientation for touch input. Default is 90.
    /// </summary>
    public static int Orientation { get; set; } = 90;
    /// <summary>
    /// Touch Pointer ID.
    /// </summary>
    public static uint TouchPointerId { get; set; } = 0;
    /// <summary>
    /// Pen Pointer ID.
    /// </summary>
    public static uint PenPointerId { get; set; } = 0;
    /// <summary>
    /// Touch Parameters. Default is Contact, Orientation and Pressure.
    /// </summary>
    public static InjectedInputTouchParameters TouchParameters =>
        InjectedInputTouchParameters.Contact |
        InjectedInputTouchParameters.Orientation |
        InjectedInputTouchParameters.Pressure;
    /// <summary>
    /// Touch contact area. Default is a 4x4 square.
    /// </summary>
    public static InjectedInputRectangle Contact =>
    new()
    {
        Top = -2,
        Bottom = 2,
        Left = -2,
        Right = 2
    };
    public static void SendWinRTKeys(KybdAction<Utilities.Enums.WinRTKey> kybdAction)
    {
        BringToFrontNameOrHwnd(kybdAction.WindowhWnd, kybdAction.WindowTitle);
        List<InjectedInputKeyboardInfo> inputList = [];
        for (int i = 0; i < kybdAction.Keys.Length; i++)
            inputList.Add(new()
            {
                VirtualKey = (ushort)kybdAction.Keys[i],
                KeyOptions = kybdAction.States[i]
                    ? InjectedInputKeyOptions.None
                    : InjectedInputKeyOptions.KeyUp
            });
        WinRTInjector.InjectKeyboardInput(inputList);
    }
    public static void SendMouseInput(MouseAction mouseAction)
    {
        BringToFrontNameOrHwnd(mouseAction.WindowhWnd, mouseAction.WindowTitle);
        List<InjectedInputMouseInfo> inputList = [];
        for (int i = 0; i < mouseAction.Options.Length; i++)
            inputList.Add(new()
            {
                MouseOptions = mouseAction.Options[i],
                MouseData = mouseAction.MouseData[i],
                DeltaX = mouseAction.DeltaX[i],
                DeltaY = mouseAction.DeltaY[i],
                TimeOffsetInMilliseconds = TimeOffsetInMilliseconds
            });
        WinRTInjector.InjectMouseInput(inputList);
    }
    public static void SendGamepadInput(GamepadAction gamepadAction)
    {
        BringToFrontNameOrHwnd(gamepadAction.WindowhWnd, gamepadAction.WindowTitle);
        InjectedInputGamepadInfo input = new()
        {
            Buttons = gamepadAction.Buttons,
            LeftTrigger = gamepadAction.LeftTrigger,
            RightTrigger = gamepadAction.RightTrigger,
            LeftThumbstickX = gamepadAction.LeftThumbstickX,
            LeftThumbstickY = gamepadAction.LeftThumbstickY,
            RightThumbstickX = gamepadAction.RightThumbstickX,
            RightThumbstickY = gamepadAction.RightThumbstickY
        };
        WinRTInjector.InjectGamepadInput(input);
    }
    public static void SendTouchInput(TouchAction touchAction)
    {
        WinRTInjector.InjectTouchInput([new()
        {
            PointerInfo = new()
            {
                PointerId = TouchPointerId,
                PixelLocation = touchAction.Point,
                PointerOptions = touchAction.Options,
                PerformanceCount = PerformanceCount,
                TimeOffsetInMilliseconds = TimeOffsetInMilliseconds
            },
            TouchParameters = TouchParameters,
            Orientation = Orientation,
            Pressure = Pressure,
            Contact = Contact
        }]);
    }
    public static void SendPenInput(PenAction penAction)
    {
        WinRTInjector.InjectPenInput(new()
        {
            PointerInfo = new()
            {
                PointerId = PenPointerId,
                PixelLocation = penAction.Point,
                PointerOptions = penAction.Options,
                PerformanceCount = PerformanceCount,
                TimeOffsetInMilliseconds = TimeOffsetInMilliseconds
            },
            Pressure = penAction.Pressure,
            TiltX = penAction.TiltX,
            TiltY = penAction.TiltY,
            Rotation = penAction.Rotation,
            PenButtons = penAction.PenButtons,
            PenParameters = penAction.PenParameters
        });
    }
#endif
    public static void SendGameInput(KybdAction<VirtualKey> kybdAction)
    {
        BringToFrontNameOrHwnd(kybdAction.WindowhWnd, kybdAction.WindowTitle);
        var inputs = new INPUT[kybdAction.Keys.Length];
        for (int i = 0; i < kybdAction.Keys.Length; i++)
            inputs[i] = new INPUT
            {
                type = INPUT_KEYBOARD,
                u = new INPUTUNION
                {
                    ki = new KEYBDINPUT
                    {
                        wVk = 0,
                        wScan = (ushort)MapVirtualKey((uint)kybdAction.Keys[i], 0),
                        dwFlags = kybdAction.States[i] ? KEYEVENTF_SCANCODE : (KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP),
                        time = 0,
                        dwExtraInfo = GetMessageExtraInfo()
                    }
                }
            };
        _ = Services.Interop.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf<INPUT>());
    }
    public static void SendInput(KybdAction<VirtualKey> kybdAction)
    {
        BringToFrontNameOrHwnd(kybdAction.WindowhWnd, kybdAction.WindowTitle);
        var inputs = new INPUT[kybdAction.Keys.Length];
        for (int i = 0; i < kybdAction.Keys.Length; i++)
            inputs[i] = new INPUT
            {
                type = INPUT_KEYBOARD,
                u = new INPUTUNION
                {
                    ki = new KEYBDINPUT
                    {
                        wVk = (ushort)kybdAction.Keys[i],
                        dwFlags = kybdAction.States[i] ? 0 : KEYEVENTF_KEYUP,
                        time = 0,
                        dwExtraInfo = nint.Zero
                    }
                }
            };
        _ = Services.Interop.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf<INPUT>());
    }
    public static void KybdEvent(KybdAction<VirtualKey> kybdAction)
    {
        BringToFrontNameOrHwnd(kybdAction.WindowhWnd, kybdAction.WindowTitle);
        for (int i = 0; i < kybdAction.Keys.Length; i++)
            if (kybdAction.States[i])
                keybd_event((byte)kybdAction.Keys[i], 0, KEYEVENTF_EXTENDEDKEY, 0);
            else
                keybd_event((byte)kybdAction.Keys[i], 0, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, 0);
    }
    public static void PostMessage(KybdAction<VirtualKey> kybdAction)
    {
        nint windowhWnd = FindWindow(kybdAction.WindowhWnd, kybdAction.WindowTitle, true);
        BringToFrontNameOrHwnd(windowhWnd, kybdAction.WindowTitle);
        for (int i = 0; i < kybdAction.Keys.Length; i++)
        {
            int msg = kybdAction.States[i] ? WM_KEYDOWN : WM_KEYUP;
            _ = Services.Interop.PostMessage(windowhWnd, (uint)msg, (ushort)kybdAction.Keys[i], nint.Zero);
        }
    }
    public static void SendMessage(KybdAction<VirtualKey> kybdAction)
    {
        nint windowhWnd = FindWindow(kybdAction.WindowhWnd, kybdAction.WindowTitle, true);
        BringToFrontNameOrHwnd(windowhWnd, kybdAction.WindowTitle);
        for (int i = 0; i < kybdAction.Keys.Length; i++)
        {
            int msg = kybdAction.States[i] ? WM_KEYDOWN : WM_KEYUP;
            _ = Services.Interop.SendMessage(windowhWnd, (uint)msg, (ushort)kybdAction.Keys[i], nint.Zero);
        }
    }
    public static void SendKeys(KybdAction<VirtualKey> kybdAction) => SendKeys(string.Concat(kybdAction.Keys.Select(k => k.ToKeyString())), kybdAction.WindowhWnd, kybdAction.WindowTitle);
    public static void SendKeys(string keys, nint windowhWnd = 0, string windowTitle = "")
    {
        BringToFrontNameOrHwnd(windowhWnd, windowTitle);
        System.Windows.Forms.SendKeys.Send(keys);
    }
    public static void SendWait(KybdAction<VirtualKey> kybdAction) => SendWait(string.Concat(kybdAction.Keys.Select(k => k.ToKeyString())), kybdAction.WindowhWnd, kybdAction.WindowTitle);
    public static void SendWait(string keys, nint windowhWnd = 0, string windowTitle = "")
    {
        BringToFrontNameOrHwnd(windowhWnd, windowTitle);
        System.Windows.Forms.SendKeys.SendWait(keys);
    }
}

Private Injectors:

C#:
private static char ApplyShiftToNonLetter(char c) => c switch
{
    '1' => '!',
    '2' => '@',
    '3' => '#',
    '4' => '$',
    '5' => '%',
    '6' => '^',
    '7' => '&',
    '8' => '*',
    '9' => '(',
    '0' => ')',
    '-' => '_',
    '=' => '+',
    ';' => ':',
    ',' => '<',
    '.' => '>',
    '/' => '?',
    '`' => '~',
    '[' => '{',
    ']' => '}',
    '\\' => '|',
    '\'' => '"',
    _ => c
};
private static void MouseClick(bool isLeftClick, bool isDown)
{
    INPUT input = new()
    {
        type = INPUT_MOUSE,
        u = new INPUTUNION
        {
            mi = new MOUSEINPUT
            {
                dwFlags = isLeftClick
                    ? (isDown ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP)
                    : (isDown ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP),
                time = 0,
                dwExtraInfo = nint.Zero
            }
        }
    };
    _ = Services.Interop.SendInput(1, [input], Marshal.SizeOf<INPUT>());
}
private static void MouseMiddleClick(bool isDown)
{
    INPUT input = isDown
        ? new INPUT { type = INPUT_MOUSE, u = new INPUTUNION { mi = new MOUSEINPUT { dwFlags = MOUSEEVENTF_MIDDLEDOWN } } }
        : new INPUT { type = INPUT_MOUSE, u = new INPUTUNION { mi = new MOUSEINPUT { dwFlags = MOUSEEVENTF_MIDDLEUP } } };
    _ = Services.Interop.SendInput(1, [input], Marshal.SizeOf<INPUT>());
}
private static void MouseXButtonClick(bool isXButton1, bool isDown)
{
    INPUT input = new()
    {
        type = INPUT_MOUSE,
        u = new INPUTUNION
        {
            mi = new MOUSEINPUT
            {
                mouseData = isXButton1 ? XBUTTON1 : XBUTTON2,
                dwFlags = isDown ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP,
                time = 0,
                dwExtraInfo = nint.Zero
            }
        }
    };
    _ = Services.Interop.SendInput(1, [input], Marshal.SizeOf<INPUT>());
}
private static void MouseWheelRight(int scrollAmount)
{
    INPUT input = new()
    {
        type = INPUT_MOUSE,
        u = new INPUTUNION
        {
            mi = new MOUSEINPUT
            {
                mouseData = WHEEL_DELTA * scrollAmount,
                dwFlags = MOUSEEVENTF_HWHEEL,
                time = 0,
                dwExtraInfo = nint.Zero
            }
        }
    };
    _ = Services.Interop.SendInput(1, [input], Marshal.SizeOf<INPUT>());
}
private static void MouseWheelDown(int scrollAmount)
{
    INPUT input = new()
    {
        type = INPUT_MOUSE,
        u = new INPUTUNION
        {
            mi = new MOUSEINPUT
            {
                mouseData = WHEEL_DELTA * scrollAmount,
                dwFlags = MOUSEEVENTF_WHEEL,
                time = 0,
                dwExtraInfo = nint.Zero
            }
        }
    };
    _ = Services.Interop.SendInput(1, [input], Marshal.SizeOf<INPUT>());
}
private static void MouseMoveTo(int x, int y)
{
    INPUT input = new()
    {
        type = INPUT_MOUSE,
        u = new INPUTUNION
        {
            mi = new MOUSEINPUT
            {
                dx = x * 65535 / ScreenSize.Width,
                dy = y * 65535 / ScreenSize.Height,
                dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
                time = 0,
                dwExtraInfo = nint.Zero
            }
        }
    };
    _ = Services.Interop.SendInput(1, [input], Marshal.SizeOf<INPUT>());
}
private static async Task MouseMoveToSmooth(int x, int y)
{
    var startPos = GetCursorPos();
    float currentX = startPos.X, currentY = startPos.Y,
          deltaX = x - currentX, deltaY = y - currentY,
          distance = MathF.Sqrt(deltaX * deltaX + deltaY * deltaY);
    int steps = Math.Max(1, Math.Min(SmoothDurationMs / PerStepTimeDelay, (int)(distance / Math.Max(1, PixelsPerStepDelay)))),
        absX, absY, lastX = -1, lastY = -1;
    float stepX = deltaX / steps,
          stepY = deltaY / steps;
    for (int i = 0; i < steps; i++)
    {
        currentX += stepX;
        currentY += stepY;
        absX = (int)(currentX * 65535 / ScreenSize.Width);
        absY = (int)(currentY * 65535 / ScreenSize.Height);
        if (absX == lastX && absY == lastY)
            continue;
        lastX = absX; lastY = absY;
        INPUT input = new()
        {
            type = INPUT_MOUSE,
            u = new INPUTUNION
            {
                mi = new MOUSEINPUT
                {
                    dx = absX,
                    dy = absY,
                    dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
                    time = 0,
                    dwExtraInfo = nint.Zero
                }
            }
        };
        _ = Services.Interop.SendInput(1, [input], Marshal.SizeOf<INPUT>());
        await Task.Delay(PerStepTimeDelay);
    }
}
private static void MouseMoveRelative(int deltaX, int deltaY)
{
    INPUT input = new()
    {
        type = INPUT_MOUSE,
        u = new INPUTUNION
        {
            mi = new MOUSEINPUT
            {
                dx = deltaX,
                dy = deltaY,
                dwFlags = MOUSEEVENTF_MOVE,
                time = 0,
                dwExtraInfo = nint.Zero
            }
        }
    };
    _ = Services.Interop.SendInput(1, [input], Marshal.SizeOf<INPUT>());
}
private static async Task MouseMoveRelativeSmooth(int deltaX, int deltaY)
{
    float distance = MathF.Sqrt(deltaX * deltaX + deltaY * deltaY);
    int steps = Math.Max(1, Math.Min(SmoothDurationMs / PerStepTimeDelay, (int)(distance / Math.Max(1, PixelsPerStepDelay)))),
        moveX, moveY;
    float stepX = (float)deltaX / steps,
          stepY = (float)deltaY / steps,
          accX = 0, accY = 0;
    for (int i = 0; i < steps; i++)
    {
        accX += stepX;
        accY += stepY;
        moveX = (int)Math.Round(accX);
        moveY = (int)Math.Round(accY);
        accX -= moveX;
        accY -= moveY;
        if (moveX != 0 || moveY != 0)
            MouseMoveRelative(moveX, moveY);
        await Task.Delay(PerStepTimeDelay);
    }
}

Record'lar:

C#:
#if WINUI || WINDOWS_APP || WINRT
using Windows.UI.Input.Preview.Injection;
using Windows.Gaming.Input;
#endif
using ReisProduction.Winjoys.Utilities.Enums;
using ReisProduction.Windelay.Utilities;
using Windows.Graphics;
namespace ReisProduction.Winjoys.Utilities;
public record InputSequence(IReadOnlyList<InputStep> Steps);
public record InputStep(
    IInputAction Action,
    DelayAction? Delay = default
);
public record KybdAction<T>(
    T[] Keys,
    bool[] States,
    nint WindowhWnd = 0,
    string WindowTitle = ""
) : IKybdAction<T>;
public record ButtonAction(
    bool[] States,
    ButtonType[] Buttons,
    nint WindowhWnd = 0,
    string WindowTitle = ""
) : IMouseButton;
public record ScrollAction(
    ScrollType[] ScrollTypes,
    int[] ScrollAmount,
    nint WindowhWnd = 0,
    string WindowTitle = ""
) : IScrollAction;
public record MoveAction(
    MoveType[] Moves,
    PointInt32[] CursorPoints
) : IMoveAction;
#if WINUI || WINDOWS_APP || WINRT
public record MouseAction(
    InjectedInputMouseOptions[] Options,
    uint[] MouseData,
    int[] DeltaX,
    int[] DeltaY,
    nint WindowhWnd = 0,
    string WindowTitle = ""
) : IMouseAction;
public record GamepadAction(
    GamepadButtons Buttons,
    byte LeftTrigger = 0,
    byte RightTrigger = 0,
    short LeftThumbstickX = 0,
    short LeftThumbstickY = 0,
    short RightThumbstickX = 0,
    short RightThumbstickY = 0,
    nint WindowhWnd = 0,
    string WindowTitle = ""
) : IGamepadAction;
public record PenAction(
    InjectedInputPointerOptions Options,
    InjectedInputPoint Point,
    int Pressure = 2000,
    int TiltX = 0,
    int TiltY = 0,
    double Rotation = 0,
    InjectedInputPenButtons PenButtons = InjectedInputPenButtons.None,
    InjectedInputPenParameters PenParameters = InjectedInputPenParameters.Pressure |
    InjectedInputPenParameters.Rotation | InjectedInputPenParameters.TiltX | InjectedInputPenParameters.TiltY,
    nint WindowhWnd = 0,
    string WindowTitle = ""
) : IPenAction;
public record TouchAction(
    InjectedInputPointerOptions Options,
    InjectedInputPoint Point,
    nint WindowhWnd = 0,
    string WindowTitle = ""
) : ITouchAction;
#endif

Interop / Helper:

C#:
public static void BringToFrontNameOrHwnd(nint hWnd = 0, string windowTitle = "")
{
    if (!InputInjector.BringToFrontWindow && hWnd is 0 &&
        string.IsNullOrWhiteSpace(windowTitle)) return;
    if (hWnd is 0)
        if (string.IsNullOrWhiteSpace(windowTitle))
            throw new ArgumentException("Window title cannot be null or empty when hWnd is 0.");
        else
            hWnd = FindWindow(hWnd, windowTitle);
    ShowWindow(hWnd, SW_SHOW);
    SetForegroundWindow(hWnd);
}
public static POINT GetCursorPos() => GetCursorPos(out POINT point) ? point :
    throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get cursor position");
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool GetCursorPos(out POINT lpPoint);
public static nint FindWindow(nint windowhWnd = 0, string? windowTitle = null, bool GetForeground = false)
{
    if (windowhWnd is 0)
        if (!string.IsNullOrWhiteSpace(windowTitle))
            windowhWnd = FindWindow(null, windowTitle);
        else if (GetForeground)
            windowhWnd = GetForegroundWindow();
    if (windowhWnd is 0 && GetForeground) throw new Exception($"Window= \"{windowTitle}\" or hWnd= \"{windowhWnd}\" is doesn't exit");
    return windowhWnd;
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool PostMessage(nint hWnd, uint Msg, nint wParam, nint lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern nint SendMessage(nint hWnd, uint Msg, nint wParam, nint lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern uint MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern nint GetMessageExtraInfo();
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern nint GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetForegroundWindow(nint hWnd);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool ShowWindow(nint hWnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern nint FindWindow(string? lpClassName, string lpWindowName);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool CloseHandle(nint hObject);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern nint CreateWaitableTimer(nint lpTimerAttributes, bool bManualReset, string? lpTimerName);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetWaitableTimer(nint hTimer, ref long pDueTime, int lPeriod, nint pfnCompletionRoutine, nint lpArgToCompletionRoutine, bool fResume);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern uint WaitForSingleObject(nint hHandle, uint dwMilliseconds);
[DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern uint timeBeginPeriod(uint uMilliseconds);
[DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern uint timeEndPeriod(uint uMilliseconds);
Yakında Winmenu projemi de bitirirsem ardından Winmacro'yu da ücretsiz olarak public sunabilirim. Ama şu an bir oyun projem de olduğu için biraz daha arka planda kalacaklar. Ben macro kullanmayı seven birisiyim ve Jitbit Recorder'a bağlı kalmaktan memnun değilim. Daha iyisini ücretsiz bir şekilde yapabileceğimi düşünüyorum. Şu anda gayet iyi gidiyorum. Görünüm olarak da WinUI 3 kullanacağımdan, o konuda problemim olmayacak. Kütüphaneyi inceleyip eklememi istediğiniz özellik veya düzeltmem gereken bir bug bulursanız bildirmeyi unutmayın. Şimdilik bitti ve rafa kaldırdım. Arada bir değişiklik olursa güncellemeler getiririm.

Ciddi emek sarfettim. C# ile açık kaynak olup Global çalışan en sağlam Injector'lerden biri oldu.
NOT: Güncellemeler pushlanıp v25.5.1 olarak GitHub'a eklenmiştir.

İyi günler ve iyi çalışmalar dilerim!

Yakında çıkacak Windoc uygulamamı takip etmek için: Bilgisayarın sağlığını ve performansını kontrol eden uygulamam Windoc
 
Elinize sağlık, gene yararlı bir rehber. 👍🏿
Lise düzeyinde bu kadar C# öğretmediler bize. Kütüphane nasıl eklenir onu bile bilmiyorum. 😅
Ben de yazılım geliştirme okudum ve Bilgisayar Programcılığı okuyorum şu an lisedekiyle aynı. Kendi kendini geliştirmen gerekiyor. Ben de bir dönem bütün kodları düzensiz yazardım, algoritmam kötüydü, dll kullanmasını bilmezdim vs. vs. Bir baktım kendi dll imi yazarken buldum. Zor değil aslında nasıl ekleyebileceğini gösteriyorum. Teşekkür ederim🙂
 

Technopat Haberler

Geri
Yukarı