I wanted one small thing: press Win+Shift+S and get my own screenshot tool instead of Snipping Tool.
That turned into a 7,600-line Python project, and a pile of Windows internals that I couldn't find explained in one place. Here are the four that cost me the most hair.
1. RegisterHotKey can't claim Win+Shift+S
Every tutorial says to use RegisterHotKey. It works for Ctrl+Alt+D. It silently fails for Win+Shift+S, because the shell reserves that combo.
The way around it is a low-level keyboard hook (WH_KEYBOARD_LL, id 13), which sees the key before the shell does:
HOOKPROC = ctypes.WINFUNCTYPE(ctypes.c_ssize_t, ctypes.c_int,
ctypes.c_size_t, ctypes.c_ssize_t)
def cb(nCode, wParam, lParam):
if nCode == 0 and wParam in (0x100, 0x101, 0x104, 0x105):
kb = ctypes.cast(lParam, ctypes.POINTER(KBDLLHOOKSTRUCT)).contents
if kb.vkCode == 0x53 and win_down() and shift_down(): # S
u.keybd_event(0xE8, 0, 0, 0) # see below
u.keybd_event(0xE8, 0, 2, 0)
QTimer.singleShot(0, on_trigger) # hop back to the Qt thread
return 1 # swallow the key
return u.CallNextHookEx(None, nCode, wParam, lParam)
self._proc = HOOKPROC(cb) # keep a reference!
hook = u.SetWindowsHookExW(13, self._proc, k.GetModuleHandleW(None), 0)
Three gotchas worth knowing:
-
Keep a reference to the callback. If
HOOKPROC(cb)gets garbage-collected, your app crashes on the next keypress. There's no traceback, it just dies. -
Swallowing
Sisn't enough. If you eat the keypress and the user then releasesWin, Windows opens the Start menu. The fix is to inject an unassigned virtual key (0xE8) so Windows thinksWinwas used in a combo. -
Track the key-up too. If you swallow the key-down, you have to swallow its matching key-up, or apps see an orphaned release. I pulled this into a pure function (
snip_hook_decision(...)) with no ctypes in it, so it's unit-testable.
The hook only looks at the specific keys it cares about while Win+Shift are held. Nothing is logged or stored. If you ship a keyboard hook, say that in your README.
2. Keeping your own UI out of your own recording
My screen recorder has a floating control bar. Early on, every video started with the recorder recording itself, which looks amateur.
Windows 10 (2004+) has a one-line fix:
WDA_EXCLUDEFROMCAPTURE = 0x11
ctypes.windll.user32.SetWindowDisplayAffinity(int(widget.winId()),
WDA_EXCLUDEFROMCAPTURE)
The window stays visible to the user but is invisible to capture APIs. It's best-effort on older builds, so wrap it in try/except and move on.
3. System audio in Python: sounddevice can't do it
Recording the microphone is easy. Recording what the PC is playing is not. sounddevice can't do WASAPI loopback.
What worked:
| Source | Library |
|---|---|
| Microphone | sounddevice |
| System audio (loopback) | PyAudioWPatch |
| Mixing + muxing into MP4 |
imageio-ffmpeg (bundles an ffmpeg binary) |
The part that matters: don't mix in real time. I record each source to its own WAV on disk, then mix once after the take with ffmpeg's amix. Real-time mixing means two clocks drifting against each other. Post-mixing means a dropped buffer in one stream can't corrupt the other.
I also pad or trim each track to exactly the video duration before mixing, because mic and loopback streams never end at the same millisecond.
4. OCR with zero extra installs
I wanted "select text from a screenshot" without telling users to install Tesseract.
Windows 10/11 already ships an OCR engine: Windows.Media.Ocr. You can reach it from PowerShell, so Python just shells out:
subprocess.run(
["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", ps1],
capture_output=True, timeout=120,
creationflags=0x08000000, # CREATE_NO_WINDOW, no console flash
)
The PowerShell script tries the user's language packs first and falls back to whatever recognizer is available. It runs in a QThread, so the UI never freezes. The result: no models to download, no 200 MB dependency, and it works offline.
Small things that made it feel finished
-
Recycle Bin, not delete.
os.removeis permanent.SHFileOperationWwithFOF_ALLOWUNDOsends files to the Recycle Bin, so "delete capture" is never scary. -
Atomic settings. Write to
settings.json.tmp, thenos.replace(). A crash mid-write can't corrupt your config. -
Single instance. A named mutex (
CreateMutexW+ check for error183) stops two copies fighting over the same global hotkeys. -
Autostart is one registry value under
HKCU\...\Run. No installer needed.
Where all this ended up
All of the above lives in ASNIPP, a screenshot tool, screen recorder and editor that sits as a small "Dynamic Island" pill at the top of your screen. The parts I'm proudest of:
- Screenshots with pen, highlighter, arrows, shapes, text and pixelate (for hiding sensitive info)
- A color picker with a magnifier loupe, and a pixel ruler
- Pin-to-screen floating captures
- Screen recording with system audio and mic, plus live drawing that gets recorded into the video
- A built-in video editor: trim, speed, reverse, color looks, fades, rotate and GIF export
- OCR on any capture
It's one 7600+ lines of python code packed into .exe, PyQt6, mss for capture and OpenCV for video. If you've ever wondered how far a python desktop app can go, it's a decent case study.
π [https://github.com/Akhouri-Systems/ASNIPP/releases/download/v1.0.0/ASNIPP_v1.zip]
If you've built something with ctypes and Win32, I'd love to hear what bit you. Drop it in the comments.
Top comments (69)
β€οΈ
thanks star for comment
what about your channel?
After Diwali π
oh okay channel name StarDev?
Idk yet. Bro please share your github link.π
github.com/Akhouri-Anmol-Kumar <--my personal
github.com/Akhouri-Systems <--Akhouri Systems
Thanks π₯°
my personal ones is not updated wait let me update it π
yeah star now I've updated my personal ones too.
@star123 can you suggest some upgrades, changes needed in my personal readme to make it look better?
Very beautiful π
what?
@star123 if you want to join Akhouri Systems server then you can, currently it is not active or honestly it is dead because my goal is to first collect some top individuals in the server and then start the server 24/7 like big companyy servers are... and currently it has @effessdev, puneet kumar mishra, @nyaomaru @xulingfeng
discord.gg/Hb7Mk8vh5
Akhouri Systems is ready to welcome you π€²
@effessdev bro can you come online in discord?
Ok...
ok..
You mean youtube channel right?
yeah star wants to open a channel so I was suggesting a name, like I also love these channel stuff But I failed but seeing her trying I'm motivated to help her.
β€οΈ
I also tried a couple of times (a guitar covers channel) but recording was too
hard without proper equipment so I gave up.
So I give up.π€
Nonono lol that doesn't mean you should give up π€¦ββοΈ
guitar covers sounds good! :D
yeah they sound REALLY good ππΆ
Yeah
But this one.......
π«©
But is that available there as well? Wow!
My first Dream phone (mobile)π₯Ί
π€£
Did you get it?
what?
oh wow are you guitar pro??
nah, I've experience of over 7 channels so I can guide you π
@effessdev bro please come online discord.
what 7 channels? π€
yeah once I own 7 channels, all combined maybe 1200-1500 subscribers biggest channel was 450 subs I guess
only had one channel with 10k subscribers, which I deleted because of JEE.π£πππ
woah woah woah whattttt 10kkkkk
I deleted it because my friend Imran said so. He said if we want to crack JEE, there should be zero distractions. It was actually a vlogging channel.
oh, by the way how's my new profile pic looking?
Bahut Acchi hai.β€οΈ
bro come online discord
10k lol did is soo talented π€―
yeahhhh peak IITians
Ok coming...
@effessdev see this youtu.be/r-fBULbTtdM
From where do you find all of this lol π
π Peak Anmol stuffs.
bro have you watched full π€£
Skipped a little bit
hmm, peak busy person π
me here unemployed watched same thing 30times
Yeah now I watched it full
yeah now we are both unemployed I guess hehehe
@technogamerz do you have discord?
No.
Wt? Then you told us to use Discord y?
Me? Never use discord!π€£
okay then make a account please and then I would share you akhouri systems server invitation link
Good to remember how can we will solve this kind of promblems! βοΈ
I always fight with windows keyboard settings ( forced company laptop ) until I give up the fight. My trick: I use AutoHotkey program to redefine my keyboard to
capsLock+;=Γ©capsLock+'=Γ‘... and so on
Because my main problem is the hungarian languge special character. In chat with my hungarian collegues I like to use full hungarian char set, but I always use english keyboard set and really hate to install multiple language keyboard set because windows do not saw which keyboard currently use, plus many program using english keyboard shortkeys. The whole question is so anoying.
The 0xE8 virtual-key injection is the detail that would have taken me days to find β swallowing S and then watching Start open on Win release is exactly the kind of bug that only shows up in real use. Same for the callback getting garbage-collected with no traceback. And stating outright that the hook only watches the keys it cares about, with nothing logged or stored, is the right call for a keyboard hook. Really nice write-up.
Thanks π
You're welcome β and thanks again for the write-up, the callback GC gotcha alone was worth the read. π
The line that hit me: "If HOOKPROC(cb) gets garbage-collected, your app crashes on the next keypress. There's no traceback, it just dies."
That is genuinely the scariest sentence in Python. I'm relatively new to Python, and the silent failures are what keep me up at nightβjust a flash of a console and then nothing.
The fact that you built a 7,600-line Win32 API app in pure Python is wild. The WASAPI loopback and the Windows OCR workarounds are incredibly useful. I tried recording system audio in Python once, hit a wall with sounddevice, and gave up.
Quick question: what was the biggest headache when packaging this into a .exe with PyInstaller? Did you have to do a lot of custom hooking to include the ffmpeg binaries and the PowerShell OCR scripts?
Massive respect for seeing this through to the end.
Bro please change profile pic πΌοΈ π€£
why π I know I look joker but still bro I want that me and akhouri systems both visible in profile hehehe but any more cool idea?
You changed your profile image! Wow!! :D
is that profile pic looking good?
ζι©εγεζΆη«ε ι€εεεεε ₯θΏδΊε°η»θι½εεΊζ₯δΊοΌθ―»θ΅·ζ₯εΎθΏηΎοΌζΆθδΊγ
This is exactly the gap we hit from the Electron side:
globalShortcut.registerwraps RegisterHotKey, which silently returns false on any Win+ chord the shell has reserved β so Win+Shift+S is genuinely unreachable from main without a native module that drops to WH_KEYBOARD_LL. The two pitfalls you called out map directly to Electron main-process work too: keeping the HOOKPROC callback referenced at module scope is the same rule as keeping a Tray object β let it GC and the next keypress takes the process down with no stack. The 0xE8 trick to mark Win as 'used in a combo' is the kind of shell-internals detail the docs never mention and that every low-level-hook port eventually rediscovers.The 0xE8 virtual-key injection was the detail I would never have found on my own β swallowing S but leaving Win to open the Start menu on release is exactly the kind of bug that looks like the code is haunted. The HOOKPROC garbage-collection gotcha is the other one: a crash with no traceback is brutal to debug, so keeping the callback alive and making the hook decision unit-testable is a really nice touch. I also liked the discipline of recording the mic and loopback to separate WAVs and mixing with ffmpeg amix afterwards instead of fighting two drifting clocks in real time. Did the built-in Windows.Media.Ocr hold up on low-contrast screenshots, or is that where you reach for a fallback?