OutputDebugString 是很常用的調試函數, 起作用是將參數的字符串, 在運行時輸出到特定的位置, 以便我們能都獲知程序執行的情況. 其 MSDN: http://msdn.microsoft.com/en-us/library/windows/apps/aa363362(v=vs.85).aspx
其中比較重要的兩點:
1. Applications should send very minimal debug output...
2. OutputDebugStringW converts the specified string based on the current system locale information and passes it to OutputDebugStringA to be displayed. As a result, some Unicode characters may not be displayed correctly.
首先我們來看第 1 點. msdn 說: "very mininal" 到底是多 mininal 呢? 讓我們寫一個程序測試一下:
1 // main.cpp 2 // 3 4 #include <iostream> 5 using namespace std; 6 7 int main() 8 { 9 const size_t len = 1 * 1024 * 1024; 10 void *p = reinterpret_cast<void *>(new char[len]); // 1 MB. 11 12 // OutputDebugStringW 測試. 13 { 14 wchar_t *pw = reinterpret_cast<wchar_t *>(p); 15 size_t lenW = len / sizeof(pw[0]); 16 fill(pw, pw + lenW - 1, L'1'); 17 18 // `wcslen(pw) == 1 * 1024 * 1024 - 1`. 19 pw[lenW - 1] = '\0'; 20 OutputDebugStringW(pw); // 太長, 無法輸出. 21 22 // `wcslen(pw) == 32768`. 23 pw[327678] = '\0'; 24 OutputDebugStringW(pw); // 太長, 無法輸出. 25 26 // `wcslen(pw) == 32767`. 27 pw[32767] = '\0'; 28 OutputDebugStringW(pw); // 太長, 無法輸出. 29 30 // `wcslen(pw) == 32766`. 31 pw[32766] = '\0'; 32 OutputDebugStringW(pw); // ok. 正常輸出. 33 } 34 35 // OutputDebugStringA 測試. 36 { 37 char *pa = reinterpret_cast<char *>(p); 38 size_t lenA = len / sizeof(pa[0]); 39 fill(pa, pa + lenA - 1, '1'); 40 41 // `strlen(pa) == 65536`. 42 pa[65535] = '\0'; 43 OutputDebugStringA(pa); // 太長, 無法輸出. 44 45 pa[65534] = '\0'; 46 OutputDebugStringA(pa); // ok. 正常輸出. 47 } 48 49 return 0; 50 }
通過上述實驗, 我們得知:
1. W 系列: 32766. (包括結尾的 L'\0')
2. A 系列: 65534. (包括結尾的 '\0')
那么兩個數字暗示着什么? 如下:
1. 32766 == 0xFFFF / 2 - 1. (紅色部分, 是因為 `sizeof(wchar_t) == 2`)
2. 65534 == 0xFFFF - 1.
也就是說:
1. OutputDebugStringW 最長支持 32766 個字符的輸出(包括結尾的 L'\0').
2. OutputDebugStringA 最長支持 65534 個字符的輸出(包括結尾的 L'\0').
那么為什么是 32766 而不是 32767 呢? 這一點我們只能求助於 ReAct OS 了:
圖中能夠看出, OutputDebugStringW 是通過將其輸出內容轉換為 AnsiString, 然而就是這個 `RtlUnicodeStringToAnsiString` 中, 對 `Length` 重新計算了長度, 但是 `(UniSource->Lenth + sizeof(WCHAR)) / sizeof(WCHAR)` 相當於 `(65535 + 1) / 2 == 32768`, 因此導致 `32768 > MAXUSHORT` 成立, 最終函數執行失敗.
再來看看第 2 點. 無論 W 還是 A 系列, 因為最終都會將輸出內容轉換為 ANSI, 因此很多字符是無法輸出的. 比如四個龍組成的 "𪚥", 讀作 zhe(2). 不舉代碼了, 看效果吧:
但是這個例子要注意, 由於 vs 編輯器的默認文件存儲格式 ANSI 存儲的, 因此第一次編譯的時候應該會提醒由於當前文本格式不支持某些字符, 要求你換一個格式存儲文件, 這是你選擇 Unicode 就行了. 編譯運行就能看到效果了.
當今我們使用的 Windows 中, 大部分函數都是 A 系列依賴於 W 系列, 也就是遇到 ANSI 字符, 會有系統 API 轉換為相應的 UNICODE 版本, 然后調用 W 系列的相應函數. 但是 OutputDebugString 卻是一個例外, 這也導致了即使使用 OutputDebugStringW 也會有很多字符都沒法正確輸出.
這再次提醒我們, 做企業級應用, 如果需要同時提供 ANSI 和 UNICODE 版本, 則應該由 W 版本實現具體功能, A 版本轉換為 W 版本然后調用之. 當然, 這應該是個人盡皆知的問題才對.