發表文章

目前顯示的是 12月, 2020的文章

使用 SetWindowsHookEx 要留意的地方

最近在公司發現一個小問題, 當我們在 Debug Process A 的時候 (Process A 處於 break mode), 如果我們在其它 Process B 例如: Chrome、記事本有打字的話,Process B 會直接 hang 住。 而造成這個現象的原因是來自於公司之前有人為了做熱鍵功能, 因此使用 SetWindowsHookEx hook global keyboard event。 但是由於 Process A 在 break mode,所以 OS 沒辦法將 keyboard event 交給 Process A 處理, 因此 OS 只能 suspend Process B 直到 keyboard event 被 Process A 處理完成。 補充說明一下:以上是 Windows 10 針對 SetWindowsHookEx 行為,不同版本可能有不同行為。 例如: 在 Windows 7 則是會將 DLL Inject 到其它 Process , 但因為 Windows 7 有 64 位元的版本,所以 DLL 可能會有位元相容性問題, 針對不能相容的 DLL,Windows 7 行為跟 Windows 10 相同 (Context switch 回 Process A), 至於能相容的 Process 則不需要 Context switch (所以也不會有卡住的問題)。 了解整個來龍去脈後,要解決問題就簡單了, 第一種就是在編譯 Debug 版本不  hook global keyboard event (workaround), 第二種就是改用  RegisterHotKey 處理熱鍵 , 第二種才是比較好的解法, 因為熱鍵而要  hook global keyboard event 非常不合理 (有資安上疑慮), 且其行為一般會被防毒軟體視為惡意軟體, 因此大家要做熱鍵功能還是乖乖使用  RegisterHotKey  吧 ~

使用 Visual C++ 2015 除錯 Visual C++ 6.0 的程式

圖片
最近遇到一個狀況是要使用 Visual C++ 2015 來除錯 Visual C++ 6.0 的程式, 使用 Visual C++ 2015 預設設定會無法下斷點 (也就是說 Visual C++ 2015 認為執行檔/DUMP 跟 PDB 不 match), 但是再三比對確定 PDB 是正確的之後,果斷懷疑是  Visual C++ 2015 不相容舊版 PDB , 於是開啟 Google 大神查詢相關資料,結果幸運地發現是可以透過下面設定來讓  Visual C++ 2015 相容舊版 PDB ~ 經過上述設定後,Visual C++ 6.0 的 PDB 就能成功被 Visual C++ 2015 識別了!!

LNK1201: Visual C++ 無法產生 PDB 的解法

前陣子遇到一個大型專案下 Visual C++ 6.0 編譯失敗的問題, 其錯誤訊息是 Linker Tools Error LNK1201 (Fatal Error C1060), 根據  Microsoft Docs 得知這個錯誤訊息是由 Linker 無法產生 PDB 造成, 於是先行確認磁碟可用空間、檔案權限 ... 等,但始終都查不出任何問題, 後來根據 Change Set 退回比較舊的版本就沒事了,而且這個問題也只發生在 Debug 版, 於是就在猜測是不是 PDB 會有大小上的限制。 最後根據 PDB 大小這個關鍵字在 Stack Overflow  發現也有人在  Visual C++ 2010   遇到類似的狀況。 根據 Stack Overflow 這篇討論串整理出來的最終結論是: PDB 是一個 MSF (Multi-Stream Format) File,Linker 在創建 PDB 時會以預設 2 KB Page Size 創建 (無法藉由參數修改 Linker 設定),因此 PDB 檔案大小是有限制的。 不過幸運的是,Compiler 在創建 PDB 時用的 Page Size 為 Linker 的 2 倍 (即 4KB Page Size),而且 Linker 在發現有 PDB 的狀況下會使用原先 PDB 的設定。 因此藉由在 Pre-Link Event 中增加下列指令讓 Compiler 先行創建一個初始 PDB , 就可以增加 PDB 檔案大小的上限。 cl -c "dummy_empty.cpp" /Zi /Fd"$(TargetDir)$(TargetName).pdb" 後來我們嘗試 Stack Overflow 這個神奇的解法後,真的就編譯成功了 XD  (唯一的差異是,Visual C++ 6.0 Linker 創建 PDB 預設是以 128 Byte Page Size,所以預設 PDB 最大只能來到 64 MB)

VectoredExceptionHandler 與 UnhandledExceptionFilter 的簡單比較

在 Windows 作業系統中,有二個比較常見的 API 可以用來攔截異常,  其中一個是 AddVectoredExceptionHandler ,另外一個則是 SetUnhandledExceptionFilter , VEH (Vectored Exception Handler) 能攔截所有的異常 (VEH 是當異常發生後第一個被執行的), 而 UEF (Unhandled Exception Filter) 則是在較後面才被呼叫。 而如果前面的異常處理機制已經處理完異常,那麼 UEF 是不會被執行的。 這樣聽起來 VEH 比較厲害,那為何一般程式都是使用 UEF 而不是 VEH 呢? 答案是 VEH 是第一個被執行的異常處理機制,因此會攔截到太多沒必要的異常, 所以你很難在 VEH 中判斷哪些異常是真的會 crash,哪一些是會被處理的異常。 因此一般應用程序都會使用 UEF,會使用到 VEH 的程序主要是 Debugger 、Diagnostic Tool ... 等 ~

[LeetCode][Medium] 752. Open the Lock

Leetcode 網址: https://leetcode.com/problems/open-the-lock/ 題目說明: 題目大意是有一個四位數的密碼鎖,每一位數字可以透過旋轉 (也就是可以9->0, 0->9) 來設定, 但是這個密碼鎖有被設定炸彈,轉到指定的密碼清單時 ( deadends),會爆炸。 給定一組密碼 ( target) 以及指定的密碼清單 (deadends), 請問從 0000 轉到 target 不爆炸的最短轉動次數是多少 ? 解題說明: 這題可以直接把四位數的密碼鎖想成四維 (w, x, y, z) 的地圖,起始位置為 0 0 0 0, 每一步可以移動一個維度 (w + 1, w - 1, x + 1, x - 1, y + 1, y - 1, z + 1, z - 1), 而指定的密碼清單則為地圖的路障 (牆壁 or 死路), 因此這個問題直接套用解圖論的算法 Breadth-first Search 或是 Depth-first Search 即可。 程式碼 class Solution { public: int table[10][10][10][10]; struct Lock { char w, x, y, z; }; inline int rotate(int v, int way) { v = v + way; return v >= 10 ? 0 : (v < 0 ? 9 : v); } inline void addQueue(queue & q, char w, char x, char y, char z, int step) { if (table[w][x][y][z] != -1 || table[w][x][y][z] == INT_MAX) return; table[w][x][y][z] = step; q.push({w, x, y, z}); } int openLock(vector & deadends, string target) { memset(table, 0