CString、BSTR、LPCTSTR、TCHAR、char*、wchar_t*……VC++ 開發者每天都在與它們纏鬥。本文系統整理最常用、最易錯的轉換場景與正確寫法。
// 從字面常數建構 (自動偵測 ANSI/Unicode)
CString s1 = _T("char string");
CString s2 = L"wide char string";
// 預先配置緩衝區
CString s3(_T(' '), 100); // 100 個空白字元
// 直接傳給 LPCTSTR 參數函數 (隱含轉換運算子)
SetWindowText(hWnd, s1); // 正確
SetWindowText(hWnd, (LPCTSTR)s1); // 等效顯式轉換
// 方法 1:建構函數 (資源 ID 需轉型)
CString s4((LPCTSTR)IDS_MYSTRING);
// 方法 2:LoadString
CString s5;
s5.LoadString(IDS_MYSTRING);
// 方法 3:Format 從字串表讀取格式化字串
CString s6;
s6.Format(IDS_FORMAT_STRING, _T("bob"), 123);
錯誤寫法 (破壞封裝,未來版本會崩潰):
LVITEM item = {0};
item.pszText = (LPTSTR)(LPCTSTR)str; // 錯誤!移除 const
正確寫法 (GetBuffer/ReleaseBuffer 配對):
LVITEM item = {0};
item.pszText = str.GetBuffer(0); // 取得可寫指標,長度至少目前字串長
// ... 填充 item ...
ListView_SetItem(&item);
str.ReleaseBuffer(); // 歸還控制權,更新 CString 內部長度
GetBuffer(n):確保緩衝區至少 n 字元,傳 0 表示不擴充。GetBuffer 後必須呼叫 ReleaseBuffer,否則長度不同步。CString 轉為 LPTSTR,僅能轉 LPCTSTR。CString s = _T("Bob!");
BSTR bs1 = NULL, bs2 = NULL;
// 方法 1:AllocSysString (呼叫者負責 SysFreeString)
bs1 = s.AllocSysString();
::SysFreeString(bs1);
// 方法 2:SetSysString (可重複使用現有 BSTR)
s.SetSysString(&bs2);
::SysFreeString(bs2);
// Unicode 專案中:CString → char* (需 ATL 轉換巨集)
#include
USES_CONVERSION;
CString wstr = _T("Unicode 字串");
char* ansi = W2A(wstr); // 分配在堆疊,函數返回失效
// 反向:char* → CString
char* ansiStr = "ANSI string";
CString wstr2 = A2W(ansiStr);
注意:W2A/A2W 回傳指標指向堆疊緩衝區,**不可跨函數儲存**,需立即複製到 CString 或 std::string。
// 數值 → 字串
CString str;
str.Format(_T("%d"), 123);
str.Format(_T("%.2f"), 3.14159);
// 字串 → 數值
int n = _ttoi(_T("123"));
double d = _ttof(_T("3.14"));
long l = _ttol(_T("1234567890"));
| 場景 | 錯誤寫法 | 正確寫法 |
|---|---|---|
| 傳給 LPCTSTR | (LPTSTR)(LPCTSTR)str | str / (LPCTSTR)str |
| 取得可寫緩衝區 | (LPTSTR)(LPCTSTR)str | str.GetBuffer(n) / ReleaseBuffer() |
| CString → BSTR | (BSTR)(LPCTSTR)str | str.AllocSysString() / SetSysString() |
| W2A 結果儲存 | char* p = W2A(str); 保存 p | CString s = W2A(str); 保存 s |
| Format 緩衝區過小 | str.GetBuffer(10); str.Format(...超過10字元...) | str.GetBuffer(預估長度); 或直接 str.Format(...) |
型別轉換看似瑣碎,實則關係專案穩定性與可維護性。遵循「隱含轉換只用於 LPCTSTR 讀取、寫入必須 GetBuffer/ReleaseBuffer、COM 互操作用 AllocSysString/SetSysString、跨編碼用 ATL 轉換巨集並立即複製」原則,即可避開 99% 的坑。