如何測試與除錯 DLL

本篇定位

這是 Phase 8「整合測試與發布」的第一篇。 DLL 開發的除錯比一般應用程式複雜:你不能直接執行 DLL,崩潰訊息也不像 EXE 那樣直接。這篇整理從開發到穩定運行的完整測試流程。


一、測試環境建立

1.1 獨立測試資料夾

永遠在一個和正式遊戲目錄分開的測試資料夾裡測試:

C:\MapleTest\               ← 測試環境(只放這裡)
├── MapleStory.exe          ← 遊戲執行檔的副本
├── dinput8.dll             ← 剛編譯的登入器 DLL
├── dinput8.ini             ← 設定檔(Debug 選項開啟)
└── *.wz                    ← WZ 資源檔案

C:\MapleStory\              ← 正式遊戲目錄(不碰)

不要直接在正式遊戲目錄測試

崩潰的 DLL 可能損壞遊戲設定,或者讓正式帳號資料出問題。測試環境和正式環境完全分開。

1.2 設定檔開啟 Debug 模式

[Debug]
ShowConsole = true    ; 開啟控制台視窗,看即時日誌
VerboseLog  = true    ; 顯示每個 Hook 的安裝訊息

開啟後,遊戲啟動時會出現一個 cmd 視窗,顯示:

[MainMain] Waiting for Themida depack...
[MainMain] Themida depack complete.
[Config] Server: 127.0.0.1:8484
[Config] Resolution: 1280x720
[Hook] Hook_CreateMutexA installed
[Hook] Hook_WSPStartup installed
...
[Client] Resolution set to 1280x720

二、常見崩潰原因與排查方式

2.1 遊戲立刻崩潰(DLL 載入時)

症狀:點擊 MapleStory.exe,視窗閃一下就消失,或直接沒有反應。

原因排查

可能原因排查方式
Hook 安裝了錯誤的地址用 x64dbg 附加,查看崩潰時的 EIP 和 Call Stack
DLL 本身有編譯錯誤確認是 Release x86 編譯,不是 Debug 或 x64
dinput8.dll 找不到系統 DLL控制台日誌是否有 Failed to load original dinput8.dll
使用了錯誤版本的 EXE確認 EXE 的 MD5 與登入器支援的版本一致

2.2 遊戲卡在載入畫面

症狀:黑色視窗出現但沒有進展,或者 Nexon 啟動畫面卡住。

最常見原因WaitForThemida() 等待條件永遠不成立(特徵 byte 沒有變成 0x55)。

// 在 WaitForThemida 加入計時器,超時後輸出診斷
while (*(BYTE*)CHECK_ADDR != 0x55) {
    Sleep(100);
    elapsed += 100;
    if (elapsed > 10000) {  // 10 秒
        std::cout << "[Error] Themida wait timeout. "
                  << "Current byte at " << std::hex << CHECK_ADDR
                  << ": 0x" << (int)*(BYTE*)CHECK_ADDR << std::endl;
        break;
    }
}

2.3 遊戲崩潰在特定操作後

症狀:能進入遊戲,但在登入 / 選角 / 進入地圖時崩潰。

排查:用 x64dbg 在崩潰時查看 Call Stack,找到第一個屬於我們 DLL 的函式。


三、主要除錯工具

3.1 x64dbg / OllyDbg:動態除錯

用途:在遊戲執行時設定斷點、查看暫存器、追蹤崩潰原因
步驟:
1. 開啟 x64dbg(選 x32 版本,因為 v83 是 32 位元)
2. File → Open → 選 MapleStory.exe
3. Debug → Run(讓遊戲啟動,DLL 會自動載入)
4. 崩潰時 x64dbg 會自動暫停,顯示崩潰位置和 Call Stack

如何附加到已執行的遊戲

如果遊戲已經在跑,可以用 File → Attach 選擇 MapleStory.exe 的進程 ID。但注意附加後 Themida 可能偵測到偵錯器並讓遊戲崩潰。用 ScyllaHide 外掛可以隱藏偵錯器的存在。

3.2 Cheat Engine:記憶體驗證

用途:確認我們寫入的值是否正確到達目標地址
步驟:
1. 附加到 MapleStory.exe
2. 搜尋特定地址(如 GameWidth_Addr = 0x00C4FCA4)
3. 查看該地址的值是否已被改為我們設定的解析度寬度

3.3 Process Monitor(Procmon):檔案系統追蹤

用途:確認 dinput8.ini 是否被正確讀取,追蹤 WZ 檔案載入順序
步驟:
1. 開啟 Procmon
2. 設定 Filter:Process Name is MapleStory.exe
3. 啟動遊戲,觀察檔案讀取事件

3.4 Console 日誌(最快速的除錯手段)

在關鍵位置加入 std::cout 輸出,確認執行到哪一步:

void Client::UpdateResolution() {
    std::cout << "[Client] Starting UpdateResolution..." << std::endl;
 
    Memory::Write<int>(addys::GameWidth_Addr, m_nGameWidth);
    std::cout << "[Client] GameWidth written: " << m_nGameWidth << std::endl;
 
    // ... 每個步驟都輸出確認
    std::cout << "[Client] UpdateResolution complete." << std::endl;
}

四、測試清單(Checklist)

每次修改 DLL 後,依序確認:

基礎功能
  □ DLL 成功載入(控制台出現 "Themida depack complete")
  □ 遊戲視窗以正確解析度出現
  □ 連線到私服而非官方伺服器(查看私服伺服器端的連線日誌)

登入流程
  □ 登入畫面 UI 元素位置正確(帳號輸入框置中)
  □ 帳號密碼輸入正常
  □ 登入按鈕可以點擊

選角流程
  □ 角色名稱和等級顯示正確
  □ 角色位置平均分布
  □ 「開始遊戲」按鈕正常

遊戲內
  □ HUD 元素(血條、魔力條、小地圖)位置正確
  □ 聊天視窗顯示正常
  □ 角色可以正常移動、攻擊
  □ NPC 對話視窗顯示正常

穩定性
  □ 遊戲運行 10 分鐘無崩潰
  □ 換地圖 3 次無崩潰
  □ 開啟背包、裝備視窗無崩潰

五、Access Violation 的診斷思路

最常見的崩潰是 Access Violation(0xC0000005),表示程式嘗試存取不存在或沒有權限的記憶體。

崩潰訊息:Access violation at 0x009F5239

診斷步驟:
1. 確認 0x009F5239 是有效地址(在 EXE 的 .text section 範圍內)
2. 如果是在 Hook 安裝後立刻崩潰:
   → 可能是 JMP 目標地址計算錯誤(相對偏移算錯)
3. 如果是在呼叫 Trampoline 時崩潰:
   → 可能是被覆蓋的原始指令沒有完整複製(截斷了多 byte 指令)
4. 如果是在普通 Memory::Write 後崩潰:
   → 確認目標地址在記憶體中確實存在且已解壓縮

延伸閱讀