Projects

Aether

Aether is a lightweight, high-performance, and safety-focused Windows automation utility and workflow runner. Built in C#/.NET 8 and WPF with direct Win32 SendInput injection, it guarantees input release safety, target window focus gating, and coordinate-relative positioning.

Mar 6, 2026LiveSource
C#.NET 8WPFWin32 APIWindows

Overview

Aether 🖱️

Aether is a native Windows workflow automation utility built for users who want:

  • Guaranteed Input Cleanup: Ensures simulated mouse buttons and modifier keys are released on cancel, crash, or forced-exit.
  • Target Window Gating: Automatically pauses automation when the target window loses focus and resumes upon focus recovery.
  • Precise Timing Control: Uses high-resolution multimedia timers for sub-millisecond precision with minimal CPU overhead.
  • Advanced Workflow Authoring: Supports multi-step sequences with custom delays, nested loop blocks, and coordinate offsets.
  • Low-Latency Simulation: Simulates user inputs atomically via Win32 SendInput without kernel-level drivers.
  • Relative Coordinates: Target points can be defined relative to the screen, window, or client area to survive moved or resized windows.

Key Features

  • High-Resolution Timer Loop: Compensates for timer drift using system multimedia timers, maintaining ticks within 5ms under load.
  • Global Hotkey Controller: Integrates Win32 RegisterHotKey to toggle the armed state with an automatic suggestion engine for key conflicts.
  • Safety-Critical Releases: Listens to system/process exit events and cancellation tokens to execute EnsureReleaseAll() unconditionally.
  • Expanded Action Types: Supports scrolls, keyboard keypresses, mouse button holds, and modifier combinations.
  • Workflow Editing & UX: Enables step duplication, drag-and-drop step reordering, inline parameter validation, and running custom subsets.
  • Randomization Options: Emulates human interactions by adding randomized min/max jitter ranges and fixed-position pixel variance.

Tech Stack

  • UI Shell: WPF (.NET 8) with custom styled MVVM components.
  • Core Engine: C# (.NET 8) state machine-driven scheduler.
  • Native Interop: Direct P/Invoke bindings for Win32 User32 and Winmm libraries.
  • Persistence: JSON-based local profiles and configurations stored in %APPDATA%\Aether.

Architecture

Aether is designed around a strictly decoupled, interface-driven architecture that enforces the Dependency Inversion Principle (DIP). The core scheduling and state machine components have zero dependencies on direct Windows APIs or presentation framework libraries, enabling fully deterministic unit-testing via mock abstractions.

Layered Topology & Control Flow

The application is structured into three clean layers:

┌──────────────────────────────────────────────────────────────────┐
│                           PRESENTATION                           │
│                          [Aether.App]                            │
│                                                                  │
│    ┌─────────────────┐                ┌─────────────────────┐    │
│    │ MainWindow (UI) │◄──────────────►│ GlobalHotkeyService │    │
│    └────────┬────────┘                └──────────┬──────────┘    │
└─────────────┼────────────────────────────────────┼───────────────┘
              │ Commands & Updates                 │ Events (WM_HOTKEY)
              ▼                                    ▼
┌─────────────┼────────────────────────────────────┼───────────────┐
│             ▼                                    │               │
│    ┌──────────────────────────────────┐          │               │
│    │       AutomationController       │◄─────────┘               │
│    │         (State Machine)          │                          │
│    └────────────────┬─────────────────┘                          │
│                     │ Starts/Cancels                             │
│                     ▼                                            │
│    ┌──────────────────────────────────┐                          │
│    │         SchedulerEngine          │      CORE DOMAIN         │
│    │     (High-Res Timing Loop)       │     [Aether.Core]        │
│    └────────┬────────────────┬────────┘                          │
│             │                │                                   │
│             ▼                ▼                                   │
│     ┌──────────────┐  ┌──────────────┐                           │
│     │IInputInjector│  │ ITargetGuard │  (Interfaces / Boundary)   │
│     └───────▲──────┘  └──────▲───────┘                           │
└─────────────┼────────────────┼───────────────────────────────────┘
              │ Implements     │ Implements
┌─────────────┼────────────────┼───────────────────────────────────┐
│             │                │                                   │
│     ┌───────┴──────┐  ┌──────┴───────┐                           │
│     │SendInputInj. │  │TargetWindowG.│      INFRASTRUCTURE       │
│     └───────┬──────┘  └──────┬───────┘   [Aether.Infrastructure] │
│             │                │                                   │
│             ▼                ▼                                   │
│     ┌────────────────────────────────┐                           │
│     │        Win32 Subsystem         │                           │
│     │ (SendInput / GetForegroundWnd) │                           │
│     └────────────────────────────────┘                           │
└──────────────────────────────────────────────────────────────────┘

Key Architectural Patterns

  • Dependency Inversion: Core components (like the SchedulerEngine) consume abstractions (IInputInjector, ITargetWindowGuard). Concrete classes wrapping P/Invoke structures live solely inside the Infrastructure tier.
  • Process & Safety Guarantees: A global RuntimeSafetyGuard tracks simulated held buttons and modifier keys. Under cancellation or unexpected process termination, a fail-safe cleanup routine triggers to prevent stuck inputs:
public void EnsureReleaseAll()
{
    EnsureRelease(MouseButton.Left);
    EnsureRelease(MouseButton.Right);
    EnsureRelease(MouseButton.Middle);

    int[] keysToRelease;
    HotkeyModifiers[] modifiersToRelease;

    lock (_sync)
    {
        keysToRelease = _heldKeys.ToArray();
        modifiersToRelease = _heldModifiers.ToArray();
    }

    foreach (var key in keysToRelease)
    {
        SendKeyboardInput((ushort)key, NativeMethods.KeyeventfKeyup);
    }

    foreach (var modifier in modifiersToRelease)
    {
        var vk = GetVirtualKeyForModifier(modifier);
        SendKeyboardInput(vk, NativeMethods.KeyeventfKeyup);
    }
}