🎙️ Lecture 21: 【終極大師專案】Windows 底層鍵盤攔截與執行緒安全 (zhuyin_say)

NOTE作者資訊 (Author Info)

🎧 錄音室開場:駭入 Windows 的最深處!


🎯 專案解密:zhuyin_say 的底層生死鬥

根據 nv-addon-zhuyin_say_development.md 的開發紀錄,這個專案經歷了四個版本的慘烈演進:

1. 突破防線:接管系統底層鉤子 (winInputHook)

一般的 @scriptHandler.script 攔截,面對微軟輸入法會直接失效。 因此,開發者大膽放棄了常規做法,直接呼叫了 NVDA 內部用來與 Windows 作業系統溝通的 API:winInputHook.setCallbacks。 在自訂的按鍵攔截函式 (patched_keyDown) 裡,只要外掛決定攔截這個按鍵,就直接向作業系統回傳 False。這招「釜底抽薪」,讓 Windows 直接把按鍵丟進垃圾桶,微軟注音輸入法連鍵盤按下的訊號都收不到,達成了 100% 的輸入阻斷

2. 致命崩潰:C++ 執行緒斷言錯誤

當成功阻斷輸入後,開發者想要讓 NVDA 唸出注音,於是寫了 ui.message("ㄅ")。 結果,NVDA 瞬間當機閃退! * 崩潰原因:因為 winInputHook 是在 Windows 背景的「底層 C++ 執行緒」裡運作的。而在這個背景執行緒裡,我們居然試圖叫負責畫面的「主 GUI 執行緒」講話。這違反了作業系統的「執行緒安全 (Thread-Safety)」鐵律,導致 C++ 直接報錯崩潰! * 終極解法:祭出了我們在 L16 學過的安全金律:wx.CallAfter(ui.message, text)。透過 wx.CallAfter,背景執行緒會「寄一封信」給主執行緒,請主執行緒有空的時候唸出注音,完美避開了撞車危機!

3. 點字閱讀優化:Tab 鍵縮排

開發紀錄中還特別提到,為了讓使用「點字顯示器 (Braille Display)」的視障者能更順暢地閱讀 Python 原始碼,專案特地把所有 4 空格的縮排,全部取代成了 Tab 鍵 (\t)。這能節省點字顯示器寶貴的儲存格空間。


📝 大導演的 Vibe Coding 派工單 (Prompt)

要請 AI 寫出這麼硬核的外掛,我們不需要會寫 C++,但我們必須在 Prompt 中把「架構層級」拉到最高,並強制下達安全命令!

「你現在是深諳 Windows 底層 API 與 NVDA 核心架構的 C++ / Python 雙棲工程師。 請幫我開發『鍵盤注音學習模式』全域外掛 (zhuyin_say)。

核心架構與極嚴格規範: 1. 【100% 底層阻斷】:一般的 gesture hook 擋不住 IME 輸入法。請直接接管 winInputHook.setCallbacks(keyDown=self.patched_keyDown...)。在 patched_keyDown 中,若模式開啟,必須回傳 False 以在 Windows OS 底層徹底丟棄按鍵事件! 2. 【跨執行緒防撞金律】patched_keyDown 是由 C++ 背景執行緒呼叫的。當你需要朗讀注音時,嚴禁直接呼叫 ui.messagecore.callLater(會引發 wxWidgets 斷言崩潰)。你必須且只能使用 wx.CallAfter(ui.message, 注音字串) 將發聲任務安全遞交給主執行緒! 3. 【點字友善排版】:為了方便視障者閱讀你的原始碼,請捨棄 Python 常見的 4 空格縮排,全面改用 Tab 鍵 (\t) 進行縮排

請直接給我完整且保證不當機的 zhuyinExplanation.py 原始碼。」


🎓 學習總結:造物主的境界

當你讀懂了 zhuyin_say 的開發邏輯,你會發現:程式碼只是語言,真正的價值在於「解決問題的戰略架構」! 我們透過 Vibe Coding,指揮 AI 繞過表層的限制,直接駭入 Windows 最底層,並用 wx.CallAfter 化解了致命的系統崩潰。這,就是 Vibe Coding 大導演的最高境界!


🔍 零基礎讀碼指南:白話拆解 zhuyinExplanation.py 原始碼

如果你從未寫過代碼,看到 zhuyinExplanation.py 裡兩百多行的英文字母,可能會覺得像天書一樣。別慌!我們用「工廠流水線」和「信件收發室」的比喻,帶你一步步拆解它的物理構造。

📦 1. 備料與工具箱引進 (第 3 至 16 行:import)

在程式的最開頭,有一堆以 import 開頭的句子。這幕就像是在工廠開工前,從工具庫裡借出工具:

📖 2. 注音字典對照表 (第 36 至 67 行:ZHUYIN_MAP)

這是一個大括號包起來的區塊,稱為「字典 (Dictionary)」。

🛡️ 3. 貍貓換太子:極底層攔截 (第 122 至 181 行:patched_keyDown)

這是這個外掛的靈魂函數,像一個嚴格的「海關檢查哨」:

✉️ 4. 執行緒安全郵差 (第 168 至 177 行:wx.CallAfter)

這是最專業也最關鍵的一行:wx.CallAfter(ui.message, zhuyin_char)


👉 回課程大綱:學習地圖

🔥 專家級深度思考與實戰挑戰 (Expert Challenge)

以下題目專為尋求真正硬核觀念與專家級實戰能力的學員設計。題目無法從講義表面抄寫解答,請嘗試獨立思考後再點擊展開解析。

❓ 挑戰 1:注音聲調與同音異字在 NVDA 語音報讀中的「破音字拆解」 👉 點擊展開專家解析答案

【實戰情境問題】:在繁體中文環境中,許多中文字具備多個讀音(如「重」:zhòng / chóng)。NVDA 語音合成器在沒有上下文時常常讀錯。注音報讀外掛如何透過字典詞庫 (Dictionary Replacement) 與詞性標註進行動態修正?

【鑑別點說明】:測試學員對繁體中文化無障礙 TTS 語音字典修復與斷詞 (Tokenization) 的深度掌握。

【專家級解答與深度剖析】: 單字播報時缺少語境,TTS 只能隨機挑選預設讀音。注音外掛透過兩階梯修正:1. **字典替換 (Dictionary Matching)**:建立 `speechDict` 正則字典,將特定的詞組組合(如「重新」)強行替換為帶有精確注音符號的拼音字串(`ㄔㄨㄥˊ ㄒㄧㄣ`);2. **NLP 斷詞與詞性標註**:透過結巴斷詞或比對前端詞庫,判斷「重」字後方接的是名詞還是動詞,動態切換聲調,徹底解決破音字問題。

❓ 挑戰 2:單字逐字審閱時的「注音+字形拆解(如:木子李)」播報演算法 👉 點擊展開專家解析答案

【實戰情境問題】:當視障學員逐字檢查自己的姓名或程式碼變數時,光聽「ㄌㄧˇ」無法區分是「李」、「理」還是「禮」。請設計一套注音+字形拆解(Character Description)的自動播報邏輯。

【鑑別點說明】:考驗學員針對繁體中文無障礙獨特需求(漢字形音義拆解)的演算法設計能力。

【專家級解答與深度剖析】: 建立一個漢字形音義映射表(Character Description Table)。當學員使用審閱游標移動到單個漢字(如「李」)時:1. 外掛取得該字的 Unicode 碼;2. 先發送基本注音(`ㄌㄧˇ`);3. 延遲 150 毫秒後,查表取得該字的字形拆解部件(「木子李,木頭的木,孩子的子」);4. 透過 `ui.message()` 播報。這讓視障學員在寫程式與聽讀文件時,能 100% 精確確定每一個漢字的正確字形。