注
次のテクニカル ノートは、最初にオンライン ドキュメントに含まれてから更新されていません。 その結果、一部の手順やトピックが古くなっているか、正しくない可能性があります。 最新情報については、オンライン ドキュメント インデックスで関心のあるトピックを検索することをお勧めします。
このメモでは、AFXPRIV.H で定義されている MBCS/Unicode 変換にマクロを使用する方法について説明します。 これらのマクロは、アプリケーションが OLE API を直接処理する場合、または何らかの理由で Unicode と MBCS の間で変換する必要がある場合に最も便利です。
概要
MFC 3.x では、OLE インターフェイスが呼び出されたときに Unicode と MBCS の間で自動的に変換するために、特殊な DLL (MFCANS32.DLL) が使用されました。 この DLL は、OLE アプリケーションが常に Unicode (Macintosh を除く) であっても、OLE API とインターフェイスが MBCS であるかのように書き込むことが可能な、ほぼ透過的なレイヤーでした。 このレイヤーは便利で、アプリケーションを Win16 から Win32 (MFC、Microsoft Word、Microsoft Excel、VBA) に迅速に移植できますが、このテクノロジを使用した Microsoft アプリケーションの一部にすぎませんが、パフォーマンスに大きな影響を与えることがありました。 このため、MFC 4.x はこの DLL を使用せず、代わりに Unicode OLE インターフェイスと直接通信します。 これを行うには、MFC は OLE インターフェイスを呼び出すときに Unicode から MBCS に変換する必要があり、多くの場合、OLE インターフェイスを実装するときに Unicode から MBCS に変換する必要があります。 これを効率的かつ簡単に処理するために、この変換を容易にするために多くのマクロが作成されました。
このようなマクロのセットを作成する最大のハードルの 1 つは、メモリ割り当てです。 文字列を所定の位置に変換できないため、変換された結果を保持する新しいメモリを割り当てる必要があります。 これは、次のようなコードで行われた可能性があります。
// 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)既存のソースコードに簡単に挿入できるマクロを作成することです。 定義の 1 つの例を次に示します。
#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 は最大 1 つの WCHAR に変換され、 WCHAR ごとに最大 2 MBC バイトになることがわかります。 必要以上に少し割り当てることで、変換を処理するのに十分ですが、変換関数への 2 回目の呼び出しは避けられます。 ヘルパー関数の呼び出し AfxA2Whelper
変換を実行するために実行する必要がある引数プッシュの数を減らします (これにより、 MultiByteToWideChar
直接呼び出された場合よりも小さなコードになります)。
マクロに一時的な長さを格納する領域を確保するには、変換マクロを使用する各関数でこれを行う_convertというローカル変数を宣言する必要があります。 これを行うには、上記の例に示すように、USES_CONVERSION マクロを呼び出します。
汎用変換マクロと OLE 固有のマクロの両方があります。 これら 2 つの異なるマクロ セットについては、以下で説明します。 すべてのマクロは AFXPRIV.H にあります。
汎用変換マクロ
ジェネリック変換マクロは、基になるメカニズムを形成します。 前のセクション A2W で示したマクロの例と実装は、このような "汎用" マクロの 1 つです。 OLE とは特に関係ありません。 一般的なマクロのセットを次に示します。
A2CW (LPCSTR) -> (LPCWSTR)
A2W (LPCSTR) -> (LPWSTR)
W2CA (LPCWSTR) -> (LPCSTR)
W2A (LPCWSTR) -> (LPSTR)
テキスト変換だけでなく、 TEXTMETRIC
、 DEVMODE
、 BSTR
、OLE で割り当てられた文字列を変換するためのマクロやヘルパー関数もあります。 これらのマクロは、この説明の範囲外です。AFXPRIV を参照してください。これらのマクロの詳細については、H を参照してください。
OLE 変換マクロ
OLE 変換マクロは、 OLESTR 文字を必要とする関数を処理するために特別に設計されています。 OLE ヘッダーを調べると、 LPCOLESTR と OLECHAR への参照が多数表示されます。 これらの型は、プラットフォームに固有ではない方法で OLE インターフェイスで使用される文字の種類を参照するために使用されます。
OLECHAR は 、Win16 および Macintosh プラットフォームの char
と 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
の内容に応じて、スタックにメガバイト単位のメモリが割り当てられる可能性があります。 また、ループの各イテレーションの文字列の変換にも時間がかかります。 代わりに、このような定数変換をループの外に移動します。
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]);
}
戻り値が戻り値の前にデータのコピーを作成することを意味しない限り、マクロの 1 つの結果を返すことはありません。 たとえば、次のコードは正しくありません。
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
}
マクロは使いやすく、コードに簡単に挿入できますが、上記の注意事項からわかるように、マクロを使用するときは注意する必要があります。