🪟 Windows TippsPixel-Smartphones haben soeben ein riesiges Update erhalten(17.09.2026 um 10:32 Uhr)
🪟 Windows TippsAus alt mach (fast wie) neu: Stadionbeschallung Aachener Tivoli(17.09.2026 um 10:30 Uhr)
🪟 Windows ServerLenovo modernisiert Virtualisierungsinfrastruktur(17.09.2026 um 09:06 Uhr)
🤖 Android TippsAus alt mach (fast wie) neu: Stadionbeschallung Aachener Tivoli(17.09.2026 um 10:30 Uhr)
🕵️ SicherheitslückenCVE-2026-81165 | Drupal Blazy Module up to 3.0.18 improper authorization(17.09.2026 um 10:33 Uhr)
🪟 Windows TippsPixel-Smartphones haben soeben ein riesiges Update erhalten(17.09.2026 um 10:32 Uhr)
🪟 Windows TippsAus alt mach (fast wie) neu: Stadionbeschallung Aachener Tivoli(17.09.2026 um 10:30 Uhr)
🪟 Windows ServerLenovo modernisiert Virtualisierungsinfrastruktur(17.09.2026 um 09:06 Uhr)
🤖 Android TippsAus alt mach (fast wie) neu: Stadionbeschallung Aachener Tivoli(17.09.2026 um 10:30 Uhr)
🕵️ SicherheitslückenCVE-2026-81165 | Drupal Blazy Module up to 3.0.18 improper authorization(17.09.2026 um 10:33 Uhr)
🔧 Programmierung 🕛 vor 3 Monaten 11 Min Lesezeit
0

After 4 months solo: shipping a Windows tray AI hotkey on .NET 8 + WPF (and the Win32 paste-back rabbit hole)

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

I spent 4 months of nights and weekends building — caller must have received last input event, no active foreground lock, target not minimized. Any check fails → it silently returns false.

  • The actual editor lives in a child control (e.g. Scintilla inside Notepad++). SetForegroundWindow activates the top-level frame, but keyboard focus stays elsewhere. SendInput Ctrl+V then lands on the WindowProc of a non-input frame.






  • Working solution: AttachThreadInput sandwich



    The trick is AttachThreadInput — an API that lets your thread temporarily share input state with another thread. While attached, the OS treats both threads as one for focus/foreground purposes, bypassing the "didn't receive last input event" check.



    Plus: I need to know which child HWND had keyboard focus before my modal stole it. GetGUIThreadInfo.hwndFocus returns exactly that.



    Here's the production code (extracted from , not config.json. DPAPI encryption under the hood, bound to the user account, non-portable across machines by design.






    Lessons after 4 months




    1. Win32 is alive. Microsoft didn't replace it — they hid it behind WPF/WinUI. Build anything non-trivial system-side, and you're back to user32.dll. My P/Invoke list for the core workflow: RegisterHotKey, SendInput, GetForegroundWindow, GetGUIThreadInfo, AttachThreadInput, SetFocus, BringWindowToTop, IsIconic, ShowWindowAsync. That's just the baseline.


    2. Native beats Electron for tray utilities. Not ideology — pragmatism. 49 MB vs 250 MB installer, <1s cold start vs 3-5s, ~80 MB RAM idle vs ~400 MB. For something that lives in the background, that's the difference between "I don't notice it" and "oh there you are."


    3. Local\ Mutex, not Global\. Singletons usually use Global\ namespace, which requires SeCreateGlobalPrivilege. That right is granted to interactive users by default but stripped on locked-down domain machines (kiosks, AppLocker configs). On those systems, my app crashed at startup with UnauthorizedAccessException. Local\ (per-session) has no such restriction and matches the semantics I actually want (one instance per user session, not per machine):





    CODE
       public const string DefaultMutexName = @"Local\CapyBroV2";
    var mutex = new Mutex(initiallyOwned: false, name: mutexName, createdNew: out var createdNew);






    Also: initiallyOwned: false. With true, I'd get AbandonedMutexException after every crash. With false, process death cleans up silently.




    1. Foreground-poller for popup dismiss, not Mouse.Capture. My first prompt-picker used Mouse.Capture(this, SubTree) to detect clicks outside. WPF ListBox grabs Mouse.Capture internally for click-drag selection — my LostMouseCapture handler closed the popup BEFORE the user's MouseLeftButtonUp reached the ListBox. Final version uses a 100ms DispatcherTimer + GetForegroundWindow() poll. If foreground isn't my popup → close. Cross-process clicks (browser tabs, Notepad, Telegram) are invisible to WPF's input system — polling Win32 is the only reliable catch-all.


    2. STJ with source generation. Not JsonSerializer.Deserialize<T>(json) (reflection-based). Instead:





    CODE
       [JsonSerializable(typeof(AppConfig))]
    public partial class AppConfigJsonContext : JsonSerializerContext { }

    var config = JsonSerializer.Deserialize(json, AppConfigJsonContext.Default.AppConfig);






    One day to set up [JsonSerializable] attrs for each DTO. Result: AOT-friendly, no runtime reflection, faster parsing, cleaner stack traces on JsonException.





    1. Tech is ~30% of the work. The other 70% is marketing, docs, screenshots, localizations, SEO, GitHub issue triage, replying on Reddit. As a solo dev, that's not "side activity" — it's the activity after MVP.






    Stack receipts




    • ~12,000 lines of C# (the WPF app)

    • ~3,000 lines of Next.js (marketing site)

    • ~4 months, nights + weekends

    • ~$130 spent (domain, OpenRouter test credits, stock icons I didn't end up using)

    • Coffee: uncountable






    What's next




    • macOS port via Avalonia (~2 months)

    • Browser extension companion for web apps with shadow DOM

    • Native AOT once WPF + AOT become compatible (would shave 49 MB → ~25 MB)






    Code + links





    If you've built a similar Windows-side AI tool, I'd love to hear what Win32 weirdness you ran into. The Office (Word, Excel) paste-back behavior is something I still haven't 100% nailed — Word works, Excel works only via the F2/Esc edit-mode dance. If anyone has a clean solution, drop it in the comments 🙏



    Thanks for reading.

    Vollständiger Original-Artikel
    Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
    ↗ Original-Artikel auf dev.to lesen
    Wie bewertest du diesen Beitrag?
    1 Klick Feedback
    Teilen mit Netzwerk & Team:

    Community-Analysen & Experten-Meinungen 0

    Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
    Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
    Community Pulse: Relevanz-Einschätzung
    1 Klick Experten-Votum
    🔴 Akute Relevanz 0%
    🟡 In Evaluierung 0%
    🟢 Keine Auswirkung 0%
    Spannende Innovation 0%
    Verwandte Story-Cluster & Quellen (Vektor-KI)
    Port 8095 Engine
    1 Quelle
    Comulytic Note Pro im Test: KI-Rekorder mit überzeugenden Auswertungen und lästigem Ladeproblem
    1 Quelle
    Pixel-Smartphones haben soeben ein riesiges Update erhalten
    1 Quelle
    Red Bull E-Scooter: Zu diesem Vodafone-Handytarif mit 20 GB gibt es einen E-Scooter und Lautsprecher dazu
    Ähnliche Beiträge
    🔍 Verwandte News

    Auch interessante Nachrichten After 4 months solo: shipping a Windows tray AI hotkey on .NET 8 + WPF (and the Win32 paste-back rabbit hole)

    Thematisch verwandte Begriffe: After, months, solo, shipping · 6 Treffer

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...