TN059: MFC MBCS/Unicode Dönüştürme Makrolarını Kullanma
Dekont
Aşağıdaki teknik not, çevrimiçi belgelere ilk kez eklendiğinden beri güncelleştirilmemiştir. Sonuç olarak, bazı yordamlar ve konular güncel olmayabilir veya yanlış olabilir. En son bilgiler için, çevrimiçi belge dizininde ilgilendiğiniz konuyu aramanız önerilir.
Bu notta, AFXPRIV.H içinde tanımlanan MBCS/Unicode dönüştürme makrolarının nasıl kullanılacağı açıklanmaktadır. Uygulamanız doğrudan OLE API'siyle ilgileniyorsa veya bir nedenle genellikle Unicode ile MBCS arasında dönüştürme yapması gerekiyorsa, bu makrolar en çok yararlıdır.
Genel Bakış
MFC 3.x'te, OLE arabirimleri çağrıldığında Unicode ve MBCS arasında otomatik olarak dönüştürmek için özel bir DLL (MFCANS32.DLL) kullanıldı. Bu DLL, OLE uygulamalarının her zaman Unicode olsalar bile (Macintosh dışında) OLE API'leri ve arabirimleri MBCS gibi yazılmasına izin veren neredeyse saydam bir katmandı. Bu katman kullanışlı olsa da ve uygulamaların Win16'dan Win32'ye hızla taşımasına izin verilse de (MFC, Microsoft Word, Microsoft Excel ve VBA, bu teknolojiyi kullanan Microsoft uygulamalarından yalnızca bazılarıdır), bazen önemli bir performans isabeti elde etmiştir. Bu nedenle, MFC 4.x bu DLL'yi kullanmaz ve bunun yerine doğrudan Unicode OLE arabirimleriyle konuşur. Bunu yapmak için, MFC'nin OLE arabirimine çağrı yaparken Unicode'u MBCS'ye dönüştürmesi ve genellikle OLE arabirimi uygularken Unicode'dan MBCS'ye dönüştürmesi gerekir. Bunu verimli ve kolay bir şekilde işlemek için, bu dönüştürmeyi kolaylaştırmak için bir dizi makro oluşturulmuştur.
Böyle bir makro kümesi oluşturmanın en büyük engellerinden biri bellek ayırmadır. Dizeler yerinde dönüştürülemediğinden, dönüştürülen sonuçları tutacak yeni bellek ayrılmalıdır. Bu, aşağıdakine benzer bir kodla yapılmış olabilir:
// 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;
Bir dizi sorun olarak bu yaklaşım. Asıl sorun, yazmak, test etmek ve hata ayıklamak için çok fazla kod olmasıdır. Basit bir işlev çağrısı olan bir şey artık çok daha karmaşıktır. Buna ek olarak, bunu yaparken önemli bir çalışma zamanı yükü vardır. Bellek yığında ayrılmalı ve dönüştürme her yapıldığında serbest olmalıdır. Son olarak, yukarıdaki kodun Unicode ve Macintosh derlemeleri için uygun #ifdefs
şekilde eklenmesi gerekir (bu dönüştürmenin gerçekleşmesi gerekmez).
Bu çözüm, 1) çeşitli platformlar arasındaki farkı maskeleyen ve 2) verimli bir bellek ayırma şeması kullanan ve 3) mevcut kaynak koduna kolayca ekleyebileceğiniz bazı makrolar oluşturmaktır. Tanımlardan birine bir örnek aşağıda verilmiştir:
#define A2W(lpa) (\
((LPCSTR)lpa == NULL) NULL : (\
_convert = (strnlen(lpa)+1),\
AfxA2WHelper((LPWSTR) alloca(_convert*2),
lpa,
_convert)\)\)
Yukarıdaki kod yerine bu makroyu kullanmak çok daha basittir:
// use it to call OLE here
USES_CONVERSION;
pI->SomeFunctionThatNeedsUnicode(T2OLE(lpszA));
Dönüştürmenin gerekli olduğu ek çağrılar vardır, ancak makroları kullanmak basit ve etkilidir.
Her makronun uygulanması yığın yerine yığından bellek ayırmak için _alloca() işlevini kullanır. Yığının belleğini ayırma işlemi yığına bellek ayırmaktan çok daha hızlıdır ve işlevden çıkıldığında bellek otomatik olarak boşaltılır. Buna ek olarak, makrolar birden çok kez çağrılmasını MultiByteToWideChar
(veya WideCharToMultiByte
) önler. Bu, gerekenden biraz daha fazla bellek ayırarak yapılır. MBC'nin en fazla bir WCHAR'ye dönüştürüleceğini ve her WCHAR için en fazla iki MBC bayta sahip olacağımızı biliyoruz. Gerekliden biraz daha fazla ayırarak, ancak dönüştürmeyi işlemek için her zaman yeterli olduğunda, dönüştürme işlevine ikinci çağrıdan kaçınılır. Yardımcı işlevine AfxA2Whelper
yapılan çağrı, dönüştürmeyi gerçekleştirmek için yapılması gereken bağımsız değişken gönderme sayısını azaltır (bu, doğrudan çağrılmaktan MultiByteToWideChar
daha küçük bir kodla sonuçlanabilir).
Makroların geçici bir uzunluğu depolamak için alana sahip olması için, dönüştürme makrolarını kullanan her işlevde bunu _convert adlı bir yerel değişkenin bildirilmesi gerekir. Bu, yukarıda örnekte görüldüğü gibi USES_CONVERSION makroyu çağırarak yapılır.
Hem genel dönüştürme makroları hem de OLE'ye özgü makrolar vardır. Bu iki farklı makro kümesi aşağıda açıklandı. Tüm makrolar AFXPRIV.H dosyasında bulunur.
Genel Dönüştürme Makroları
Genel dönüştürme makroları temel mekanizmayı oluşturur. Önceki A2W bölümünde gösterilen makro örneği ve uygulaması, bu tür "genel" makrolardan biridir. Özellikle OLE ile ilgili değildir. Genel makro kümesi aşağıda listelenmiştir:
A2CW (LPCSTR) -> (LPCWSTR)
A2W (LPCSTR) -> (LPWSTR)
W2CA (LPCWSTR) -> (LPCSTR)
W2A (LPCWSTR) -> (LPSTR)
Metin dönüştürmeleri yapmanın yanı sıra, , , DEVMODE
BSTR
ve OLE tarafından ayrılan dizeleri dönüştürmek TEXTMETRIC
için makrolar ve yardımcı işlevler de vardır. Bu makrolar bu tartışmanın kapsamı dışındadır; AFXPRIV'ye bakın. Bu makrolar hakkında daha fazla bilgi için H.
OLE Dönüştürme Makroları
OLE dönüştürme makroları, OLESTR karakterleri bekleyen işlevleri işlemek için özel olarak tasarlanmıştır. OLE üst bilgilerini incelerseniz, LPCOLESTR ve OLECHAR için birçok başvuru görürsünüz. Bu türler, OLE arabirimlerinde kullanılan karakterlerin türüne platforma özgü olmayan bir şekilde başvurmak için kullanılır. OLECHAR, Win16 ve Macintosh platformlarında ve Win32'de WCHAR ile eşlerchar
.
MFC kodundaki #ifdef yönergelerinin sayısını en düşük düzeyde tutmak için, OLE dizelerinin dahil olduğu her dönüştürme için benzer bir makromuz vardır. En yaygın kullanılan makrolar aşağıdakilerdir:
T2COLE (LPCTSTR) -> (LPCOLESTR)
T2OLE (LPCTSTR) -> (LPOLESTR)
OLE2CT (LPCOLESTR) -> (LPCTSTR)
OLE2T (LPCOLESTR) -> (LPCSTR)
TextMETRIC, DEVMODE, BSTR ve OLE ayrılmış dizeleri yapmak için de benzer makrolar vardır. Bkz. AFXPRIV. Daha fazla bilgi için H.
Diğer Konular
Makroları sıkı bir döngüde kullanmayın. Örneğin, aşağıdaki kod türünü yazmak istemezsiniz:
void BadIterateCode(LPCTSTR lpsz)
{
USES_CONVERSION;
for (int ii = 0; ii <10000; ii++)
pI->SomeMethod(ii, T2COLE(lpsz));
}
Yukarıdaki kod, dizenin lpsz
içeriğine bağlı olarak yığında megabayt bellek ayırmaya neden olabilir! Döngünün her yinelemesi için dizenin dönüştürülmesi de zaman alır. Bunun yerine, bu tür sabit dönüştürmeleri döngünün dışına taşıyın:
void MuchBetterIterateCode(LPCTSTR lpsz)
{
USES_CONVERSION;
LPCOLESTR lpszT = T2COLE(lpsz);
for (int ii = 0; ii <10000; ii++)
pI->SomeMethod(ii, lpszT);
}
Dize sabit değilse, yöntem çağrısını bir işleve kapsülleyin. Bu, dönüştürme arabelleğinin her seferinde serbest olmasını sağlar. Örnek:
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]);
}
Dönüş değeri, dönüş öncesinde verilerin bir kopyasının yapılması anlamına gelmediği sürece hiçbir zaman makrolardan birinin sonucunu döndürmeyin. Örneğin, bu kod kötüdür:
LPTSTR BadConvert(ISomeInterface* pI)
{
USES_CONVERSION;
LPOLESTR lpsz = NULL;
pI->GetFileName(&lpsz);
LPTSTR lpszT = OLE2T(lpsz);
CoMemFree(lpsz);
return lpszT; // bad! returning alloca memory
}
Yukarıdaki kod, dönüş değeri değeri kopyalayan bir değerle değiştirilerek düzeltilebilir:
CString BetterConvert(ISomeInterface* pI)
{
USES_CONVERSION;
LPOLESTR lpsz = NULL;
pI->GetFileName(&lpsz);
LPTSTR lpszT = OLE2T(lpsz);
CoMemFree(lpsz);
return lpszT; // CString makes copy
}
Makroları kullanmak ve kodunuz içine eklemek kolaydır, ancak yukarıdaki uyarılardan da anlayabileceğiniz gibi, bunları kullanırken dikkatli olmanız gerekir.