Here is a bug report I could not have found in a log file.
I dictated a sentence on Windows. In Chrome it arrived perfectly. In a notes app, the same sentence arrived as the right number of characters, every one of them wrong: rows of ?, or -, or ..
The code that typed both was identical. And in both cases, Windows told me it worked.
How text gets typed on Windows
YazSes is an offline dictation tool. After transcribing your speech, it has to type the text into whatever window has focus. On Windows the standard way is SendInput with the KEYEVENTF_UNICODE flag: instead of pressing physical keys, you send each character as a Unicode code point. Windows wraps it in a special virtual key called VK_PACKET and delivers it.
SendInput returns the number of events it inserted into the input stream. Inserted. Not "received correctly by the app". It has no idea what the receiving window does next.
Two ways the receiver can break it
Microsoft's own documentation describes two ways VK_PACKET input goes wrong, and both are properties of the receiving app, not the sender:
-
The window is an ANSI window. Each character gets converted to the system's active ANSI code page. Anything that code page can't represent becomes
?. -
A text service or input handler doesn't unpack
VK_PACKET. It treats the packet as an ordinary key and runs it through the keyboard layout instead, which gives you whatever character that key happens to be.
Chromium handles VK_PACKET carefully. So testing in a browser proves almost nothing about other apps. That is a sobering thought for anyone who tested their Windows input code by typing into Chrome. (I did.)
To be honest about what I know: the symptom is real, I saw it on my own machine. Which of the two causes applied in that app, I have not proven. I diagnosed it from the code and the docs, not from a debugger on the failing window.
What I changed
You can't fix someone else's window. But you can stop pretending you know whether it worked, and you can offer a path that doesn't depend on VK_PACKET at all.
1. A clipboard backend (opt-in). Put the text on the clipboard as CF_UNICODETEXT, send a real Ctrl+V, then restore whatever was on the clipboard before. Almost every app handles paste correctly, because paste is the thing users do all day.
def inject(self, text: str) -> None:
if not text:
return
user32 = _load_clipboard_user32()
kernel32 = _load_kernel32()
previous: str | None = None
with _Clipboard(user32):
previous = _read_unicode_text(user32, kernel32)
_write_unicode_text(user32, kernel32, text)
time.sleep(_SETTLE_S)
self._send_ctrl_v(user32)
# ...then the previous clipboard text is put back
[injection]
backend = "clipboard" # typing stays the default
While doing this I found that the [injection] backend setting had been ignored on Windows entirely: the platform layer hard-coded the typing injector. So "try the clipboard backend" would have silently done nothing. Fixed in the same release (v2.40.0).
2. A diagnostic that names the receiver. Instead of guessing which window misbehaves, ask it:
yazses inject -d 5 --diagnose "hello"
You get five seconds to click into the problem app. Then it types "hello" and reports the window class, whether Windows considers it a Unicode window (IsWindowUnicode), and the active keyboard layout. If the window is ANSI, the cause is cause 1. If it's Unicode and still garbled, look at the input method.
The honest status
What is verified on a real Windows machine: the symptom.
What is not yet verified on real Windows: that the clipboard backend fixes that particular app, and that --diagnose gives the right verdict in the wild. The unit tests mock the Win32 calls. I don't have a Windows box I can debug on regularly. That is a real gap, and I'd rather say it here than have you discover it.
What I took away
- "The API returned success" describes the sender. Whether input arrives intact is decided by the receiver.
- Test against more than one receiver. A browser is the most forgiving target there is.
- When you can't fix a failure, make it describe itself. A diagnostic that prints the receiving window's properties turns a "it types garbage sometimes" report into one with a cause in it.
If you're on Windows and have an app that garbles synthesized input, I would genuinely like the output of yazses inject -d 5 --diagnose "hello" from it. Issue #183 is where Windows testing is collected.
YazSes is an offline, open-source (Apache-2.0) dictation tool: github.com/MSKazemi/yazses. Windows install: winget install MSKazemi.YazSes.
I wrote this with help from an AI assistant for drafting and editing; the bug, the diagnosis and the conclusions are mine, and I checked the facts against the code, Microsoft's documentation and the release notes.

Top comments (0)