共用方式為


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

注意

下列技術提示自其納入線上文件以來,未曾更新。 因此,有些程序和主題可能已過期或不正確。 如需最新資訊,建議您在線上文件索引中搜尋相關的主題。

此提示描述如何使用 MBCS/Unicode 轉換的巨集 (在 AFXPRIV.H 中定義)。 如果您的應用程式會直接處理 OLE API,或基於某些原因,常常需要在 Unicode 和 MBCS 之間進行轉換,則使用這些巨集是最有用的。

概觀

在 MFC 3.x 中,當呼叫 OLE 介面時,會使用一個特別的 DLL (MFCANS32.DLL) 自動在 Unicode 和 MBCS 之間進行轉換。 這個 DLL 是一個幾乎透明的層級,允許撰寫 OLE API 和介面為 MBCS 的 OLE 應用程式,即使它們永遠都是 Unicode (除了 Macintosh 之外)。 雖然這個層級很方便且可讓應用程式從 Win16 快速移植到 Win32 (MFC、Microsoft Word、Microsoft Excel 和 VBA 是使用這項技術的一些 Microsoft 應用程式),但它有時會影響效能。 因此,MFC 4.x 不會使用這個 DLL,而是直接與 Unicode OLE 介面溝通。 為了要這麼做,MFC 需要在呼叫 OLE 介面時從 Unicode 轉換成 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)

除了轉換文字以外,也有巨集和 Helper 函式可轉換 TEXTMETRICDEVMODEBSTR 和 OLE 配置的字串。 這些宏超出此討論的範圍 - 請參閱 AFXPRIV。如需這些宏的詳細資訊,H。

OLE 轉換巨集

OLE 轉換宏是專為處理預期 OLESTR 字元的函式所設計。 如果您檢查 OLE 標頭,您會看到許多 LPCOLESTR OLECHAR 參考 。 這些類型用來以非專屬於平台的方法參考 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
}

巨集容易使用且容易插入至程式碼,但是您必須在使用它們時特別注意上述的各種警告。

另請參閱

依編號顯示的技術提示
依分類區分的技術提示