Bagikan melalui


TN059: Menggunakan Makro Konversi MFC MBCS/Unicode

Catatan

Catatan teknis berikut belum diperbarui sejak pertama kali disertakan dalam dokumentasi online. Akibatnya, beberapa prosedur dan topik mungkin kedaluarsa atau salah. Untuk informasi terbaru, disarankan agar Anda mencari topik yang menarik dalam indeks dokumentasi online.

Catatan ini menjelaskan cara menggunakan makro untuk konversi MBCS/Unicode, yang ditentukan dalam AFXPRIV.H. Makro ini paling berguna jika aplikasi Anda berurusan langsung dengan OLE API atau karena alasan tertentu, sering kali perlu mengonversi antara Unicode dan MBCS.

Gambaran Umum

Di MFC 3.x, DLL khusus digunakan (MFCANS32.DLL) untuk secara otomatis mengonversi antara Unicode dan MBCS ketika antarmuka OLE dipanggil. DLL ini adalah lapisan yang hampir transparan yang memungkinkan aplikasi OLE ditulis seolah-olah API dan antarmuka OLE adalah MBCS, meskipun selalu Unicode (kecuali pada Macintosh). Meskipun lapisan ini nyaman dan memungkinkan aplikasi untuk di-porting dengan cepat dari Win16 ke Win32 (MFC, Microsoft Word, Microsoft Excel, dan VBA, hanyalah beberapa aplikasi Microsoft yang menggunakan teknologi ini), itu kadang-kadang memiliki hit performa yang signifikan. Untuk alasan ini, MFC 4.x tidak menggunakan DLL ini dan sebaliknya berbicara langsung ke antarmuka Unicode OLE. Untuk melakukan ini, MFC perlu mengonversi ke Unicode ke MBCS saat melakukan panggilan ke antarmuka OLE, dan sering kali perlu mengonversi ke MBCS dari Unicode saat menerapkan antarmuka OLE. Untuk menangani ini secara efisien dan mudah, sejumlah makro dibuat untuk mempermudah konversi ini.

Salah satu rintangan terbesar dalam membuat sekumpulan makro seperti itu adalah alokasi memori. Karena string tidak dapat dikonversi di tempat, memori baru untuk menahan hasil yang dikonversi harus dialokasikan. Ini bisa dilakukan dengan kode yang mirip dengan yang berikut:

// 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;

Pendekatan ini sebagai sejumlah masalah. Masalah utamanya adalah banyak kode untuk menulis, menguji, dan men-debug. Sesuatu yang merupakan panggilan fungsi sederhana, sekarang jauh lebih kompleks. Selain itu, ada overhead runtime yang signifikan dalam melakukannya. Memori harus dialokasikan pada tumpukan dan dibebaskan setiap kali konversi dilakukan. Akhirnya, kode di atas harus memiliki tambahan yang sesuai #ifdefs untuk build Unicode dan Macintosh (yang tidak mengharuskan konversi ini terjadi).

Solusi yang kami buat adalah membuat beberapa makro yang 1) menutupi perbedaan antara berbagai platform, dan 2) menggunakan skema alokasi memori yang efisien, dan 3) mudah dimasukkan ke dalam kode sumber yang ada. Berikut adalah contoh salah satu definisi:

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

Menggunakan makro ini alih-alih kode di atas dan hal-hal jauh lebih sederhana:

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

Ada panggilan tambahan di mana konversi diperlukan, tetapi menggunakan makro sederhana dan efektif.

Implementasi setiap makro menggunakan fungsi _alloca() untuk mengalokasikan memori dari tumpukan alih-alih tumpukan. Mengalokasikan memori dari tumpukan jauh lebih cepat daripada mengalokasikan memori pada tumpukan, dan memori secara otomatis dikosongkan ketika fungsi keluar. Selain itu, makro menghindari panggilan MultiByteToWideChar (atau WideCharToMultiByte) lebih dari satu kali. Ini dilakukan dengan mengalokasikan sedikit lebih banyak memori daripada yang diperlukan. Kita tahu bahwa MBC akan mengonversi menjadi paling banyak satu WCHAR dan untuk setiap WCHAR kita akan memiliki maksimum dua byte MBC. Dengan mengalokasikan sedikit lebih dari yang diperlukan, tetapi selalu cukup untuk menangani konversi panggilan kedua panggilan kedua ke fungsi konversi dihindari. Panggilan ke fungsi AfxA2Whelper pembantu mengurangi jumlah dorongan argumen yang harus dilakukan untuk melakukan konversi (ini menghasilkan kode yang lebih kecil, daripada jika dipanggil MultiByteToWideChar secara langsung).

Agar makro memiliki ruang untuk menyimpan panjang sementara, perlu untuk mendeklarasikan variabel lokal yang disebut _convert yang melakukan ini di setiap fungsi yang menggunakan makro konversi. Ini dilakukan dengan memanggil makro USES_CONVERSION seperti yang terlihat di atas dalam contoh.

Ada makro konversi generik dan makro khusus OLE. Kedua set makro yang berbeda ini dibahas di bawah ini. Semua makro berada di AFXPRIV.H.

Makro Konversi Generik

Makro konversi generik membentuk mekanisme yang mendasar. Contoh makro dan implementasi yang ditunjukkan di bagian sebelumnya, A2W, adalah salah satu makro "generik" seperti itu. Ini tidak terkait dengan OLE secara khusus. Kumpulan makro generik tercantum di bawah ini:

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

Selain melakukan konversi teks, ada juga makro dan fungsi pembantu untuk mengonversi TEXTMETRICstring yang dialokasikan , , DEVMODEBSTR, dan OLE. Makro ini berada di luar cakupan diskusi ini - lihat AFXPRIV. H untuk informasi selengkapnya tentang makro tersebut.

Makro Konversi OLE

Makro konversi OLE dirancang khusus untuk menangani fungsi yang mengharapkan karakter OLESTR . Jika Anda memeriksa header OLE, Anda akan melihat banyak referensi ke LPCOLESTR dan OLECHAR. Jenis ini digunakan untuk merujuk ke jenis karakter yang digunakan dalam antarmuka OLE dengan cara yang tidak spesifik untuk platform. OLECHAR memetakan ke char di platform Win16 dan Macintosh dan WCHAR di Win32.

Untuk menjaga jumlah arahan #ifdef dalam kode MFC minimal, kami memiliki makro serupa untuk setiap konversi tempat string OLE terlibat. Makro berikut adalah yang paling umum digunakan:

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

Sekali lagi, ada makro serupa untuk melakukan string TEXTMETRIC, DEVMODE, BSTR, dan OLE yang dialokasikan. Lihat AFXPRIV. H untuk informasi lebih lanjut.

Pertimbangan Lainnya

Jangan gunakan makro dalam perulangan yang ketat. Misalnya, Anda tidak ingin menulis jenis kode berikut:

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

}

Kode di atas dapat mengakibatkan alokasi megabyte memori pada tumpukan tergantung pada apa isi stringnya lpsz ! Diperlukan juga waktu untuk mengonversi string untuk setiap iterasi perulangan. Sebagai gantinya, pindahkan konversi konstan seperti itu dari perulangan:

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

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

}

Jika string tidak konstan, maka merangkum panggilan metode ke dalam fungsi. Ini akan memungkinkan buffer konversi dibebaskan setiap kali. Contohnya:

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]);

}

Jangan pernah mengembalikan hasil salah satu makro, kecuali nilai pengembalian menyiratkan pembuatan salinan data sebelum pengembalian. Misalnya, kode ini buruk:

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

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // bad! returning alloca memory
}

Kode di atas dapat diperbaiki dengan mengubah nilai pengembalian menjadi sesuatu yang menyalin nilai:

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

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // CString makes copy
}

Makro mudah digunakan dan mudah dimasukkan ke dalam kode Anda, tetapi seperti yang Anda tahu dari peringatan di atas, Anda harus berhati-hati saat menggunakannya.

Baca juga

Catatan Teknis menurut Angka
Catatan Teknis menurut Kategori