共用方式為


TN059:使用 MFC MBCS/Unicode 轉換巨集

備註

自第一次包含在在線文件中以來,尚未更新下列技術附注。 因此,某些程式和主題可能已過期或不正確。 如需最新信息,建議您搜尋在線檔索引中感興趣的主題。

此附註描述如何使用 MBCS/Unicode 轉換的巨集,這些巨集定義於 AFXPRIV.H 中。 如果您的應用程式直接處理 OLE API 或基於某些原因,經常需要在 Unicode 與 MBCS 之間進行轉換,那麼這些巨集會非常有用。

概觀

在 MFC 3.x 中,在呼叫 OLE 介面時,會使用特殊 DLL(MFCANS32.DLL)自動在 Unicode 和 MBCS 之間轉換。 此 DLL 是一個幾乎透明的層,允許撰寫 OLE 應用程式,就像 OLE API 和介面是 MBCS 一樣,即使它們一律是 Unicode(在 Macintosh 上除外)。 雖然這一層很方便,而且允許應用程式快速從 Win16 移植到 Win32(MFC、Microsoft Word、Microsoft Excel 和 VBA 只是使用這項技術的某些 Microsoft 應用程式),但有時會對效能產生顯著影響。 基於這個理由,MFC 4.x 不會使用此 DLL,而是直接與 Unicode OLE 接談。 若要這樣做,MFC 必須在呼叫 OLE 介面時轉換成 MBCS,而且在實作 OLE 介面時,通常需要從 Unicode 轉換成 MBCS。 為了有效率且輕鬆地處理此作業,已建立一些巨集,讓此轉換更容易。

建立這類巨集集的最大障礙之一是記憶體配置。 因為字串無法就地轉換,所以必須配置新的記憶體來保存轉換的結果。 這可以使用類似下列的程式代碼來完成:

// we want to convert an MBCS string in lpszA
int nLen = MultiByteToWideChar(CP_ACP,
    0,
    lpszA, -1,
    NULL,
    NULL);

LPWSTR lpszW = new WCHAR[nLen];
MultiByteToWideChar(CP_ACP,
    0,
    lpszA, -1,
    lpszW,
    nLen);

// use it to call OLE here
pI->SomeFunctionThatNeedsUnicode(lpszW);

// free the string
delete[] lpszW;

此方法存在一些問題。 主要問題是需要撰寫、測試和偵錯大量的程序代碼。 這是一個簡單的函數調用,現在要複雜得多。 此外,這樣做會有顯著的運行時間額外負荷。 每次轉換完成時,都必須在堆積上配置記憶體並釋放。 最後,上述程式代碼需要為導入 Unicode 和 Macintosh 組建而新增適當的 #ifdefs(因為這些組建不需要進行此轉換)。

我們想出的解決方案是建立一些巨集,其中 1) 遮罩各種平臺之間的差異,2) 使用有效率的記憶體配置配置,3) 很容易插入現有的原始程式碼。 以下是其中一個定義的範例:

#define A2W(lpa) (\
((LPCSTR)lpa == NULL) NULL : (\
    _convert = (strnlen(lpa)+1),\
    AfxA2WHelper((LPWSTR) alloca(_convert*2),
    lpa,
    _convert)\)\)

使用此巨集來代替上述程式碼,事情變得更簡單:

// use it to call OLE here
USES_CONVERSION;
pI->SomeFunctionThatNeedsUnicode(T2OLE(lpszA));

有一些函式呼叫需要進行轉換,但使用巨集既簡單又有效。

每個巨集的實作都會使用 _alloca() 函式,從堆疊配置記憶體,而不是堆積。 從堆疊配置記憶體比配置堆積上的記憶體快很多,而且在函式結束時會自動釋放記憶體。 此外,巨集避免多次呼叫 MultiByteToWideChar(或 WideCharToMultiByte)。 這可藉由配置比必要更多的記憶體來完成。 我們知道 MBC 最多會轉換為一個 WCHAR,而且對於每個 WCHAR,我們最多可能需要兩個 MBC 字節。 藉由配置略多於所需的資源,但始終足以應付轉換的需求,可以避免第二次呼叫轉換函式。 輔助函數AfxA2Whelper的呼叫會減少為了執行轉換而必須進行的參數推送數量(這樣生成的程式碼比直接呼叫MultiByteToWideChar的程式碼要小)。

若要讓巨集有空間來儲存暫時的長度,您必須宣告名為 _convert 的局部變數,以便在使用轉換巨集的每個函式中執行這項作業。 這是藉由調用 USES_CONVERSION 巨集來完成的,如前面範例所示。

有一般轉換巨集和 OLE 特定巨集。 以下將討論這兩個不同的巨集組。 所有巨集都位於 AFXPRIV.H 中。

一般轉換巨集

通用轉換宏構成基礎機制。 上一節 A2W 所示的巨集範例和實作,就是這樣一個「泛型」巨集。 這與 OLE 無關。 下列列出一組泛型巨集:

A2CW      (LPCSTR) -> (LPCWSTR)
A2W      (LPCSTR) -> (LPWSTR)
W2CA      (LPCWSTR) -> (LPCSTR)
W2A      (LPCWSTR) -> (LPSTR)

除了執行文字轉換之外,還有巨集和協助函式可轉換 TEXTMETRICDEVMODEBSTR 和 OLE 分配的字串。 這些巨集超出了此討論的範圍,請參閱 AFXPRIV.H 以獲取有關這些巨集的更多資訊。

OLE 轉換巨集

OLE 轉換巨集是專為處理預期 OLESTR 字元的函式所設計。 如果您檢查 OLE 標頭,您會看到許多 LPCOLESTROLECHAR 的引用。 這些類型是用來參考 OLE 介面中使用的字元類型,其方式並非平台專屬。 OLECHAR 對應至 char Win16 和 Macintosh 平臺和 Win32 中的 WCHAR

為了將 MFC 程式代碼中的 #ifdef 指示詞數目保持在最小值,我們對涉及 OLE 字串的每個轉換都有類似的巨集。 下列是最常使用的宏:

T2COLE   (LPCTSTR) -> (LPCOLESTR)
T2OLE   (LPCTSTR) -> (LPOLESTR)
OLE2CT   (LPCOLESTR) -> (LPCTSTR)
OLE2T   (LPCOLESTR) -> (LPCSTR)

同樣地,有類似的巨集可用於處理 TEXTMETRIC、DEVMODE、BSTR 和動態分配的 OLE 字串。 請參閱 AFXPRIV.H 以獲取更多資訊。

其他考慮

請勿在緊密迴圈中使用巨集。 例如,您不想撰寫下列類型的程式代碼:

void BadIterateCode(LPCTSTR lpsz)
{
    USES_CONVERSION;
    for (int ii = 0; ii <10000; ii++)
    pI->SomeMethod(ii, T2COLE(lpsz));

}

上述程式代碼可能會根據字串 lpsz 的內容,在堆疊上配置 MB 的記憶體! 將字串轉換為每次迴圈執行也需要時間。 相反地,將這類常數轉換移出循環:

void MuchBetterIterateCode(LPCTSTR lpsz)
{
    USES_CONVERSION;
    LPCOLESTR lpszT = T2COLE(lpsz);

    for (int ii = 0; ii <10000; ii++)
    pI->SomeMethod(ii, lpszT);

}

如果字串不是常數,請將方法呼叫封裝到函式中。 這將允許每次釋放轉換緩衝區。 例如:

void CallSomeMethod(int ii, LPCTSTR lpsz)
{
    USES_CONVERSION;
    pI->SomeMethod(ii, T2COLE(lpsz));

}

void MuchBetterIterateCode2(LPCTSTR* lpszArray)
{
    for (int ii = 0; ii <10000; ii++)
    CallSomeMethod(ii, lpszArray[ii]);

}

除非回傳值表示在回傳之前需要複製資料,否則絕不要回傳其中一個巨集的結果。 例如,此程式代碼不正確:

LPTSTR BadConvert(ISomeInterface* pI)
{
    USES_CONVERSION;
    LPOLESTR lpsz = NULL;
    pI->GetFileName(&lpsz);

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // bad! returning alloca memory
}

可以通過將傳回值改為複製值的方式來修正上述程式碼:

CString BetterConvert(ISomeInterface* pI)
{
    USES_CONVERSION;
    LPOLESTR lpsz = NULL;
    pI->GetFileName(&lpsz);

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // CString makes copy
}

巨集很容易使用,而且很容易插入到程序代碼中,但如您可以從上述注意事項中得知,使用巨集時必須小心。

另請參閱

依編號的技術注意事項
依類別排序的技術注意事項