> Windows Sistemlerinde UNICODE ve ASCII uyumlu kodlar yazmak: Eskiden Windows'un 3'lü versiyonlarında işletim sistemi tamamen ASCII yazılarla çalışıyordu. Windows 95, Windows 98 ve Milenium versiyonlarında kernel ASCII olarak çalışıyordu. UNICODE yazılar ASCII'ye dönüştürülüp işlem yapılıyordu. Ancak Windows NT grubu sistemlerin kernel kodları tam tersine UNICODE olarak çalışıyordu. Bu kernel versiyonları ASCII yazıları UNICODE dönüştürerek işleme sokuyordu. Artık çok uzun bir süredir Windows kernel kodları yalnızca UNICODE kullanmaktadır. Dolayısıyla ASCII yazılar alan API fonksiyonları kernel tarafından UNICODE yazılara dönüştürülerek işleme sokulmaktadır. Bugün Windows sistemlerinde bütün yazı parametresi alan API fonksiyonları A'lı ve W'lu iki ayrı versiyon olarak bulunmaktadır. Örneğin yazı parametresi alan API fonksiyonunun ismi XXX olmak üzere aslında XXXA ve XXXW biçiminde iki ayrı API fonksiyonu vardır. XXX biçiminde bir API fonksiyonu yoktur. Örneğin CreateFile isimli bir API fonksiyonu aslında yoktur. CreateFileA ya da CreateFileW isimli API fonksiyonları vardır. İşte ileride açıklayacağımız biçimde biz kodumuzda CreateFile ismini kullandığımızda bu isim önişlemci tarafından aslında CreateFileA ya da CreateFileW biçimine dönüştürülmektedir. Pekiyi XXX isimli bir API fonksiyonu önişlemci tarafından nasıl XXXA ya da XXXW biçimine dönüştürülmektedir? İşte bunun için UNICODE isimli bir sembolik sabit kullanılmaktadır. dosyası içerisinde fonksiyonların isimlerini dönüştüren aşağıdaki makrolar bulunmaktadır: #ifdef UNICODE #define CreateFile CreateFileW #define CreateProcess CreateProcessW #define SetCurrentDirecory SetCurrentDirectoryW ... #else #define CreateFile CreateFileA #define CreateProcess CreateProcessA #define SetCurrentDirecory SetCurrentDirectoryA ... #endif Görüldüğü gibi yazı parametresi alan API fonksiyonlarının isimleri UNICODE sembolik sabitine bağlı olarak A'lı ya da W'lu biçime dönüştürülmektedir. UNICODE sembolik sabiti define edilmemişse program ASCII uyumlu define edilmişse UNICODE uyumlu hale gelmektedir. Eğer bu sembolik sabit define edilecekse dosyasının yukarısında define edilmeli ya da "-D seçeneği ile predefined" yapılmalıdır. Ancak Visual Studio IDE'sinde bu işlem de görsel olarak yapılabilmektedir. Proje seçeneklerine gelinip "Properties/Configuration Properties/Advanced"/Character Set" eğer "Use Unicode Character Set" olarak girilirse aslında IDE "cl" derleyicisini "/D UNICODE" komut satırı argümanını da ekleyerek çalıştırmaktadır. Dolayısıyla Visual Studio IDE'sinde çalışıyorsak bu seçeneği seçerek API fonksiyonlarının UNICODE versiyonlarının kullanılmasını sağlayabiliriz. Tabii bu seçenek seçilmezse UNICODE sembolik sabiti define edilmemiş olacağı için API fonksiyonlarının ASCII versiyonları kullanılacaktır. Ancak programımızı UNICODE uyumlu yazabilmek için yalnızca API fonksiyonlarının UNICODE versiyonlarının kullanılması yetmemektedir. Çünkü API fonksiyonlarının ASCII ve UNICODE versiyonlarının yazı parametreleri farklı türlerdendir. * Örnek 1, Aşağıda bir Windows API programının UNICODE olarak yazılmasını görüyorsunuz: #define UNICODE #include #include #include void ExitSys(LPCWSTR lpszMsg); int main(void) { HANDLE hFile; if ((hFile = CreateFile(L"test.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) ExitSys(L"CreateFile"); printf("success...\n"); CloseHandle(hFile); return 0; } void ExitSys(LPCWSTR lpszMsg) { DWORD dwLastErr = GetLastError(); LPWSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { fwprintf(stderr, L"%s: %s", lpszMsg, lpszErr); LocalFree(lpszErr); } exit(EXIT_FAILURE); } İşte bir Windows programını UNICODE uyumlu yazmak demek "yalnızca UNICODE ve _UNICODE sembolik sabitlerine dayalı olarak kodda hiçbir değişiklik yapılmadan kodun hem ASCII hem de UNICODE derlemesinde sorun çıkarmaması" demektir. UNICODE uyumlu Windows API kodu yazabilmek için, -> Program içerisindeki tüm string'ler TEXT makrosuyla ya da _TEXT makrosuyla kullanılmalıdır. TEXT makrosu içerisinde _TEXT makrosu ise içerisinde bulunmaktadır. dosyası da Microsoft spesifik bir dosyadır. Biz TEXT makrosunu kullanacaksak dosyasının include edilmesi yeterlidir. Ancak biz _TEXT makrosunu kullanacaksak dosyasının include edilmesi gerekir. Genel olarak programcılar her iki dosyayı da include ederler. Başı "_" ile başlayan ve ilk harfi büyük olan tüm isimler "reserved" olduğu için _TEXT makrosunun kullanılması C için daha uygun gibi görünmektedir. İki makronun da yaptığı işlem aynıdır. TEXT makrosu UNICODE sembolik sabitine, _TEXT makrosu ise _UNICODE sembolik sabitine bakmaktadır. TEXT makrosu şöyle yazılmıştır: #ifdef UNICODE #define TEXT(s) L##s #else #define TEXT(s) s #endif _TEXT makrosu ise şöyle yazılmıştır: #ifdef _UNICODE #define _TEXT(s) L##s #else #define _TEXT(s) s #endif Genellikle programcıların hem UNICODE hem de _UNICODE sembolik sabitlerini define etmeleri gerekmektedir. Zaten Visual Studio IDE'sinde "character set" UNICODE olarak değiştirildiğinde her iki sembolik sabit de define edilmektedir. -> char türü yerine TCHAR isimli typedef türünün kullanılması gerekir. Çünkü TCHAR türü UNICODE sembolik sabitine göre char ya da wchar_t türü anlamına gelmektedir. TCHAR makrosu içerisinde aşağıdaki gibi oluşturulmuştur: #ifdef UNICODE #define TCHAR wchar_t #else #define TCHAR char #endif Karakter sabitleri için de içerisindeki _T makrosu kullanılmaktadır. Bu makro da şöyle oluşturulmuştur: #ifdef _UNICODE #define _T(c) L##c #else #define _T(c) c #endif -> LPCSTR const "char *" anlamına, LPSTR ise "char *" anlamına gelir. Bunların UNICODE versiyonları LPCWSTR ve LPWSTR biçimindedir. Ancak T li versiyonlar yine UNICODE sembolik sabitine bağlı olarak "char *" ya da "wchar_t *" türünü temsil etmektedir. Bu nedenle yazılara ilişkin gösterici parametreleri ya "TCHAR *" ve "const TCHAR *" biçiminde ya da LPTSTR ve LPCTSTR biçiminde belirtilmelidir. Burada örneğin LPCSTR türü ile LPCTSTR türü arasındaki farka dikkat ediniz. LPCSTR türü her zaman "const char *" anlamına gelmektedir. Halbuki LPCTSTR türü duruma göre "const char *" duruma göre ise "const wchar_t *" anlamına gelmektedir. -> Standart C fonksiyonları için de aynı durum söz konusudur. Örneğin ASCII programlarda printf kullanılırken UNICODE programlarda wprintf kullanılmaktadır. Ancak uyumlu printf ismi için yine dosyası içerisindeki _xxx biçimindeki özel fonksiyon isimleri kullanılır. Her standart C fonksiyonun Windows sistemlerindeki UNICODE uyumlu ismi bilinmelidir. Örneğin _tprintf ismi printf fonksiyonun UNICODE uyumlu ismidir. Bu isim _UNICODE sembolik sabiti define edilmişse "wprintf" olarak, edilmemişse printf olarak değiştirilmektedir. -> Microsoft aslında main fonksiyonun da iki versiyonunu bulundurmaktadır: main ve wmain. Her ne kadar C standartlarında wmain diye bir fonskiyon yoksa da Microsoft Windows sistemlerinde main fonksiyonunun UNICODE versiyonu wmain biçimindedir. Yani siz eğer main fonksiyonunun argv parametresinin UNICODE olmasını istiyorsanın artık main yerine wmain kullanmalısınız. main fonksiyonunun _UNICODE uyumlu ismi _tmain biçimindedir. O halde main fonksiyonu da şöyle tanımlanabilir: int _tmain(int args, TCHAR *argv[]) { //... } gibi hususlara dikkat etmeliyiz. * Örnek 1, Aşağıda basit bir Windows API programının UNICODE uyumlu yazımı verilmiştir. #include #include #include #include void ExitSys(LPCTSTR lpszMsg); int _tmain() { HANDLE hFile; TCHAR path[MAX_PATH] = _TEXT("test.txt"); if ((hFile = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) ExitSys(_TEXT("CreateFile")); _tprintf(_TEXT("Ok")); CloseHandle(hFile); return 0; } void ExitSys(LPCTSTR lpszMsg) { DWORD dwLastErr = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { _ftprintf(stderr, _TEXT("%s: %s"), lpszMsg, lpszErr); LocalFree(lpszErr); } exit(EXIT_FAILURE); } * Örnek 2, Şimdi de daha önce ASCII uyumlu olarak yazdığımız proses listesini alan programı UNICODE uyumlu olarak yazalım. Aşağıdaki örneği inceleyiniz. Programda yukarıda sıraladığımız maddelere uyulduğuna dikkat ediniz. #include #include #include #include #include #define NPROCESSES 4096 #define NMODULES 4096 void ExitSys(LPCTSTR lpszMsg); int _tmain(void) { DWORD dwProcessIds[NPROCESSES]; DWORD cbNeeded; HANDLE hProcess; HMODULE hModules[NMODULES]; DWORD dwProcessCount; DWORD dwModuleCount; TCHAR szModuleName[MAX_PATH]; if (!EnumProcesses(dwProcessIds, sizeof(dwProcessIds), &cbNeeded)) ExitSys(_TEXT("EnumProcesses")); dwProcessCount = cbNeeded / sizeof(DWORD); for (DWORD i = 0; i < dwProcessCount; ++i) { if ((hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessIds[i])) == NULL) continue; if (!EnumProcessModules(hProcess, hModules, sizeof(hModules), &cbNeeded)) { CloseHandle(hProcess); continue; } dwModuleCount = cbNeeded / sizeof(DWORD); for (DWORD k = 0; k < dwModuleCount; ++k) { if (!GetModuleBaseName(hProcess, hModules[k], szModuleName, MAX_PATH)) continue; if (k == 0) { _tprintf(_TEXT("%s: "), szModuleName); continue; } if (k != 1) _tprintf(_TEXT(", ")); _tprintf(_TEXT("%s"), szModuleName); } _tprintf(_TEXT("\n\n")); CloseHandle(hProcess); } _tprintf(_TEXT("%lu process(es) listed...\n"), dwProcessCount); getchar(); return 0; } void ExitSys(LPCTSTR lpszMsg) { DWORD dwLastError = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { _ftprintf(stderr, _TEXT("%s: %s"), lpszMsg, lpszErr); LocalFree(lpszErr); } exit(EXIT_FAILURE); } Gördüğünüz üzere programların UNICODE uyumlu yazılması konusu Microsoft Windows sistemlerine özgüdür. UNIX/Linux sistemlerinde böyle bir konu yoktur. Bütün POSIX fonksiyonları ASCII uyumludur. Dolayısıyla Windowssistemlerinde olduğu gibi bir POSIX fonksiyonunun UNICODE ve ASCII versiyonları yoktur. Zaten Linux sistemlerinde UNICODE gerektiği zaman UNICODE UTF-8 encoding'i kullanılmaktadır. Bu encoding de "multibyte" bir kodlama oluşturduğu için char * türü ile ifade edilebilmektedir.