大家可要注意了，这是俺从作者Jeffrey Richter的主页上弄下来的勘误表 ，呵呵，错误似乎还不少呀：）

Chapter 1, Page 12 (Error Handling | The ErrorShow Sample Application).
The second call to FormatMessage should include the FORMAT_MESSAGE_ALLOCATE_BUFFER flag
Chapter 3, Page 59 (Kernel Objects | Named Objects).
The following line should appear immediately before the first return statement:
CloseHandle(h);
Chapter 4, Page 74 (Processes | Writing Your First Windows Application).
Third line from the bottom of the page, a left parenthesis is missing between WinMain and GetModuleHandle. The line should read as follows:
int nMainRetVal = WinMain(GetModuleHandle(NULL), NULL, pszCommandLineAnsi,
Chapter 4, Page 82 (Processes | A Process's Environment Variables).
The prototype for ExpandEnvironmentStrings (at the bottom of the page) should read as follows:
DWORD ExpandEnvironmentStrings(PCTSTR pszSrc, PTSTR pszDst, DWORD nSize);
Chapter 8, Page 271 (Thread Synchronization in User Mode | Critical Sections).
In the code, the while statement's conditional test of g_nIndex is not thread safe since the test is outside the critical section. The functions should be written as follows:
DWORD WINAPI FirstThread(PVOID pvParam) {
for (BOOL fContinue = TRUE; fContinue; ) {
EnterCriticalSection(&g_cs);
if (g_nIndex < MAX_TIMES) {
g_dwTimes[g_nIndex] = GetTickCount();
g_nIndex++;
} else fContinue = FALSE;
LeaveCriticalSection(&g_cs);
}
return(0);
}
DWORD WINAPI SecondThread(PVOID pvParam) {
for (BOOL fContinue = TRUE; fContinue; ) {
EnterCriticalSection(&g_cs);
if (g_nIndex < MAX_TIMES) {
g_nIndex++;
g_dwTimes[g_nIndex - 1] = GetTickCount();
} else fContinue = FALSE;
LeaveCriticalSection(&g_cs);
}
return(0);
}
Chapter 9, Page 294 (Thread Synchronization with Kernel Objects | Event Kernel Objects).
The line that reads:
g_hEvent = CreateEve\nt(NULL, TRUE, FALSE, NULL);
Should read as follows:
g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
Chapter 15, Page 541 (Using Virtual Memory in Your Own Applications | Address Windowing Extensions).
The line that reads:
ULONG_PTR ulRAMPages = ulRAMBytes / sinf.dwPageSize
Should read as follows:
ULONG_PTR ulRAMPages = (ulRAMBytes + sinf.dwPageSize - 1) / sinf.dwPageSize;
Chapter 17, Page 601 (Memory-Mapped Files | Step 2: Creating a File-Mapping Kernel Object).
The last sentence of the 2nd full paragraph should say "For files that are less than 4GB, dwMaximumSiseHigh will always be 0."
Chapter 17, Page 621 (Memory-Mapped Files | Processing a Big File Using Memory-Mapped Files).
The comment "Count the number of Js in this block" should read "Count the number of 0s in this block".
Chapter 19, Page 680 (DLL Basics | The Overall Picture).
Figure 19-1, right side should have 3 ovals around the word "Compiler" and another oval around the word "Linker"
Chapter 22, Page 756 (DLL Injection and API Hooking | Injecting a DLL Using the Registry).
In Windows 2000, User32.dll re-reads the AppInit_DLLs registry value each time it loads into a process so rebooting the machine is not required like it was in Windows NT 4.0 and earlier.
Chapter 22, Page 801 (DLL Injection and API Hooking | The LastMsgBoxInfo Sample Application).
The sample fails to hooks some applications. To fix this, you must modify the CAPIHook::ReplaceIATEntryInOneMod method so that it calls VirtualProtect just before calling WriteProcessMemory. The call to VirtualProtect should look like this:
DWORD dwDummy;
VirtualProtect(ppfn, sizeof(ppfn), PAGE_EXECUTE_READWRITE, &dwDummy);