There are a number of topics that have not been covered elsewhere in this book. The four topics gathered together in this chapter have little in common other than a reputation as advanced or intimidating. Typically you will not use these techniques on your first project, though they are not, in fact, as difficult as many people imagine. Once you are comfortable generating ordinary Windows applications with AppWizard, consider these techniques to move you beyond ordinary Windows programming.
Console applications are also referred to as DOS applications. Interestingly enough you can still create applications with MS-Visual C++ that run under DOS. More importantly they can still play a beneficial role in software development, even Windows 95 or Windows NT software development. That's right, you heard it here first. There are still a few great uses for DOS applications, including the ftp and telnet console applications that come with Windows 95 and NT. But an important use of Console apps for Windows developers is a technique called scaffolding, used to test objects before integrating them into a large application.
A Console (or DOS) application is still executed from the DOS command line or by choosing Start, Run and typing the full name of the application. Console applications are probably still among the easiest programs to create and this version of the compiler supports them directly.
You can also invoke a DOS box from the "programs" item in the Start Menu. BOB. Start button
Let's walk together through the few steps necessary to create a basic Console application and then we'll explore some beneficial uses of creating these kinds of applications. The first Console application we'll create is a spin on the classic "Hello, World!" Kernighan and Ritchie-the creators of C++'s ancestor C-created in the 1970s.
Open the Microsoft Developer Studio and follow these steps to create a Console application:
NOTE |
Although this application may be small, Visual C++ creates a lot of overhead files. One small application may produce 3 or 4 megabytes of intermediate files. Developers in general need plenty of disk space. When compiled using a Debug Build, HelloWorld requires about 850K bytes on my system. |
If you followed the steps as described above precisely and did not make any prior modifications to the Microsoft Developer Studio, the code in Listing 32.1 should have compiled error-free and produced the output:
"Welcome to Valhalla Tower Material Defender."
from the cool game "Descent II" from Interplay.
Listing 32.1 (HelloWorld.Cpp) "Welcome to Valhalla Tower..."
// HelloWorld.Cpp - A new spin on an old classic<g>. // Copyright (c) 1996. All Rights Reserved. // by Paul Kimmel. Okemos, MI USA #include <iostream.h> int main() { const char * const GREETINGS = "Welcome to Valhalla Tower Material Defender.\n"; cout << GREETINGS; return 0; }
Building a trivial application is a place to start. There are still many software developers living in a DOS world, but better yet, there are still valid uses for DOS-based applications. One of these is prototyping, as we'll discuss in the next section.
The best argument for anyone to build a DOS application these days is to scaffold small code fragments. This refers to building a temporary framework around the code you want to test. The simplest possible framework is a console application like the one you just built.
Further, if such a scaffold cannot be built around your algorithm, then management and other computer scientists may have a valid argument for calling into question the completeness and robustness of the design.
By the time many single functions make it into your application, they themselves should have been tested in smaller programs a few times at least. While it is a subjective call how many lines of code require a scaffold, it is a simple enough process to scaffold at the function level. If we are reusing those functions, they will be incorporated in other functions that are scaffolded too.
One possible algorithm you might want to test is a sorting algorithm. This section will present a template quicksort algorithm.
To scaffold the quicksort or any subprogram, the technique involves:
Having followed those steps, you can now test the quicksort code (Listings 32.2 and 32.3) thoroughly, focusing only on the performance characteristics and correctness of the quicksort. Scaffolding holds true to the canon of software development which states: "Design in the large and program in the small."
Listing 32.2 (SORTS.H) The Header File Contains the RecursiveQuicksort Function Declaration
// SORTS.H - Contains global sort function declarations. // Copyright (c) 1996. All Rights Reserved. // by Paul Kimmel. Okemos, MI USA #ifndef __SORT_H #define __SORTS_H // A technique for 'measuring' the number of elements in an array. #define SIZEOF( array ) sizeof(array) / sizeof( array[0] ) template <class T> inline void Swap( T& a, T& b ){ T t = a; a = b; b = t; } // The larger stack space of 32-bit platforms enables us to more // reliably use recursive functions, being less likely to underflow the stack. template <class T> void RecursiveQuickSort( T data[], unsigned long left, unsigned long right ); #endif
Listing 32.3 (SORTS.CPP) The Quicksort and Parsing Function Definitions and Scaffold
// SORTS.CPP - Contains the definitions and some test scaffolds // Copyright (c) 1995. All Rights Reserved. // By Paul Kimmel. #include "sorts.h" // Partitioning function for the RecursiveQuickSort. template <class T> unsigned long Partition( T data[], unsigned long left, unsigned long right ) { unsigned long j, k; T v = data[right]; j = left - 1; k = right; for(;;) { while( data[++j] < v ) ; while( data[--k] > v ) ; if( j >= k ) break; Swap(data[j], data[k]); }; Swap(data[j], data[right] ); return j; } // The QuickSort Function. template <class T> void RecursiveQuickSort( T data[], unsigned long left, unsigned long right) { unsigned long j; if( right > left ) { j = Partition(data, left, right ); RecursiveQuickSort(data, left, j-1); RecursiveQuickSort(data, j+1, right ); } } #ifdef SORTS_SCAFFOLD /* The scaffold is used only for testing the RecursiveQuickSort * template function. By defining SORTS_SCAFFOLD project-wide we * can easily remove the define, leaving the scaffold in place for * another time. For instance, we may want to optimize the quicksort, * or add additional functionality to this module. * An added benefit is that the scaffold code also demonstrates * to other users how to use the quicksort. */ #include <iostream.h> // Contains cout object #include <stdlib.h> // Contains srand() and rand() functions #include <time.h> // Definition of time() int main() { srand((unsigned long)time(NULL)); // Seed the random number generator unsigned long elems[10]; const unsigned long MAX = SIZEOF( elems ); // Fill the elems with random numbers. for( unsigned long i = 0; i < MAX; i++ ) elems[i] = rand(); // Sort the elements, specifying the template type implicitly unsigned long l = 0, r = MAX - 1; RecursiveQuickSort(elems, l, r ); for( i = 0; i < MAX; i++ ) cout << i << ".=\t" << elems[i] << endl; return 0; } #endif
TIP |
The sort code is a template, so that this code can be used to sort numbers, strings, or other kinds of objects. Templates are covered in the "Templates" section of Chapter 30, "Power-User C++ Features." |
By applying a scaffold to any algorithm you are helping to ensure accuracy. Remember, there are additional benefits involved, too: by placing the scaffold code directly into the module you are clearly documenting that the code has been tested and how to use it. You make it available for further testing, debugging, or extending at a later date.
Dynamic Link Libraries (DLL) are still the backbone of the Windows 95 and Windows NT operating systems. Windows 95 uses Kernel32.Dll, User32.Dll, and Gdi32.Dll to perform the vast majority of its work, and you can use them as well. The Microsoft Visual C++ On-line Books are a good source of information for API functions found in these three DLLs.
Another tool for poking around in Windows applications is the DumpBin utility in \MSDEV\BIN. This utility is a command line program that will show you the imports and exports of executable files and dynamic link libraries. Listed on the following pages is an excerpted example of the output produced when using DumpBin to examine the executable file for Spy++, one of the utilities provided with Visual C++.
dumpbin -imports spyxx.exe Microsoft (R) COFF Binary File Dumper Version 3.00.5270 Copyright (C) Microsoft Corp 1992-1995. All rights reserved. Dump of file spyxx.exe File Type: EXECUTABLE IMAGE Section contains the following Imports USER32.dll 167 LoadCursorA 135 GetWindowTextA 1DF SetDlgItemTextW 153 IsChild D7 GetClassLongA D8 GetClassLongW C5 FillRect 165 LoadBitmapA 16B LoadIconA E6 GetDC 1f4 SetRectEmpty 15B IsRectEmpty 1f3 SetRect 141 InflateRect 1FE SetTimer 162 KillTimer 1CF SetActiveWindow 249 wsprintfA 80 DeleteMenu 122 GetSystemMenu 1A1 PeekMessageA 100 GetLastActivePopup 2 AdjustWindowRectEx 4 AppendMenuA 51 CreatePopupMenu C1 EnumWindows B1 EnumChildWindows 188 MessageBoxA 206 SetWindowPlacement A BringWindowToTop 197 OffsetRect 132 GetWindowPlacement 13A GetWindowWord 15E IsWindowUnicode 243 WinHelpA DF GetClipboardFormatNameA 12F GetWindowDC 160 IsZoomed 1B9 ReleaseDC D0 GetCapture 33 ClientToScreen 246 WindowFromPoint 1D8 SetCursor 237 UpdateWindow 12D GetWindow 15F IsWindowVisible E8 GetDesktopWindow 204 SetWindowLongA 1B8 ReleaseCapture 1D0 SetCapture 1A8 PtInRect F0 GetFocus 120 GetSysColor CB FrameRect 9B DrawFocusRect f9 GetKeyState 187 MessageBeep 22C TranslateMessage 8C DispatchMessageA 159 IsIconic 1 AdjustWindowRect 133 GetWindowRect 1BF ScreenToClient C2 EqualRect 148 InvalidateRect DC GetClientRect 123 GetSystemMetrics 15C IsWindow D9 GetClassNameA 130 GetWindowLongA D3 GetClassInfoA 1A3 PostMessageA 20E SetWindowsHookExA 22E UnhookWindowsHookEx 7D DefWindowProcA 139 GetWindowThreadProcessId 1AF RegisterClipboardFormatA DB GetClassWord 86 DestroyWindow 1AB RegisterClassA 52 CreateWindowExA AB EnableWindow 1C6 SendMessageA 115 GetParent 1E2 SetForegroundWindow 232 UnpackDDElParam 216 ShowWindow Summary 10000 .data 3000 .idata 8000 .rdata 8000 .reloc F000 .rsrc 36000 .text
As you can see, the utility program Spy++ uses the User32.Dll extensively.
A Dyanamic Link Library (DLL) contains compiled code that is maintained external to an executable program which might use it. DLLs by default are loaded into memory by Windows when a program referrring to a DLL function calls the DLL function. If Windows loads the DLL, then Windows will automatically unload when the last program using it is done or unloaded. Alternatively, you can gain control over the loading and unloading of DLLs by using the API functions LoadLibrary and FreeLibrary.
16-bit Windows DLL development required the developer to write a LibMain and a WEP (Windows Exit Procedure) for the library to work correctly. There is only a single function used for 32-bit DLL development now and even this is not absolutely required.
The function is named DllMain and an empty shell version looks like the following:
BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved ) { switch (fdwReason) { case DLL_PROCESS_ATTACH: // Called when the Dll is mapped into the processes address space. break; case DLL_THREAD_ATTACH: // Called when the a thread is being created. break; case DLL_THREAD_DETACH: // Called when a thread is exiting. break; case DLL_PROCESS_DETACH: // Called when a process is being unmapped. break; }; }
hinstDll is the handle of the calling process, lpvReserved is reserved for later use and should be NULL, and fdwReason is one of the four reasons in the switch statement: DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH, DLL_THREAD_ATTACH, and DLL_THREAD_DETACH. The same function is called to attach a DLL to a process (start using it) or detach it (stop using it).
NOTE |
The macro definition WINAPI is used in place of FAR PASCAL, which was used for 16-bit DLLs. The keyword FAR instructs the compiler to use a 32-bit address. All addresses are 32-bit in Windows 95 and Windows NT; therefore, the directive would now be redundant. The keyword PASCAL has to do with pushing the function arguments on the stack in-order as opposed to reverse-order. Reverse-order is the default for C and C++. Microsoft chose the PASCAL calling convention for Windows, perhaps because it may be more efficient or because it is the one Visual Basic uses. |
You may not even need to write this function. For many DLLs the default DllMain Microsoft Visual C++ will add for you will be sufficient. You will need to create a DllMain if your DLL requires startup and cleanup code. Otherwise, writing a DLL is much like writing any other modules with functions and variables in them. In this section you'll use the default DllMain included by the compiler.
NOTE |
Many developers believe that DLLs are easier to build than applications because DLLs are usually a set of autonomous functions that any application can use. In many cases a DLL is often thought of as an application requiring individual testing prior to releasing it. This kind of thoroughness can and should be applied to all subprograms regardless of whether they will ultimately be used in a package like a DLL or part of a much larger program. |
Importing and Exporting Functions Your DLLs may need to both import and export functions. You will import those functions and variables from other DLLs which help your DLL function and will export those functions and variables that comprise the functionality of your DLL.
Microsoft has extended the syntax for exporting and importing functions and variables in DLLs. To designate a symbol as an exportable symbol, use the following syntax:
_ _declspec(dllexport) data_type int var_name; // for variables
or
_ _declspec(ddlexport) return_type func_name( [argument_list ] ); // for functions
Importing functions is almost identical: simply replace the keyword tokens, _ _declspec(dllexport) with __declspec(dllimport). Using an actual function and variable to demonstrate the syntax this time:
_ _declspec(dllimport) int referenceCount; _ _declspec(dllimport) void DiskFree( lpStr Drivepath );
TIP |
Two (2) underscores precede the keyword _ _declspec. |
By convention, Microsoft uses a header file and a preprocessor macro to make the inclusion of DLL declarations much simpler. The technique simply requires that you make a preprocessor token using a unique token-the header file name works easily, and requires very little in the way of memorization-and define a macro which will (when the preprocessor runs) replace the token with the correct import or export statement. Thus, assuming a header file named DISKFREE.H, the preprocessor macro in the header file would be:
// DISKFREE.H - Contains a simpler function for returning the amount of free disk ;space. // Copyright (c) 1996. All Rights Reserved. // By Paul Kimmel. Okemos, MI USA #ifndef _ _DISKFREE_H #define _ _DISKFREE_H #ifndef _ _DISKFREE_ _ #define DISKFREELIB _ _declspec(dllimport) #else #define DISKFREELIB _ _declspec(dllexport) #endif // Use the macro to control an import or export declaration. DISKFREELIB unsigned long DiskFree( unsigned int drive ); // (e.g. o = A:, 1 = ;B:, 2 = C: #endif
Simply by including the header file you can specify and define the token __DISKFREE__, which you only need to do in the actual DLL code, or if not, you can let the preprocessor do all of the work for you.
Creating the DiskFree DLL The DiskFree utility was designed to demonstrate creating a DLL (and it also provides a simple way to determine the amount of free disk space for any given drive). The underlying functionality is the GetDiskFreeSpace function found in Kernel32.Dll. Listings 32.4 and 32.5 contain the complete source code listing for the dynamic link library, DiskFree.Dll.
Listing 32.4 (DiskFree.h) Contains the Declarations and Reusable Import and Export Macros
// DiskFree.H - Contains a simpler DiskFree function, demonstrating 32-bit DLL ;techniques // Copyright (c) 1996. All Rights Reserved. // By Paul Kimmel. Okemos, MI USA // EDITOR: You might want to comment here a reference to the preprocessor ;directives that you discuss above. BOB #ifndef _ _DISKFREE_H #define _ _DISKFREE_H #ifndef _ _DISKFREE_ _ #define _ _DISKFREELIB_ _ _ _declspec(dllimport) #else #define _ _DISKFREELIB_ _ _ _declspec(dllexport) #endif // Returns the amount of free space on drive number (e.g. 0 = A:, 1= B:, 2 = c:) _ _DISKFREELIB_ _ unsigned long WINAPI DiskFree( unsigned int drive ); #endif
Listing 32.5 (DiskFree.Cpp) Contains the Functionality. Winbase.H Causes the GetDiskFreeSpace kernel32 Function to be Imported
// DiskFree.Cpp - Contains the DLL code for diskfree // Copyright (c) 1996. All Rights Reserved. // by Paul Kimmel. Okemos, MI USA /* Intentionally compiled with the default libraries and * configuration. It will be much smaller if you remove * unnecessary libraries and turn off debugging code. */ #include <afx.h> #include <winbase.h> // Contains the kernel32 GetDiskFreeSpace declaration. #define _ _DISKFREE_ _ // Define the token before the inclusion of the ;library #include "diskfree.h" // Returns the amount of free space on drive number (e.g. 0 = A:, 1= B:, 2 = c:) _ _DISKFREELIB_ _ unsigned long WINAPI DiskFree( unsigned int drive ) { unsigned long bytesPerSector, sectorsPerCluster, freeClusters, totalClusters; char DrivePath[4] = { char( drive + 65 ), ':', '\\', '\0' }; if( GetDiskFreeSpace( DrivePath, §orsPerCluster, &bytesPerSector, &freeClusters, &totalClusters )) { return sectorsPerCluster * bytesPerSector * freeClusters; } else { return 0; } }
In the next section we'll take a look at using 32-bit DLLs in general, including how Windows finds DLLs on your system.
The most common use of a DLL is to provide extended, reusable functionality and let Windows implicitly load the DLL. Topics that will not be discussed in this book, that you might want to explore for yourself, include:
In this section you are going to use a default compile of DiskFree, using an implicit DllMain (the compiler added one), and an implicit loading of the DLL, allowing Windows to manage loading and unloading the library.
NOTE |
A good example of a case in which you might want to explicitly load and unload a DLL is multilingual versions of resource DLLs where the DLL is loaded after the user indicates a language of choice. |
If, in preparation to use a DLL, you load the library with the Windows LoadLibrary function, then you may certainly specify the path to the DLL. However, many DLLs are loaded implicitly and their loading and unloading are managed by Windows. Libraries loaded in this fashion are searched for like executables: first the directory of the application loading the DLL is searched, followed by the current directory, the Windows\System directory, the Windows directory, and finally, each directory specified in the PATH.
It is a common practice to place a DLL in the Windows or Windows\System directories once the application is shipped, but in the meantime you may use the development directory of the executable for temporary storage. One thing to safeguard against is that you do not end up with multiple versions of the DLL in each of the Windows, Windows\System, or project directories.
Using a DLL Implicitly loading and using a DLL is about as simple as using any other function. This is especially true if you created the header file as described in the above section. When you compile your DLL, Microsoft Visual C++ creates a coincidental .LIB file. (So, DISKFREE.DLL has a coincidental DISKFREE.LIB created by the compiler.) The library (.LIB) file is used to resolve the load address of the DLL and specify the full pathname of the dynamic link library, and the header file provides the declaration.
All you have to do is include the header in the file using the DLL functionality and add the .LIB name to the Build, Settings Project Settings dialog, on the Link tab (see fig. 32.3), in the Object/library modules edit field.
Listing 32.6 demonstrates a Console application that shows the simplest use of the DiskFree DLL.
Listing 32.6 (TestDiskFree.Cpp) A Console Application Which Tests the DiskFree.Dll
// TestDiskFree.Cpp - Tests the simplified kernel32 DiskFree, based on GetDiskFreeSpace, function. // Copyright (c) 1996. All Rights Reserved. // by Paul Kimmel. Okemos, MI USA #include <afx.h> #include <iostream.h> #include "diskfree.h" #define CodeTrace(arg) \ cout << #arg << endl;\ arg int main() { CodeTrace( cout << DiskFree( 2 ) << endl ); return 0; }
Follow these steps to produce the Console application in Listing 32.6:
A Console application is the simplest way to demonstrate using a DLL because you do not have to surround the program with a lot of MFC code. Once you have tested the DLL, using it in any program requires the same number of steps. The biggest change will be the code that actually uses this housekeeping function.
As discussed in Chapter 5 "Messages and Commands," messages are the heat of Windows. Everything that happens in a Windows application happens because a message showed up to make it happen. When you move your mouse and click a button, a huge number of messages are generated, including WM_MOUSEMOVE for each movement of the mouse, WM_LBUTTONDOWN when the button goes down, WM_LBUTTONCLICK when the button is released, and higher level, more abstract messages like the WM_COMMAND message with the button's resource ID as one of its parameters. You can ignore the lower-level messages if you wish and many programmers do.
What you may not know is that you can generate messages too. There are two functions that generate messages: CWnd::SendMessage() and CWnd::PostMessage(). Each of these gets a message to an object that inherits from CWnd. An object that wants to send a message to a window using one of these functions must have a pointer to the window, and the window must be prepared to catch the message. A very common approach to this situation is to have a member variable in the sending object that stores a pointer to the window that will receive the message and another that stores the message to be sent:
CWnd* m_messagewindow; UINT m_message;
Messages are represented by unsigned integers. They appear to have names only because names like WM_MOUSEMOVE are connected to integers with #define statements.
The sending class has a member function to set these member variables, typically very short:
void Sender::SetReceiveTarget(CWnd *window, UINT message) { m_messagewindow = window; m_message = message; }
When the sending class needs to get a message to the window, it calls SendMessage:
m_messagewindow->SendMessage(m_message, wparam, lparam);
Or PostMessage:
m_messagewindow->PostMessage(m_message, wparam, lparam);
The difference between sending and posting a message is that SendMessage() does not return until the message has been handled by the window that received it, but PostMessage() just adds the message to the message queue and returns right away. If, for example, you build an object, pass that object's address as the lparam, and then delete the object, you should choose SendMessage(), since you can't delete the object until you are sure that the message-handling code has finished with it. If you are not passing pointers, you can probably use PostMessage() and move on as soon as the message has been added to the queue.
The meaning of the wparam and lparam values depends on the message you are sending. If it is a defined system message like WM_MOUSEMOVE, you can read the online documentation to learn what the parameters are. If, as is more likely, you are sending a message that you have invented, the meaning of the parameters is entirely up to you. You are the one who is inventing this message, and writing the code to handle it when it arrives at the other window.
To invent a message, add a defining statement to the header file of the class that will catch it:
#define WM_HELLO WM_USER + 300
WM_USER is an unsigned integer that marks the start of the range of message numbers available for user defined messages. In this release of MFC, its value is 0x4000, though you should not depend on that. User-defined messages have message numbers between WM_USER and 0x7FFF.
Then add a line to the message map, in both the header and source files, outside the ClassWizard comments. The source file message map might look like this:
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd) //{{AFX_MSG_MAP(CMainFrame) // NOTE - the ClassWizard will add and remove mapping macros ; here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP ON_MESSAGE(WM_HELLO, OnHello) END_MESSAGE_MAP()
The entry added outside the //AFX_MSG_MAP comments catches the WM_HELLO message and arranges for the OnHello() function to be called. The header file message map might look like this:
// Generated message map functions protected: //{{AFX_MSG(CMainFrame) afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // NOTE - the ClassWizard will add and remove member functions ; here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG afx_msg LRESULT OnHello(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP()
Then you add an implementation of OnHello() to the source file to complete the process.
International boundaries are shrinking at incredible rates. As access to wider serial communications widens and the preponderance of discrete resalable components continues, more and more demands for pieces built by vendors worldwide will grow. Even in-house software development will less-frequently be able to ignore international markets. This means your applications should be able to communicate with users in languages other than English and in character sets other than the typical Western character set.
Microcomputers were created in the United States which explains why we have 8-bit character-based operating systems. There are only 26 letters in our alphabet and ten digits, which leaves plenty of room (about 220 characters worth) for punctuation and other miscellaneous characters. But countries like Japan and China require a character set in the thousands.
Unicode is one way to tackle the character set problem. The Unicode standard was developed and is supported by a consortium of some of the biggest players in the international computing markets; among these are Adobe, Aldus, Apple, Borland, Digital, IBM, Lotus, Microsoft, Novell, and Xerox.
Unicode uses two bytes for each character, whereas ASCII uses only one. One byte, 8 bits, can represent 2 8 or 256 characters. Two bytes, 16 bits, can represent 65,536 characters. This is enough not just for one language, but for all the character sets in general use. For example, the Japanese character set, one of the largest, needs about 5,000 characters. Most require far less. The Unicode specification sets aside different ranges for different character sets and can cover almost every language on Earth in one universal code-a Unicode.
MFC has full Unicode support, with Unicode versions of almost every function. For example, consider the function CWnd::SetWindowText(). It takes a string and sets the title of the window, or the caption of a button, to that string. What kind of string it takes depends on whether you have Unicode support turned on in your application. In reality, there are two different functions to set the window text on, a Unicode version and a non-Unicode version, and in WINUSER.H, the block of code shown in Listing 32.7 changes the function name that you call to SetWindowTextA if you are not using Unicode, or SetWindowTextW if you are.
Listing 32.7 Microsoft's WINUSER.H Implementing Unicode Support
WINUSERAPI BOOL WINAPI SetWindowTextA(HWND hWnd, LPCSTR lpString); WINUSERAPI BOOL WINAPI SetWindowTextW(HWND hWnd, LPCWSTR lpString); #ifdef UNICODE #define SetWindowText SetWindowTextW #else #define SetWindowText SetWindowTextA #endif // !UNICODE
The difference between these two functions is the type of the second parameter: LPCSTR for the A version and LPCWSTR for the W version-W stands for Wide.
If you are using Unicode, whenever you pass a literal string (like "Hello") to a function, wrap it in the _T macro, like this:
pWnd->SetWindowText(_T("Hello"));
To turn on Unicode support, use the Default Project Configuration box on the Project toolbar. You can choose Unicode Debug or Unicode Release in addition to the familiar Debug and Release versions.
If you can deal with the annoyance of wrapping all text strings in _T macros, choose this option and just like that, your application is Unicode-aware. When you prepare your Greek or Japanese version of the application, life will be much simpler.
NOTE |
Windows 95 was built on old Windows, so it was not built using Unicode. This means that if you use Unicode in your Windows 95 programs, you are going to suffer performance penalties because the Windows 95 kernel will have to convert Unicode strings back to ordinary strings. Windows NT was designed at Microsoft from scratch, so it is completely compatible with Unicode. If you are developing for several platforms with C++ and intend using Unicode, your Win95 version may seem sluggish in comparison to the Windows NT version. |
This chapter demonstrated the way to build a console application and a major use for console applications called scaffolding. Because scaffolding allows you to test small pieces of your application at a time, it is a way to speed your software development and reduce the number of bugs in your code. You also learned how to use a dynamic-link library (DLL), how to create your own DLL, and how to send custom messages to another part of your application. Finally you learned how to ready your applications for the international market with Unicode.
To learn about related topics see: