You've decided to create a Win32 Unicode application. Following the tips and examples included here will help you to get underway and to perform your tasks efficiently-whether you're converting between code-page encodings and Unicode, compiling your Unicode application in Microsoft Visual C++, migrating your code page-based application to Unicode, or writing Unicode code. Although adopting the Unicode encoding system for your application is only a first step toward fully globalized software, it certainly is the most important factor in multilingual computing. In fact, any language version of a Unicode Windows application can run on any language version of Windows 2000 and Windows XP, regardless of the user's settings or the language version of the operating system.
As mentioned earlier, Windows 2000 and Windows XP use Unicode as their default encoding system. Whenever dealing with data based on code pages, the data is internally converted to UTF-16 and processed in Unicode. This means that a legacy or non-Unicode application's behavior depends on the system's conversion from code-page encoding to Unicode. The system performs this conversion based on the value of the default code page of what is called the system locale. Windows XP refers to this variable as "Language for non-Unicode programs" (found when clicking the Advanced tab within the Regional And Language Options property sheet). (See Figure 3-9.) For each system, this value can only be changed or set by a system administrator and requires the computer to be restarted. The primary effect of the system-locale value is to set the internal system's conversion from the code page to Unicode and vice versa.
Figure 3.9 - Windows XP Regional And Language Options property sheet, Advanced tab.
In Figure 3-9, the system locale is set to Russian (Windows code page 1251). This means that if you are not dealing with a Unicode application, the default text encoding in the application has to be either 1251 or ASCII. All applications built on other encodings will fail, since the system will be using the Russian code page to convert between the code page and Unicode, unless they implement their own code-page conversion mechanism. Figure 3-10 shows how a message box from a non-Unicode Arabic application running with an English system locale would appear. The conversion between code page and Unicode fails to map to appropriate Arabic characters, and instead randomly maps to extended Windows 1252 characters.
Figure 3.10 - Non-Unicode application running on mismatched system locales.
Porting existing code page-based applications to Unicode is easier than you might think. In fact, Unicode was implemented in such a way as to make writing Unicode applications almost transparent to developers. Unicode also needed to be implemented in such a way as to ensure that non-Unicode applications remain functional whenever running in a pure Unicode platform. To accommodate these needs, the implementation of Unicode required changes in two major areas:
Most string operations for Unicode can be coded with the same logic used for handling the Windows character set. The difference is that the basic unit of operation is a 16-bit quantity instead of an 8-bit one. The header files provide a number of type definitions that make it easy to create sources that can be compiled for Unicode or the Windows character set.
For 8-bit (ANSI) and double-byte characters: typedef char CHAR; // 8-bit character typedef char *LPSTR; // pointer to 8-bit string For Unicode (wide) characters: typedef unsigned short WCHAR; // 16-bit character typedef WCHAR *LPWSTR; // pointer to 16-bit string
Figure 3-11 shows the method by which the Win32 header files define three sets of types:
With generic declarations, it is possible to maintain a single set of source files and compile them for either Unicode or ANSI support.
Figure 3.11 - WCHAR, a new data type.
All Win32 APIs that take a text argument either as an input or output variable have been provided with a generic function prototype and two definitions: a version that is based on code pages or ANSI (called "A") to handle code page-based text argument and a wide version (called "W ") to handle Unicode. The generic function prototype consists of the standard API function name implemented as a macro. The generic prototype gets resolved into one of the explicit function prototypes ("A " or "W "), depending on whether the compile-time manifest constant UNICODE is defined in a #define statement. The letter "W" or "A" is added at the end of the API function name in each explicit function prototype.
// windows.h #ifdef UNICODE #define SetWindowText SetWindowTextW #else #define SetWindowText SetWindowTextA #endif // UNICODE
With this mechanism, an application can use the generic API function to work transparently with Unicode, depending on the #define UNICODE manifest constant. It can also make mixed calls by using the explicit function name with "W" or "A."
One function of particular importance in this dual compile design is RegisterClass (and RegisterClassEx). A window class is implemented by a window procedure. A window procedure can be registered with either the RegisterClassA or RegisterClassW function. By using the function's "A" version, the program tells the system that the window procedure of the created class expects messages with text or parameters that are based on code pages; other objects associated with the window are created using a Windows code page as the encoding of the text. By registering the window class with a call to the wide-character version of the function, the program can request that the system pass text parameters of messages as Unicode. The IsWindowUnicode function allows programs to query the nature of each window.
On Windows NT 4, Windows 2000, and Windows XP, "A" routines are wrappers that convert text that is based on code pages or ANSI to Unicode-using the system-locale code page-and that then call the corresponding "W" routine. On Windows 95, Windows 98, and Windows Me, "A" routines are native, and most "W" routines are not implemented. If a "W" routine is called and yet not implemented, the ERROR_CALL_NOT_ IMPLEMENTED error message is returned. (For more information on how to write Unicode-based applications for non-Unicode platforms, see Chapter 18, "Microsoft Layer for Unicode [MSLU].")
Visual C++ lets you prefix a literal with an "L" to indicate it is a Unicode string, as shown here:
LPWSTR str = L"This is a Unicode string";
In the source file, the string is expressed in the code page that the editor or compiler understands. When compiled, the characters are converted to Unicode. The Win32 SDK resource compiler also supports the "L" prefix notation, even though it can interpret Unicode source files directly. WINDOWS.H defines a macro called TEXT() that will mark string literals as Unicode, depending on whether the UNICODE compile flag is set.
#ifdef UNICODE #define TEXT(string) L#string #else #define TEXT(string) string #endif // UNICODE
So, the generic version of a string of characters should become:
LPTSTR str = TEXT("This is a generic string");
The Unicode data type is compatible with the wide-character data type wchar_t in ANSI C, thus allowing access to the wide-character string functions. Most of the C run-time (CRT) libraries contain wide-character versions of the strxxx string functions. The wide-character versions of the functions all start with wcs. (See Table 3-5.)
Table 3-5 Examples of C run-time library routines used for string manipulation.
8-bit Character Sets
The C run-time library also provides such functions as mbtowc and wctomb, which can translate the C character set to and from Unicode. The more general set of functions of the Win32 API can perform the same functions as the C run-time libraries including conversions between Unicode, Windows character sets, and MS-DOS code pages. In Windows programming, it is highly recommended that you use the Win32 APIs instead of the CRT libraries in order to take advantage of locale-aware functionality provided by the system, as described in Chapter 4. (See Table 3-6.)
Table 3-6 Equivalent Win32 API functions for the C run-time library routines found in Table 3-5.
8-bit Character Sets
Since a large number of applications are still code page-based, and since you might want to support Unicode internally, there are a lot of occasions where a conversion between code-page encodings and Unicode is necessary. The pair of Win32 APIs,MultiByteToWideChar and WideCharToMultiByte, allow you to convert code-page encoding to Unicode and Unicode data to code-page encoding, respectively. Each of these APIs takes as an argument the value of the code page to be used for that conversion. You can, therefore, either specify the value of a given code page (example: 1256 for Arabic) or use predefined flags such as:
(For more information, see the Microsoft Developer Network [MSDN] documentation at http://msdn.microsoft.com.)
By using MultiByteToWideChar and WideCharToMultiByte consecutively, using the same code-page information, you do what is called a "round trip." If the code-page number that is used in this encoding conversion is the same as the code-page number that was used in encoding the original string, the round trip should allow you to retrieve the initial character string.
By using the generic data types and function prototypes, you have the liberty of creating a non-Unicode application or compiling your software as Unicode. To compile an application as Unicode in Visual C/C++, go to Project/Settings/C/C++ /General, and include UNICODE and _UNICODE in Preprocessor Definitions. The UNICODE flag is the preprocessor definition for all Win32 APIs and data types, and _UNICODE is the preprocessor definition for C run-time functions.
Creating a new program based on Unicode is fairly easy. Unicode has a few features that require special handling, but you can isolate these in your code. Converting an existing program that uses code-page encoding to one that uses Unicode or generic declarations is also straightforward. Here are the steps to follow:
In the following example, a string is loaded from the resources and is used in two scenarios:
(For more information on resources, see Chapter 7, "Software Localizability Guidelines.")
For the purpose of simplification, this example will ignore where and how irrelevant variables have been defined. Suppose you want to migrate the following code page-based code to Unicode:
char g_szTemp[MAX_STR]; // Definition of a char data type // Loading IDS_SAMPLE from the resources in our char variable LoadString(g_hInst, IDS_SAMPLE, g_szTemp, MAX_STR); // Using the loaded string as the body of the message box MessageBox(NULL, g_szTemp, "This is an ANSI message box!", MB_OK); // Using the loaded string in a call to TextOut for drawing at // run time ExtTextOut(hDC, 10, 10, ETO_CLIPPED , NULL, g_szTemp, strlen(g_szTemp), NULL);
Migrating this code to Unicode is as easy as following the generic coding conventions and properly replacing the data type, Win32 APIs, and C run-time API definitions. You can see the changes in bold typeface.
#include <tchar.h> // Include wchar specific header file TCHAR g_szTemp[MAX_STR]; // Definition of the data type as a// generic variable // Calling the generic LoadString and not W or A versions explicitly LoadString(g_hInst, IDS_SAMPLE, g_szTemp, MAX_STR); // Using the appropriate text macro for the title of our message box MessageBox(NULL, g_szTemp, TEXT("This is a Unicode message box."), MB_OK); // Using the generic run-time version of strlen ExtTextOut(hDC, 10, 10, ETO_CLIPPED , NULL, g_szTemp, _tcslen(g_szTemp), NULL);
After implementing these simple steps, all that is left to do in order to create a Unicode application is to compile your code as Unicode by defining the compiling flags UNICODE and _UNICODE.
Depending on your needs and your target operating systems, there are several options for migration from an application that is based on code pages or to one that is based on Unicode. Some of these options do have certain caveats, however.
Disadvantage: Maintaining two versions of your software is messy and goes against the principle of a single, worldwide binary, introduced in Chapter 2, "Designing a World-Ready Program."
Disadvantage: Since Windows does not support the creation of custom code pages, you will not be able to use scripts that are supported only through Unicode (such as those in the Indic family of languages, Armenian, and Georgian). Also, this option makes multilingual computing impossible since, when it comes to displaying, your application is always limited to the system's code page.
Disadvantage: This works only on Windows NT, Windows 2000, and Windows XP, since only limited Unicode support is provided on legacy platforms. This is the preferred approach if you are only targeting Unicode platforms.
When writing Unicode code, there are many points to consider, such as when to use UTF-16, when to use UTF-8, what to use for compression, and so forth. The following are recommended practices that will help ensure you choose the best method based on the circumstance at hand.
You've now seen techniques and code samples for creating Win32 Unicode applications. Unicode is also extremely useful for dealing with Web content in the global workplace and market. Knowing how to handle encoding in Web pages will help bridge the gap between the plethora of languages that are in use today within Web content.