Visual C++ 8.0 (VS2005) / 9.0 (VS2008) 強化 CRT 安全性:眾多函數 (如 printf, strcpy, memcpy) 會驗證參數合法性。檢測到非法參數 (如 NULL 指標、緩衝區溢位) 時,預設行為:**呼叫 Watson 崩潰報告**、**Debug 模式觸發斷言**。本文詳細說明自定義處理器 _set_invalid_parameter_handler、處理器原型、範例程式、Debug 斷言干擾異常拋出的問題與解決。
_invoke_watson → 彈出「發送錯誤報告」對話框 → 進程終止。_CrtDbgReport (斷言對話框:中止/重試/忽略) → 若選「忽略」則繼續執行自定義處理器 (若有)。
_invalid_parameter_handler _set_invalid_parameter_handler(
_invalid_parameter_handler pNew
);
exit/abort/terminate),CRT 函數將**重獲控制權**,通常**停止執行**、**回傳錯誤碼**、**設定 errno = EINVAL**。
void __cdecl _invalid_parameter(
const wchar_t* expression, // 參數運算式字串 (如 "(buffer != NULL)")
const wchar_t* function, // CRT 函數名 (如 "strcpy_s")
const wchar_t* file, // CRT 來源檔案名
unsigned int line, // 來源行號
uintptr_t pReserved // 保留,通常 0
);
NULL/0 (資訊被剝離)。
// crt_invalid_param_handler.cpp
// Compile: cl /Zi /MTd crt_invalid_param_handler.cpp
#include
#include
#include
void myInvalidParameterHandler(
const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved)
{
wprintf(L"[Invalid Parameter] Function: %s\n"
L" File: %s, Line: %u\n"
L" Expression: %s\n",
function, file, line, expression);
// 此處不終止程序,讓 CRT 繼續執行並回傳錯誤碼
}
int wmain()
{
_invalid_parameter_handler oldHandler, newHandler;
newHandler = myInvalidParameterHandler;
oldHandler = _set_invalid_parameter_handler(newHandler);
// 關閉 Debug CRT 斷言對話框,讓自定義處理器生效
_CrtSetReportMode(_CRT_ASSERT, 0); // 不彈出對話框
// 故意觸發:printf_s 格式字串為 NULL
wchar_t* formatString = NULL;
int result = wprintf(formatString); // 觸發非法參數
wprintf(L"wprintf 回傳: %d, errno: %d\n", result, errno);
// 恢復舊處理器 (良好習慣)
_set_invalid_parameter_handler(oldHandler);
return 0;
}
[Invalid Parameter] Function: wprintf
File: f:\dd\vctools\crt_bld\self_x86\crt\src\wprintf.c, Line: 54
Expression: (format != NULL)
wprintf 回傳: -1, errno: 22 (EINVAL)
Debug CRT 預設在非法參數**先**觸發斷言 (_CrtDbgReport),**阻塞**自定義處理器執行。必須關閉斷言報告模式:
_CrtSetReportMode(_CRT_ASSERT, 0); // 0 = 不報告
// 或僅導向檔案/除錯輸出
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
同理,**Checked Iterators** 邊界檢查若定義 _SECURE_SCL_THROWS=1 拋出異常,Debug 斷言會優先攔截,需同樣關閉 _CRT_ASSERT 才能捕獲異常。
| 場景 | 建議 |
|---|---|
| 生產環境 Release | 設定自定義處理器記錄日誌 (Event Log/檔案),**不終止程序**,讓 CRT 回傳錯誤碼,上層邏輯決定是否繼續/重試/優雅降級。 |
| 開發/測試 Debug | 保留斷言 (_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW)),立即定位問題行號。 |
| 單元測試自動化 | 關閉斷言對話框,啟用自定義處理器記錄,驗證錯誤碼/errno 符合預期。 |
| 跨 DLL 邊界 | 每個 CRT 實例 (EXE/DLL 各自靜態連結 /MT 或動態 /MD) 有**獨立**處理器,需各自設定。 |
printf_s, scanf_s, strcpy_s, strcat_s, memcpy_s, fopen_s 等 _s 後綴版本。printf, strcpy) 在 _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1 下會映射到安全版本。_CRT_SECURE_NO_WARNINGS 抑制「不安全函數」編譯警告 (不影響運行檢查)。VC8/9 CRT 非法參數處理機制提供**最後一道防線**。掌握 _set_invalid_parameter_handler、處理器簽名、**Debug 斷言干擾異常/處理器的解決方法** (_CrtSetReportMode),能在生產環境實現**優雅錯誤處理** (記錄+回傳錯誤碼) 而非硬性崩潰,並在開發期保留**即時斷言定位**能力。