Programming Windows, Fifth Edition 

( 8 )


“Look it up in Petzold” remains the decisive last word in answering questions about Windows development. And in PROGRAMMING WINDOWS, FIFTH EDITION, the esteemed Windows Pioneer Award winner revises his classic text with authoritative coverage of the latest versions of the Windows operating system—once again drilling down to the essential API heart of Win32 programming. Topics...

See more details below
Available through our Marketplace sellers.
Other sellers (Paperback)
  • All (22) from $25.59   
  • New (9) from $82.25   
  • Used (13) from $25.59   
Sort by
Page 1 of 1
Showing All
Note: Marketplace items are not eligible for any coupons and promotions
Seller since 2009

Feedback rating:



New — never opened or used in original packaging.

Like New — packaging may have been opened. A "Like New" item is suitable to give as a gift.

Very Good — may have minor signs of wear on packaging but item works perfectly and has no damage.

Good — item is in good condition but packaging may have signs of shelf wear/aging or torn packaging. All specific defects should be noted in the Comments section associated with each item.

Acceptable — item is in working order but may show signs of wear such as scratches or torn packaging. All specific defects should be noted in the Comments section associated with each item.

Used — An item that has been opened and may show signs of wear. All specific defects should be noted in the Comments section associated with each item.

Refurbished — A used item that has been renewed or updated and verified to be in proper working condition. Not necessarily completed by the original manufacturer.

1998-12-02 Hardcover 5th New 157231995X Ships Within 24 Hours. Tracking Number available for all USA orders. Excellent Customer Service. Upto 15 Days 100% Money Back Gurantee. ... Try Our Fast! ! ! ! Shipping With Tracking Number. Read more Show Less

Ships from: Bensalem, PA

Usually ships in 1-2 business days

  • Canadian
  • International
  • Standard, 48 States
  • Standard (AK, HI)
  • Express, 48 States
  • Express (AK, HI)
Seller since 2007

Feedback rating:


Condition: New
Hardcover New 157231995X Brand new book. STUDENT US EDITION. SEALED CD INCLUDED. Never used. Nice gift. Best buy. Shipped promptly and packaged carefully.

Ships from: Woodinville, WA

Usually ships in 1-2 business days

  • Canadian
  • International
  • Standard, 48 States
  • Standard (AK, HI)
  • Express, 48 States
  • Express (AK, HI)
Seller since 2010

Feedback rating:


Condition: New
Hardcover New 157231995X Friendly Return Policy. A+++ Customer Service!

Ships from: Philadelphia, PA

Usually ships in 1-2 business days

  • Canadian
  • International
  • Standard, 48 States
  • Standard (AK, HI)
  • Express, 48 States
  • Express (AK, HI)
Seller since 2013

Feedback rating:


Condition: New
Hardcover New 157231995X! ! KNOWLEDGE IS POWER! ! ENJOY OUR BEST PRICES! ! ! Ships Fast. All standard orders delivered within 5 to 12 business days.

Ships from: Southampton, PA

Usually ships in 1-2 business days

  • Canadian
  • International
  • Standard, 48 States
  • Standard (AK, HI)
  • Express, 48 States
  • Express (AK, HI)
Seller since 2010

Feedback rating:


Condition: New
Hardcover New 157231995X! ! ! ! BEST PRICES WITH A SERVICE YOU CAN RELY! ! !

Ships from: Philadelphia, PA

Usually ships in 1-2 business days

  • Canadian
  • International
  • Standard, 48 States
  • Standard (AK, HI)
  • Express, 48 States
  • Express (AK, HI)
Seller since 2014

Feedback rating:


Condition: New
Hardcover New 157231995X XCITING PRICES JUST FOR YOU. Ships within 24 hours. Best customer service. 100% money back return policy.

Ships from: Bensalem, PA

Usually ships in 1-2 business days

  • Canadian
  • International
  • Standard, 48 States
  • Standard (AK, HI)
  • Express, 48 States
  • Express (AK, HI)
Seller since 2011

Feedback rating:


Condition: New

Ships from: Philadelphia, PA

Usually ships in 1-2 business days

  • Canadian
  • International
  • Standard, 48 States
  • Standard (AK, HI)
  • Express, 48 States
  • Express (AK, HI)
Seller since 2014

Feedback rating:


Condition: New
Brand new.

Ships from: acton, MA

Usually ships in 1-2 business days

  • Standard, 48 States
  • Standard (AK, HI)
Seller since 2014

Feedback rating:


Condition: New
Brand new.

Ships from: acton, MA

Usually ships in 1-2 business days

  • Standard, 48 States
  • Standard (AK, HI)
Page 1 of 1
Showing All
Sort by
Programming Windows

Available on NOOK devices and apps  
  • NOOK Devices
  • NOOK HD/HD+ Tablet
  • NOOK
  • NOOK Color
  • NOOK Tablet
  • Tablet/Phone
  • NOOK for Windows 8 Tablet
  • NOOK for iOS
  • NOOK for Android
  • NOOK Kids for iPad
  • PC/Mac
  • NOOK for Windows 8
  • NOOK for PC
  • NOOK for Mac
  • NOOK Study
  • NOOK for Web

Want a NOOK? Explore Now

NOOK Book (eBook)
$27.49 price
(Save 42%)$47.99 List Price


“Look it up in Petzold” remains the decisive last word in answering questions about Windows development. And in PROGRAMMING WINDOWS, FIFTH EDITION, the esteemed Windows Pioneer Award winner revises his classic text with authoritative coverage of the latest versions of the Windows operating system—once again drilling down to the essential API heart of Win32 programming. Topics include:

  • The basics—input, output, dialog boxes
  • An introduction to Unicode
  • Graphics—drawing, text and fonts, bitmaps and metafiles
  • The kernel and the printer
  • Sound and music
  • Dynamic-link libraries
  • Multitasking and multithreading
  • The Multiple-Document Interface
  • Programming for the Internet and intranets

Packed as always with definitive examples, this newest Petzold delivers the ultimate sourcebook and tutorial for Windows programmers at all levels working with Microsoft Windows 95, Windows 98, or Microsoft Windows NT. No aspiring or experienced developer can afford to be without it.

An electronic version of this book is available on the companion CD.

For customers who purchase an ebook version of this title, instructions for downloading the CD files can be found in the ebook.

The classic and definitive Windows programming reference has been revised, updated and re-engineered as a massive 1470-page programmer's guide and reference. The fifth edition shows how to program under Win98, NT 4.0 and 5.0. You should know and understand C code before you dive into this pool of programming lore, (you won't get far into the innards of APIs without it).

Read More Show Less

Editorial Reviews

From Barnes & Noble

Fatbrain Review

The classic and definitive Windows programming reference has been revised, updated and re-engineered as a massive 1470-page programmer's guide and reference. The fifth edition shows how to program under Win98, NT 4.0 and 5.0. You should know and understand C code before you dive into this pool of programming lore, (you won't get far into the innards of APIs without it).


  • As with his prior editions, author Charles Petzold wastes no time diving into the fundamentals of Windows API programming with explanations of message functions, Unicode support, standard I/O basics, timers, child window controls, menus and other resources.
  • Thoroughly details graphics techniques, bitmaps and bit-block transfers, palette management, text, fonts and metafile (vector programming) concepts.
  • By this time, you are ready to follow Petzold's discussions of advanced topics. He calmly and dispassionately dissects the Multiple-Document Interface (MDI), walks you through sound and music interfaces and then guides you into multitasking, multithreading, DLLs and the Internet.


  • It is rare to have such a technically accurate programming guide, rarer still to include clear, lucid and logical explanations of how and why APIs work. This is literate programming and good non-trivial code.
  • Source code and EXE files are on the companion CD-ROM, along with a fully searchable electronic version of this publication.

Related Titles:

If you are programming for Win95, then we recommend Programming Windows 95(Petzold of course) and the Microsoft Windows 95 Resource Kit as valuable adjuncts and references. The following volumes contain a lot of supplementary information for programmers and are worth your consideration. Advanced Windows, Third Edition (for Win95 and NT 4.0 only) and Microsoft Windows 98 Resource Kit.

Read More Show Less

Product Details

  • ISBN-13: 9781572319950
  • Publisher: Microsoft Press
  • Publication date: 12/28/1998
  • Series: Microsoft Programming Series
  • Edition description: 5th Edition
  • Edition number: 5
  • Pages: 1520
  • Product dimensions: 7.66 (w) x 9.54 (h) x 2.78 (d)

Meet the Author

Charles Petzold has been writing about programming for Windows-based operating systems for 24 years. A Microsoft MVP for Client Application Development and a Windows Pioneer Award winner, Petzold is author of the classic Programming Windows, currently in its fifth edition and one of the best-known programming books of all time; the widely acclaimed Code: The Hidden Language of Computer Hardware and Software; and more than a dozen other books.
Read More Show Less

Read an Excerpt

Chapter 14: Bitmaps and Bitblts

A bitmap is a two-dimensional rectangular array of bits that correspond to the pixels of an image. When real-world images are captured in bitmaps, the image is divided into a grid and the pixel is the sampling unit. The value of each pixel in the bitmap indicates the average color of the image within a unit of this grid. Monochrome bitmaps require only one bit per pixel; gray shade or color bitmaps require multiple bits per pixel.

Bitmaps represent one of two methods for storing pictorial information in a Windows program. The other form of stored pictorial information is the metafile, which I'll cover in Chapter 18. A metafile is a description of a picture rather than a digital representation of it.

As I'll discuss in more detail shortly, Microsoft Windows 3.0 introduced something called the device-independent bitmap (DIB). I'll discuss DIBs in the next chapter. This chapter covers the GDI bitmap object, which is the bitmap support implemented in Windows prior to the DIB. As the various sample programs in this chapter demonstrate, the pre-DIB bitmap support of Windows is still quite useful and valuable.


Both bitmaps and metafiles have their place in computer graphics. Bitmaps are often used for very complex images originating in the real world, such as digitized photographs or video captures. Metafiles are more suitable for human-generated or machine-generated images, such as architectural drawings. Both bitmaps and metafiles can exist in memory or be stored on a disk as files, and both can be transferred among Windows applications through the clipboard.

The difference between bitmaps and metafiles is the difference between raster graphics and vector graphics. Raster graphics treats output devices in terms of discrete pixels; vector graphics treats the output device as a Cartesian coordinate system upon which lines and filled objects can be drawn. Most graphics output devices these days are raster devices. These include video displays, dot-matrix printers, laser printers, and ink-jet printers. A pen plotter, however, is a vector output device.

Bitmaps have two major drawbacks. First, they are susceptible to problems involving device dependence. The most obvious device dependency is color. Displaying a color bitmap on a monochrome device is often unsatisfactory. Another problem is that a bitmap often implies a particular resolution and aspect ratio of an image. Although bitmaps can be stretched or compressed, this process generally involves duplicating or dropping rows or columns of pixels, and this can lead to distortion in the scaled image. A metafile can be scaled to almost any size without distortion.

The second major drawback of bitmaps is that they require a large amount of storage space. For instance, a bitmap representation of an entire 640-by-480-pixel, 16-color Video Graphics Array (VGA) screen requires more than 150 KB; a 1024-by-768 image with 24 bits per pixel requires more than 2 MB. Metafiles usually require much less storage space than bitmaps. The storage space for a bitmap is governed by the size of the image and number of colors it contains, whereas the storage space for a metafile is governed by the complexity of the image and the number of individual GDI instructions it contains.

One advantage of bitmaps over metafiles, however, is speed. Copying a bitmap to a video display is usually much faster than rendering a metafile. in recent years, compression techniques have allowed the shrinking of bitmaps to a size where they can be effectively transmitted over telephone lines and used extensively in World Wide Web pages on the Internet.

Where Do Bitmaps Come From?

Bitmap images can be created "manually," for example, by using the Paint program included with Windows 98. Someone using a raster "paint" program rather than a vector "draw" program is working under the assumption that the eventual image will be too complex to be rendered with lines and filled areas.

Bitmap images can also be created algorithmically by computer code. Although most algorithmically generated images can be stored in a vector graphics metafile, highly detailed surfaces or fractals usually require bitmaps.

These days, bitmaps are often used for images from the real world, and various hardware devices let you move images from the real world into the computer. This hardware generally uses something called a cbarge-coupled device (CCD), which releases an electrical charge when exposed to light. Sometimes these CCD cells are arranged in an array, one CCD per pixel; to keep costs down, a single row of CCDs can be used to scan an image.

The scanner is the oldest of these computer-based CCD devices. It uses a row of CCDs that sweep along the surface of a printed image, such as a photograph. The CCDs generate electrical charges based on the intensity of light. Analog-to-digital converters (ADCs) convert the charges into numbers, which then can be arranged in a bitmap.

Video camcorders also use arrays of CCD cells to capture images. Generally these images are recorded on videotape. However, the video output can be fed directly into a video frame grabber, which converts an analog video signal into an array of pixel values. These frame grabbers can be used with any compatible video source, such as that from a VCR or a laserdisc or DVD player, or even directly from a cable decoding box.

Most recently, digital cameras have become financially viable for the home user. These often look very much like normal cameras. But instead of film, an array of CCDs is used to capture an image, and an internal ADC allows the digital image to be stored directly in memory within the camera. Generally, the camera interfaces to the computer through the serial port.


A bitmap is rectangular and has a spatial dimension, which is the width and height of the image in pixels. For example, this grid could represent a very small bitmap that is 9 pixels wide and 6 pixels high or, more concisely, 9 by 6:

By convention, the shorthand dimension of a bitmap is given with the width first. This bitmap has a total of 9 x 6, or 54, pixels. I'll often use the symbols cx and cy to refer to the width and height of a bitmap. The 'c' stands for count, so cx and cy are the number of pixels along the x (horizontal) and y (vertical) axes.

We can indicate a particular pixel of the bitmap in terms of x and y coordinates. Generally (but not always, as we'll see), the origin of a bitmap is considered to be the upper left corner of the image, as I've numbered the pixels in the grid. Thus, the pixel at the bottom right corner of this bitmap is at the coordinate (8, 5). This is one less than the width and height of the bitmap because the numbering starts with zero.

The spatial dimensions of a bitmap are often referred to as its resolution, but this is a problematic word. We say that our video displays have a resolution of 640 by 480 but that our laser printers have a resolution of 300 dots per inch. I prefer the latter use of the word-resolution as the number of pixels per metrical unit. Bitmaps could have a resolution in this sense, meaning that some number of pixels in the bitmap correspond to a particular unit of measurement. Regardless, when I use the word resolution, it should be obvious from the context which definition I'm using.

Bitmaps are rectangular but computer memory is linear. Bitmaps are generally (but not always) stored in memory by rows beginning with the top row of pixels and ending with the bottom row. (The DIB is one major exception to this rule.) Within each row, pixels are stored beginning with the leftmost pixel and continuing to the right. It's just like storing the individual characters of several lines of text.

Color and Bitmaps

Besides having a spatial dimension, bitmaps also have a color dimension. This is the number of bits required for each pixel and is sometimes called the color depth of the bitmap or the bit-count or the number of bits per pixel (bpp). Each pixel in a bitmap has the same number of color bits.

A bitmap with 1 bit per pixel is called a bilevel or bicolor or monochrome bitmap. Each pixel is either a 0 or a 1. A value of 0 could mean black, and a 1 could mean white, but that's not necessarily always the case. Additional colors require more bits per pixel. The number of possible colors is equal to 2 bit-count. With 2 bits, you get 4 colors, with 4 bits you get 16 colors, with 8 bits you get 256 colors, with 16 bits you get 65,536 colors, and with 24 bits you get 16,777,216 colors.

How exactly certain combinations of color bits correspond to real and familiar colors is a question that persistently occupies (and often plagues) the mind of anyone who works with bitmaps.

Real-World Devices

Bitmaps can be categorized based on the number of color bits they have; the various bitmap color formats are based on the color capabilities of common video display adapters available throughout the history of Windows. Indeed, one can think of video display memory as comprising a large bitmap-one that we see when we look at our monitors.

The most common video adapters used for Windows 1.0 were the IBM Color Graphics Adapter (CGA) and the Hercules Graphics Card (HGC). The HGC was a monochrome device, and the CGA had to be used in a monochrome graphics mode under Windows. The monochrome bitmap is still quite common (for example, mouse cursors are often monochrome), and monochrome bitmaps have other uses beyond the display of images. . . .

Read More Show Less

Table of Contents

  • Author's Note
  • Part I: The Basics
    • Chapter 1: Getting Started
    • Chapter 2: An Introduction to Unicode
    • Chapter 3: Windows and Messages
    • Chapter 4: An Exercise in Text Output
    • Chapter 5: Basic Drawing
    • Chapter 6: The Keyboard
    • Chapter 7: The Mouse
    • Chapter 8: The Timer
    • Chapter 9: Child Window Controls
    • Chapter 10: Menus and Other Resources
    • Chapter 11: Dialog Boxes
    • Chapter 12: The Clipboard
  • Part II: More Graphics
    • Chapter 13: Using the Printer
    • Chapter 14: Bitmaps and Bitblts
    • Chapter 15: The Device-Independent Bitmap
    • Chapter 16: The Palette Manager
    • Chapter 17: Text and Fonts
    • Chapter 18: Metafiles
  • Part III: Advanced Topics
    • Chapter 19: The Multiple-Document Interface
    • Chapter 20: Multitasking and Multithreading
    • Chapter 21: Dynamic-Link Libraries
    • Chapter 22: Sound and Music
    • Chapter 23: A Taste of the Internet
Read More Show Less

First Chapter

Chapter 20: Multitasking and Multithreading

Multitasking is the ability of an operating system to run multiple programs concurrently. Basically, the operating system uses a hardware clock to allocate "time slices" for each currently running process. If the time slices are small enough-and the machine is not overloaded with too many programs trying to do something-it appears to a user as if all the programs are running simultaneously.

a given. These mainframes often have hundreds of terminals attached to them, and each terminal user should get the impression that he or she has exclusive access to the whole machine. in addition, mainframe operating systems often allow users to "submit jobs to the background," where they are then carried out by the machine while the user can work on something else.

reality. But we now often seem to take PC multitasking for granted. As I'll discuss shortly, to some extent the earlier 16-bit versions of Microsoft Windows supported multitasking but in a somewhat limited capability. The 32-bit versions of Windows all support both true multitasking and as an extra bonus-multithreading.

program can split itself into separate "threads" of execution that also seem to run concurrently. This concept might at first seem barely useful, but it turns out that programs can use multithreading to perform lengthy jobs in the background without requiring the user to take an extended break away from their machines. Of course, sometimes this may not be desired: an excuse to take a journey to the watercooler or refrigerator is often welcome! But the user should always be able to do something on the machine, even when it's busy doing something else.


In the early days of the PC; some people advocated multitasking for the future, but man others scratched their heads in puzzlement: Of what use is multitasking on a single-user personal computer? Well, it turned out that multitasking was something users wanted without really knowing it.

Multitasking Under DOS?

The Intel 8088 microprocessor used in the original PC was not exactly meant for multitasking. Part of the problem was inadequate memory management; as multiple programs are started up and ended, a multitasking operating system is often called upon to move memory blocks around to consolidate free space. This was not possible on the 8088 in a manner transparent to applications.

way of applications, DOS supported very little beyond loading programs and providing them with access to the file system.

to overcome those obstacles, mostly with terminate-and-stay-resident (TSR) programs. Some TSRs, such as print spoolers, hooked into the hardware timer interrupt to perform true background processing. Others, like popup utilities such as SideKick, could perform a type of a task switching-suspending an application while the popup was running. DOS was also progressively enhanced to provide support for TSRs.

shells on" top of DOS (such as Quarterdeck's DesqView), but only one of these environments eventually achieved a large market penetration. That, of course, is Windows.

Nonpreemptive Multitasking

When Microsoft introduced Windows I O'in 1985, it was the most sophisticated solution yet devised to go beyond the limitations of DOS. Back then Windows ran in real mode, but even so, it was able to move memory blocks around in physical memory-a prerequisite for multitasking - in a way that was not quite transparent to applications but almost tolerable.

than it does in a command-line single-user operating system'. For example, in classical command-fine UNIX, it is possible to execute programs off the command line so that they run in the background. However, any display output from the program must be redirected to a file, or the output will get mixed up with whatever else the user is doing.

same screen. Switching back and forth becomes trivial, and it is also possible to quickly move data from one program to another; for example, to embed a picture created in a drawing program into a text file maintained by a word processing program. Data transfer has been supported in various ways under Windows, first with the clipboard, later through Dynamic Data Exchange (DDE), and now through Object Linking and Embedding (OLE).

the traditional preemptive time-slicing found in multiuser operating systems. Those operating systems use a system clock to periodically interrupt one task and restart another. The 16-bit versions of Windows supported something called "nonpreemptive multitasking." This type of multitasking is made possible because of the message-based architecture of Windows. In the general case, a Windows program sits dormant in memory until it receives a message. These messages are often the direct or indirect result of user input through the keyboard or mouse. After processing the message, the program returns control back to Windows.

Windows program to another based on a timer tick. Instead, any task switching took place when a program had finished processing a message and had returned control to Windows. This nonpreemptive multitasking is also called "cooperative multitasking" because it requires some cooperation on the part of applications. One Windows program could tie tip the whole system if it took a long time processing a message.

some forms of preemptive multitasking were also present. Windows used preemptive multitasking for running DOS programs and also allowed dynamic-link libraries to receive hardware timer interrupts for multimedia purposes.

at least cope with - the limitations of nonpreemptive multitasking. The most notorious is, of course, the hourglass mouse cursor. This is not a solution, of course, but just a way of letting the user know that a program is busy working on a lengthy job and the system will be otherwise unusable for a little awhile. Another partial solution is the Windows timer, which allows a program to receive a message and do some work at periodic intervals. The timer is often used for clock applications and animation.

PeekMessage function call, as we saw in Chapter 5 in the RANDRECT program. Normally a program uses the GetMessage call to retrieve the next message from its message queue. However, if there are no messages in the message queue, then GetMessage will not return until a message is present. PeekMessage, on the other hand, returns control to the program even if no messages are pending. Thus, a program can perform a long job and intermix PeekMessage calls in the code. The long job will continue running as long as there are no pending messages for the program or any other program.

PM and the Serialized Message Queue

The first attempt by Microsoft (in collaboration with IBM) to implement multitasking in a quasi- DOS/Windows environment was OS/2 and the Presentation Manager (PM). Although OS/2 certainly supported preemptive multitasking, it often didn't seem as if this preemptive was carried over into, the Presentation Manager. The problem is that PM serialized user input messages from the keyboard and mouse. What this means is that PM would not deliver a keyboard or mouse message to a program until the previous user input message had been fully processed.

a PM (or Windows) program can receive, most of the other messages are the result of a key- board or mouse event. For example, a menu command message is the result of the user making a menu selection using the keyboard or mouse. The keyboard or mouse message is not fully processed until the menu command message is processed.

"typeahead" and "mouse-ahead" actions by the user. it one of the keyboard or mouse messages caused a, shift in input focus from one window to another, subsequent keyboard messages should go to the window with the new input focus, So, the system doesn't know where to send a subsequent user input message until the previous ones have been processed.

application to be able to tie up the entire system; that requires a deserialized message queue, which is supported by the 32-bit versions of Windows. If one program is busy doing a lengthy job, you can switch the input focus to another program.

The Multithreading Solution

I've been discussing the. OS/2 Presentation Manager only because it was the first environment that provided some veteran Windows programmers (such as myself) with their first introduction to multithreading, Interestingly enough, the limitations of PM's implementation of multithreading provided programmers with essential clues to how multithreaded programs should be architected. Even though these limitations have now largely been lifted from the 32- bit versions of Windows, the lessons learned from more limited environments are still quite valid. So, let's proceed.

pieces, called "threads of execution," that run concurrently. The support of threads turned out to be the best solution to the problem of the serialized message queue in the Presentation Manager and continues to make a whole lot of sense under Windows.

also call other functions in the program. A program begins execution with its main (or primary) thread, which in a traditional C program is the function called main and which in Windows is WinMain. Once running, the program can create new threads of execution by making a system call (CreateThread) specifying the name of initial thread function. The operating system preemptively switches control among the threads in much the same way it switches control among processes.

queue or not. A PM thread must create a message queue if it wishes to create windows from that thread. Otherwise, a thread needn't create a message queue if it's just doing a lot of data crunching or graphics output. Because the non-message-queue threads do not process messages, they cannot hang the system. The only restriction is that a non-message-queue thread cannot send a message to a window in a message-queue thread or make any function call that causes a message to be sent. (They can, however, post messages to messagequeue threads.)

message-queue thread that created all the windows and processed messages to them, and one or more non-message-queue threads that performed lengthy background tasks. PM programmers also learned about the second rule." Basically, they were advised that a message-queue thread should spend no more than 1/10 of a second processing a message. Anything that takes longer should be done in a different thread. If all programmers followed this rule, no PM program could hang the system for more than 1/10 of a second.

Multithreaded Architecture

I said that the limitations of PM provided programmers with essential clues to understanding how to use multiple threads of execution in a program running under a graphical environment. So here's what I recommend for the architecture of your programs: Your primary thread creates all the windows that your program needs, includes all the window procedures for these windows, and processes all the messages for these windows. Any other threads are simply background crunchers. They do not interact with the user except through communication with the primary thread.

other messages), perhaps creating secondary threads in the process. These additional threads do the non-user-related tasks.

secondary threads are the governor's staff. The governor delegates all the big jobs to his or her staff while maintaining contact with the outside world. Because they are staff members, the secondary threads do not hold their own press conferences. They discreetly do their work, report back to the governor, and await their next assignment.

they share the process's resources, such as memory and open files. Because threads share the program's memory, they also share static variables. However, each thread has its own stack, so automatic variables are unique to each thread. Each thread also has its own processor state (and math coprocessor state) that is saved and restored during thread switches.

Thread Hassles

Properly designing, coding, and debugging a complex multithreaded application is conceivably one of the most difficult jobs a Windows programmer can encounter. Because a preemptive multitasking system can interrupt a thread at any point to switch control to another thread, any undesirable interaction between two threads might not be obvious and might show up only occasionafly, seemingly on a random basis.

This happens when a programmer assumes that one thread win finish doing something - for example, preparing some data - before another thread needs that data. To help coordinate thread activity, operating systems require various forms of synchronization. One is the semaphore, which allows the programmer to block the execution of a thread at a certain point in the code until another thread signals that it can resume. Similar to semaphores are "critical sections," which are sections of code that cannot be interrupted.

is called a "deadlock." This occurs when two threads have blocked each other's execution and they can only unblock that execution by proceeding.

threads than 16-bit programs. For example, suppose one thread executes the simple statement


where lCount is a long 32-bit global variable that is used by other threads. In a 16-bit program, that single statement in C is compiled to two machine code instructions, the first one incrementing the low 16 bits of the variable, and the second adding any carry. into the high 16 bits. Suppose the operating system interrupted the thread between those two machine code instructions. If lCount were 0x0000FFFF before the first machine. code instruction, then lCount would be, zero at the time the thread was interrupted, and that's the value another thread would see. Only when the thread resumed would lCount be incremented to its proper value of 0x00010000.

infrequently that it would never be detected. In a 16-bit program, the proper way to solve it would be to enclose the statement in a critical section, during which the thread cannot be interrupted. In a 32-bit program, however, the statement is fine because it is compiled to a single machine code instruction.

The Windows Advantage

The 32-bit versions of Windows (including Microsoft Windows NT and Windows 98) have a deserialized message queue. The implementation of this seems very good: If a program is taking a long time processing a message, the mouse cursor appears as an hourglass when the mouse is over that program's window but it changes to 4 normal arrow when posi- tioned over another program's window. A simple click can bring that other window to the foreground.

the big job because the big job is preventing the program from receiving other messages. This is undesirable. A program should be always open to messages, and that often requires tile use of secondary threads.

threads and non-message-queue threads. Each thread gets its own message queue when the thread is created. This reduces some of the awkward rules for threads in a PM prograin. (However, in most cases You'll want to process input through message procedures in one thread and pass off long jobs to other threads that do not maintain windows. This structure almost always makes the best sense, as we'll see.)

one thread to kill another thread in the same process. As you'll discover when you begin writing multithreaded code, this is sometimes convenient. The early versions of OS/2 did not include a "kill thread" function.

Windows 98 have implemented something called "thread local storage" (TLS). To understand recall that I mentioned earlier that static variables, both global and local to a function, are shared among threads because they sit in the process's data memory space. Automatic variables, which are always local to a function, are unique to each thread because they occupy space on the stack, and each thread has its own stack.

and for these threads to use static variables that are unique to the thread. That's thread local storage. There are a few Windows function calls involved, but Microsoft has also added ,in extension to the C compiler that makes the use of TLS more transparent to the programmer.

New! Improved! Now with Threads!

Now that I've made the case for threads, let's put the subject in proper perspective. Sometimes there's a tendency for programmers to use every feature that an operating system has to offer. But the worst case is when your boss comes to your desk and says, "I've heard that this new Whatsit thing is really hot. Let's incorporate some Whatsit in our program." And then you spend a week trying to figure out how (and if) Whatsit can possibly benefit the application.

application that doesn't need it. Some applications just can't benefit from multithreading. If Your program displays the hourglass cursor for an annoying period of time, or if it uses the PeekMessage call to avoid the hourglass cursor, then restructuring the program for multithreading is probably a good idea. Otherwise, you're just making things hard for yourself and possibly introducing new bugs into the code.

There are even some cases where the hourglass cursor might be entirely appropriate. I mentioned earlier the 1/10-second rule. Well, loading a large file into memory can take longer than 1/10 second. Does this mean that file-loading routines should be implemented in separate threads? Not necessarily. When a user commands; a program to open a file, he or she usually wants that operation to be carried out immediately, Putting the file-loading routines in a separate thread simply adds overhead. It's just not worth it, even if you want to boast to your friends that you write multithreaded programs!


The API function to create a new thread of execution is named Create Thread. The function has the following syntax:

 hThread  =      CreateThread (&security_attributes, dwStackSize, ThreadProc,

The first argument is a pointer to a structure of type SECURITY_ATTRIBUTES. This argument is ignored in Windows 98. It can also be set to. NULL in Windows NT. The second argument is an initial stack size for the new thread; this argument can be set to 0 for a default value. In any case, Windows dynamically lengthens the stack, if necessary.

This can have any name but must have the syntax

 DWORD WINAPI ThreadProc VOID pParam

The fourth argument to CreateThread becomes the parameter to ThreadProc. This is how a main thread and a secondary thread can share data.

CREATESUSPENDED if the thread is to be created but not immediately executed. The thread will remain suspended until ResumeThread is called. The sixth argument is a pointer to a vari able that will receive the value of the thread ID.

_beginthread that is declared in the PROCESS.H header file. This function has the following syntax:

thread function has the syntax

the RANDRECT program shown in Chapter 5. As you'll recall, RANDRECT used the PeekMessage loop to display a series of random rectangles.

something in the Project Settings dialog box. Select the C/C++ tab, and select Code Generation in the Category combo box. in the Use Run-Time Library combo box, you should see SingleThreaded for the Release configuration and Debug Single-Threaded for the Debug configuration. Change these to Multithreaded and Debug Multithreaded, respectively. This will change a compiler flag to /MT, which the compiler needs to compile a multithreaded application. In particular, the compiler inserts the LIBCMT.LIB filename in the OBJ file rather than LIBC.LIB. The linker uses this name to link with the run-time library functions.

library functions maintain static data. The strtok function, for example, is designed to be called more than once in succession and stores a pointer in static memory. In a multithreaded program, each thread must have its own static pointer in the strtok function. Thus, the multithreaded version of this function is a little different from the single-threaded strtok function.

This file declares the _beginthread function that starts up the new thread. The function is not declared unless an _MT identifier is defined, and that's another result of the /MT flag.

Create-Window is stored in a global variable. So also are the cxClient and cyClient values obtained from the WM_SIZE message in the window procedure.

address of the thread function (called Thread) as the first parameter and zeros for the other parameters. The thread function returns VOID and has a parameter that is a pointer to a VOID. The Thread function in RNDRCTMT does not use this parameter.

function, as well as any other function the thread function might call, runs concurrently with the rest of the code in the program. Two or more threads can use the same function in a process. In this case, the automatic local variables (stored on the stack) are unique to each thread; all static variables are common to all threads in the process. This is how the window procedure can set the global =Client and cyClient variables and the Thread function can use them.

thread. Normally, persistent data involves static variables but in Windows 98 you can use TLS, which I've touched on and which I'll discuss in greater detail later in this chapter.

The Programming Contest Problem

On October 3, 1986, Microsoft held a daylong press briefing for technical editors and writers of computer magazines to discuss their current array of language products, including their first interactive development environment, QuickBASIC 2.0. At that time, Windows 1.0 was less than a year old, and no one knew when we'd get something similar for that environ- ment. (it took quite a few years.) What made this event unique was a little something that Microsoft's public relations folks had cooked up-a programming contest called "Storm the Gates." Bill Gates would be using QuickBASIC 2.0, and the technical computer press people could use whatever language product they might decide to bring.

hat from among several others submitted by the contestants and designed to require about a half hour to program. It went something like this:

window must show a series of increasing numbers, the second must show a series of increasing prime numbers, and the third must show the Fibonacci series. (The Fibonacci series begins with the numbers 0 and 1, and every successive number is the sum of the two before it-that is, 0, 1, 1, 2, 3, 5, 8, and so forth.) These three windows should either scroll or dear themselves when the numbers reach the bottom of the window. The fourth window must display circles of random radii, and the program must terminate with a press of the Escape key.

much more than a multitasking simulation, and none of the contestants were brave enoughand most not yet knowledgeable enough - to code it for Windows. Moreover, to do so from scratch would almost certainly have taken longer than a half hour!

divided the screen into four areas. The program contained a loop that sequentially updated each window and then checked if the Escape key had been pressed. As is customary under DOS, the program used 100 percent of CPU processing.

something like the MUL111 program shown in Figure 20-2. I say "something like" because I've converted the program to 32-bit processing. But the structure and much of the code - aside from variable and function parameter definitions and the Unicode support-would have been the same.

window creates four child windows, each of which occupies one-quarter of the client area. The main window also sets a Windows timer and sends WM_TIMER messages to each of the four child windows.

the contents of its window during the WM_PAINT message. MULTI1 doesn't do this, but the windows are drawn and erased so rapidly that I didn't think it necessary.

works. A number is prime if it has no divisors except 1 and itself. To check if a particular number is prime, however, doesn't require dividing by all numbers and checking for remainders up to that number being checked, but only up to the square root of that number. That square root calculation is the reason for the unusual introduction of floating-point math in an otherwise all integer-based program.

timer is a fine way to simulate multitasking in earlier (and current) versions of Windows. However, the use of the timer sometimes restricts the speed of a program. If the program can update all its windows within a single WM_TIMER message with time to spare, then it's not taking full advantage of the machine.

WK-71MER message. But how many? That would have to depend on the speed of the machine, and that is a major variable. One would not want to write a program tuned to a 25-MHz 386 or a 50-MHz 486 or a 100-GHz Pentium VII.

The Multithreaded Solution

Let's take a look at a multithreaded solution to this programming problem. The MULT12 program is shown in Figure 20-3.

those in MULTI1.C. WndProc registers four window classes for the four windows, creates those windows, and resizes them during the WM_SIZE message. The only difference in WndProc is that it no longer sets the Windows timer or processes WM_TIMER messages.

creates another thread of execution by calling the _beginthread function during the WM_CREATE message. in total, the MULT12 program has five threads of execution that run concurrently. The main thread contains the main window procedure and the four child window procedures. The other four threads use the functions named Thread1, Thread2, and so forth. These other four threads are responsible for drawing the four windows.

third argument to _beginthread. This argument allows a thread that creates another thread to pass information to the other thread in a 32-bit variable. Customarily, this variable is a pointer, and also customarily, it is a pointer to a structure. This allows the creating thread and the new thread to share information without the use of global variables. As you can see, there are no global variables in MULT12.

the program and a pointer to that structure named PPARAMS. This structure has five fieldsa window handle, the width and height of the window, the height of a character, and .1 Boolean variable named Will. This final structure field allows the creating thread to inform the created thread when it's time to terminate itself.

sequence of increasing numbers. The window procedure has become quite simple. The only local variable is a PARAMS structure. During the WM_CREATE message, it sets the hwnd and cyChar fields of this structure and calls _beginthread to create a new thread using the Thread1 function, passing to it a pointer to this structure. During the WM_SIZE message, WndProc1 sets the cyClient field of the structure, and during the WM_DESTROY message, it sets the bKill field to TRITE. The Thread1 function concludes by calling _endthread. This is not strictly necessary because the thread is destroyed after exiting the thread function. However, _endthread is useful for exiting a thread deep within some complex levels of processing.

concurrently with the other four threads of the program. The function receives a pointer to the PARAMS structure and runs in a while loop, checking each time through the loop whether the Will field is TRUE or FALSE. If FALSE, the function essentially performs the same processing as during the WM_TIMER message in MULTI1.C- formatting the number, obtaining a device context handle, and displaying the number using TextOut.

much faster than in MULTI1, indicating the program is using the power of the processor more efficiently. There's another difference between MULTI1 and MULT12: Usually when you move or size a window, the default window procedure enters a modal loop and all output to the window stops. In MULT12, the output continues.

Any Problems?

It may seem as if MUL112 is not as bulletproof as it could be. To see what I'm getting at, let's look at some examples of multithreaded "flaws", in MULTI2.C, using WndProc1 and Thread1 as an example.

with it. The times at which Windows 98 switches between these two threads are variable and unpredictable. Suppose Thread1 is running and has just executed the code that checks whether the Will field of the PARAMS structure is TRUE. It's not, but then Windows 98 switches control to the main thread, at which time the user terminates the program. WndProc1 receives a WM_DESTROY message and sets the bKill parameter to TRUE. Oops! Too late! Suddenly the operating system switches to Thread1, and that function attempts to obtain a device context handle to a nonexistent window.

bulletproof that the graphics functions simply fail without causing any problems.

synchronization (and, in particular, critical sections), which I'll discuss in more detail shortly. Basically, critical sections are delimited by calls to EnterCriticalSection and LeaveCriticalSection. if one thread enters a critical section, another thread cannot enter a critical section. The latter thread is blocked on the EnterCriticalSection call until the first thread calls LeaveOlticalSection.

WM_ ERASEBKGND or WM_PAINT message during the time that a secondary thread is drawing its output. Again, using a critical section would help prevent any problems that could result from two threads attempting to draw on the same window But experimentation seems to show that Windows 98 properly serializes access to the graphics drawing functions. That is, one thread can't draw on a window while another thread is in the middle of doing so.

are not serialized, and that involves the use of GDI objects, such as pens, brushes, fonts, bitmaps, regions, and palettes. It is, possible for one thread to destroy an object that another thread is using. The solution to this problem requires use of a critical section or, better yet, not sharing GDI objects between threads.

The Benefits of Sleep

I've discussed what I consider to be the best architecture of a multithreaded program, which is that the primary thread creates all the program's windows, contains. all the window procedures for these windows, and processes all messages. to the windows. Secondary threads carry out background jobs or lengthy jobs.

animation in Windows is done with WM_TIMER messages. But if a secondary thread does not create a window, it cannot receive these messages. Without any timing, the animation would probably run much too fast.

function to suspend itself Voluntarily. The single argument is a time in milliseconds. The Sleep function call does not return until the specified time has elapsed. During that time, the thread is suspended and is allocated no time slices (although obviously the thread still requires a small amount of processing time during timer ticks when the system must determine whether the thread should be resumed). An argument of 0 to the Sleep function causes the thread to forfeit the remainder of its time slice.

amount of time The system still runs other threads, either in the same process or another process. I used the Sleep function in the SCRAMBLE program in Chapter 14 to slow down the scrambling operation.

because it slows clown message processing; because SCRAMBLE did not create any windows, there is no problem using it there.


About once a year, the traffic lights at the busy intersection outside my apartment window stop working. The result is chaos, and while the cars usually avoid actually hitting each other, they often come close.

southbound car and a westbound car cannot pass through an intersection at the same time without hitting each other. Depending on the traffic volume, different approaches are taken to solve the problem. For light traffic at an intersection with high visibility, drivers can be trusted to properly yield. More traffic might require a stop sign, and still heavier traffic would require traffic lights. The traffic lights help coordinate the activity of the intersection (as long as they work, of course).

The Critical Section

In a single-tasking operating system, traditional Computer programs don't need traffic lights to help them coordinate their activities. They run as if they owned the road, which they do. There is nothing to interfere with what they do.

independently of each other. But some problems can arise. For example, two programs could need to read from and write to the same file at the same time. In such cases, the operating system provides a mechanism of shared files and record locking to help out.

gets messy and potentially dangerous. It is not uncommon for two or more threads to share some data. For example, one thread could update one or more variables and another thread could use those variables. Sometimes this poses a problem, and sometimes it doesn't. (Keep in mind that the operating system can switch control from one thread to another between machine code instructions only. If only a single integer is being shared among the threads, then changes to this variable usually occur in a single instruction and potential problems are minimized.)

structure. Often, these multiple variables or the fields of the structure must be consistent among themselves. The operating system could interrupt a thread in the middle of updating these variables. The thread that uses these variables would then be dealing with inconsistent data.

like this could crash the program. What we need are the programming equivalents of traffic lights to help coordinate and synchronize the thread traffic. That's the critical section. Basically, a critical section is a block of code that should not be interrupted.

functions, you must define a critical section object, which is global variable of type CRITICAL_SECTION. For example,


This CRITICAL_SECTION data type is a structure, but the fields are used only internally to Windows. This critical section object must first be initialized by one of the threads in the program by calling

InitializeCriticalSection (&cs) ;

This creates a critical section object named cs. The online documentation for this function includes the following warnings: "A critical section object cannot be moved or copied. The process must also not modify the object, but must treat it as logically opaque." This can be translated as "Don't mess around with it, and don't even look at it."

critical section by calling

EnterCriticalSection (&cs) ;

At this point, the thread is said to "own" the critical section object. No two threads can own the critical section object at the same time. Thus, if another thread has entered a critical section, the next thread calling EnterCriticalSection, with the same critical section object will be suspended in the function call. The function will return only when the first thread leaves the critical section by calling

LeaveCriticalSection (&cs) ;

At that time, the second thread-suspended in its call to EnterCriticalSection-will own the critical section and the function call will return, allowing the thread to proceed.

be deleted by calling

DeleteCriticalSection (&cs) ;

This frees up any system resources that might have been allocated to maintain the critical section object.

will come up again as we continue to explore thread synchronization. Only one thread can own a critical section at any time. Thus, one thread can enter a critical section, set tile fields of a structure, and exit the critical section. Another thread using the structure would also enter a critical section before accessing the fields of the structure and then exit the critical section.

and cs2. If a program has four threads and the first two threads share some data, they can use one critical section object, and if the other two threads share some other data, they can use a second critical section object.

main thread. If the secondary thread spends a long time in its own critical section, it could hang the main thread for an inordinate amount of time. The secondary thread would probably just want to use the critical section to copy the fields of the structure to its own local variables.

coordinating threads within a single process only. But there are cases where you need to coordinate two different processes that share a resource (such as shared memory) You can't use critical sections for that, instead, you must use something oddly called a "mutex object." The fabricated word "mutex" stands for "mutual exclusion," and that's precisely the goal here. You want to prevent threads of a program from being interrupted while updating or using some shared memory or other resources.


The most common use of multiple threads of execution is for programs that find they must carry out some lengthy processing. We can call this a "big job," which is anything a prograin has to do that might violate the 1/10-second rule. Obvious big jobs include a spelling check in a word processing program, a file sort or indexing in a database program, a spreadsheet recalculation, printing, and even complex drawing. Of course, as we know by now, the best solution to following the 1/10-second rule is to farm out big jobs to secondary threads of execution. These secondary threads do not create windows, and hence they are not bound by the 1/10-second rule.

thread when they have completed, or for the primary thread to abort the job the secondary thread is doing. That's what we'll examine next.

"savage" benchmark, as a hypothetical big job. This calculation increments an integer in a roundabout manner: it squares a number and takes he square root ( which cancels out the square), applies the log and exp funtions (which also cancel each other out), applies the atan and tan functions. ( another cancelingout) and finally adds 1 to the result.

This is a fairly simple program, but I think you'll see how it illustrates a generalized approach to doing big jobs in a multithreaded program. To use the BIGJOB1 program, click with the left mouse button on the client area of the window. this begins 1,000,000 repitions of the savage calculation. It'll take about 2 second on a 300-MHz Pentium II machine. When the calculation has completed, the elapsed time is displayed in the window. While the calculation is in progress, you can click on the client area with the right mouse button to abort it.

So, let's take a look how this done:

The window procedure maintains a static variable called iStatus ( which can be set to one of three constants defined near the top of the program beginning with the prefix STATUS), indicating whether the program is ready to do a calculation, working on a calculation, or done with a calculation. The program uses the iStatus variable during the WM_PAINT message to display an appropriate character string in the center of the client area.

also defined near the top of the program) to share data between the window procedure and the secondary thread. The structure has only two fields-hwnd (the handle of the program's window) and bContinue, which is a Boolean variable used to indicate to the thread whether to continue the calculation or not.

procedure sets the iStatus variable to STATUS_WORKING and initializes the two fields of the PARAMS structure. The hwnd field of the structure is set to the window handle, of course, and bContinue is set to TRUE.

thread function, called Thread, begins by calling GetCurrentTime to get the elapsed time in milliseconds that 'Windows has been running. It then enters a for loop to do 1,000,000 repetitions of the savage calculation. Notice also that the thread will drop out of the loop if bContinue is ever set to FALSE.

1,000,000 calculations. If so, it calls GetCurrentTime again to get the elapsed time and then uses SendMessage to send the window procedure a program-defined WM_USER_DONE message with the elapsed time as lParam. If the calculation was aborted prematurely (that is, if the bContinue field of the PARAMS structure became FALSE during the loop), the thread sends the window procedure a WK_USER_ABORTED message. The thread then gracefully ends by calling _endtbread.

set to FALSE when you click on the client area with the right mouse button. This is how the calculation is aborted before completion.

type qualifier indicates to die compiler that a variable might be modified in some way other than actual program, statements (such as by another thread). Otherwise, an optimizing compiler might assume that since pparatw->bContinue couldn't possibly be modified by the code inside the for loop, it's not necessary for the variable to be checked following every iteration of the loop. The volatile keyword prevents such optimizations.

The, window procedure processes the WM_USER_DONE message by first saving the elapsed time. Both the processing of the WM_USER_DONE and WM_USER_ABORTED messages continue with a call to InvalidateRect to generate a WM_PAINT message and display a new text string in the client area.

field in the structure, to allow the thread to terminate gracefully. The KillThread function should be used only when graceful termination is awkward. The reason why is that threads can allocate resources, such as memory. If this memory is not freed when the thread Terminates, it will still be allocated. Threads are not processes: allocated resources are shared among all threads in a process, so they are not automatically freed when the thread" terminates. Good programming structure dictates that a thread should free any resources it allocates.

still in progress. This could happen if Windows switches control from the second thread to the first thread between the SendMessage call and the _endthread call, and the window procedure then creates a new thread on response from a mouse click. This is not a problem here, but if it is a problem in one of your own applications, you'll want to use a critical section to avoid thread collisions.

The Event Object

BIGJOB1 creates a thread every time it needs to perform the savage calculation; the thread terminates after doing the calculation.

program and only kick it into action when necessary. This is an ideal application for an event object.

(also known as "reset"). You create the event object by calling

hEvent = CreateEvent (&sa. Manual. fInitial. pszName);

The first parameter (a pointer to a SECURITY_ATTRIBUTES structure) and die last parameter (an event object name) are meaningful only when event objects are shared among processes. In a single process, these parameters are generally set to NULL. Set the fInitial parameter to TRUE if you want the event object to be initially signaled and to FALSE for initially unsignaled. I'll describe the fManual parameter shortly.

To signal an existing event object, call

SetEvent (hEvent);

To unsignal an event object, call

ResetEvent (hEvent) ;

A program generally calls

WaitForSingleObject (hEvent, dwTimeOut) ;

with the second parameter set to INFINITE. The function returns immediately if the event object is currently signaled (or set). Otherwise, the function will suspend the thread until the event object becomes signaled. YOU can set the second argument to a time-out value in milliseconds so that the function returns before the event object becomes signaled.

the event object becomes automatically unsignaled when the WaitForSingleObject function returns. This feature usually makes, it unnecessary to use the ResetEvent function.

So, now we're equipped to look at BIGJOB2.C, shown in Figure 20-5.

WaitForSingleObject at the beginning of the loop. (Notice that the PARAMS structure includes a third field containing the handle to the event object.) Because the event is initially unsignaled, the thread is suspended in the function call. A left mouse button click causes the window procedure to call SetEvent. This releases the second thread from the WaitForSingleObject call, and it begins the savage calculation. After finishing, the thread calls WaitForSingleObject again, but the event object has become unsignaled from the first call. Thus, the thread is suspended until the next mouse click.

Otherwise, the program is almost identical to BIGJOB1.


Global variables in a multithreaded program, as well as any allocated memory, are shared among all the threads in the program. Local static variables in a function are also shared among all threads using that function. Local automatic variables in a function are unique to each thread because they are stored on the stack and each thread has its own stack.

thread. For example, the C strtok function I mentioned earlier in this chapter requires this type of storage. Unfortunately, the C language does not support such a variable. But Windows includes four functions that implement a mechanism to do it, and the Microsoft extensions to C also support it. As we've seen, this is called thread local storage.

Here's how the APIs work:

among the threads. For example,

 typedef struct int a int b DATA, * PDATA

The primary thread calls TlsAlloc to obtain an index value:

dwTlslndex = TlsAlloc

This index, value can be stored in a global variable or passed to the thread function in the parameter structure.

calling TlsSetValue using the index obtained above:

 TlsSetValue (dwT1sIndex. GlobalAlloc (GPTR, sizeof (DATA))

This associates a pointer with a particular thread and a particular thread index. Now any function that needs to use this pointer, including the original thread function itself, can include code like so:

 PDATA pdata ; pdata = (PDATA) TlsGetValue (dwTIsIndex)   ;

Now it can set or use pdata->a and pdata->b. Before the thread function terminates, it frees the allocated memory:

GlobalFree (TlsGetValue OwTlsIndex)) ;

When all the threads using this data have terminated, the primary thread frees the index:

T1sFree (dwTlslndex) ;

see how thread local storage might be implemented. (I have no knowledge of how Windows actually does it, but the following is plausible.) First, TlsAlloc might simply allocate a block of memory (zero bytes in length) and return an index value that is a pointer to this block. Every time TlsSetValue is called with that index, the block of memory is increased by 8 bytes by reallocating it. Stored in these 8 bytes is. the ID of the thread calling the functionobtained by calling GetCurrentThreadId-and the pointer passed to the TlsSetValue function. TlsGetValue simply uses the thread ID to search the table and then return the pointer. TlsFree frees up the block of memory. So, as you see, this is something you could probably easily implement yourself, but it's nice to have the facility already done for us.

variable that needs to be different for each thread with _declspec (thread), like so

 _dec1lspec (thread) int iGlobal = 1     ; for static variables external to any function, or like so _declspec (thread) static int iLocal = 2     ;

for static variables within functions.

Read More Show Less

Customer Reviews

Average Rating 4.5
( 8 )
Rating Distribution

5 Star


4 Star


3 Star


2 Star


1 Star


Your Rating:

Your Name: Create a Pen Name or

Barnes & Review Rules

Our reader reviews allow you to share your comments on titles you liked, or didn't, with others. By submitting an online review, you are representing to Barnes & that all information contained in your review is original and accurate in all respects, and that the submission of such content by you and the posting of such content by Barnes & does not and will not violate the rights of any third party. Please follow the rules below to help ensure that your review can be posted.

Reviews by Our Customers Under the Age of 13

We highly value and respect everyone's opinion concerning the titles we offer. However, we cannot allow persons under the age of 13 to have accounts at or to post customer reviews. Please see our Terms of Use for more details.

What to exclude from your review:

Please do not write about reviews, commentary, or information posted on the product page. If you see any errors in the information on the product page, please send us an email.

Reviews should not contain any of the following:

  • - HTML tags, profanity, obscenities, vulgarities, or comments that defame anyone
  • - Time-sensitive information such as tour dates, signings, lectures, etc.
  • - Single-word reviews. Other people will read your review to discover why you liked or didn't like the title. Be descriptive.
  • - Comments focusing on the author or that may ruin the ending for others
  • - Phone numbers, addresses, URLs
  • - Pricing and availability information or alternative ordering information
  • - Advertisements or commercial solicitation


  • - By submitting a review, you grant to Barnes & and its sublicensees the royalty-free, perpetual, irrevocable right and license to use the review in accordance with the Barnes & Terms of Use.
  • - Barnes & reserves the right not to post any review -- particularly those that do not follow the terms and conditions of these Rules. Barnes & also reserves the right to remove any review at any time without notice.
  • - See Terms of Use for other conditions and disclaimers.
Search for Products You'd Like to Recommend

Recommend other products that relate to your review. Just search for them below and share!

Create a Pen Name

Your Pen Name is your unique identity on It will appear on the reviews you write and other website activities. Your Pen Name cannot be edited, changed or deleted once submitted.

Your Pen Name can be any combination of alphanumeric characters (plus - and _), and must be at least two characters long.

Continue Anonymously
Sort by: Showing all of 8 Customer Reviews
  • Anonymous

    Posted May 7, 2014

    This is the 5th edition, but the table of contents shown on this

    This is the 5th edition, but the table of contents shown on this page is for the *6th* edition. Those editions were written 15 years apart. The 5th edition doesn't include anything about Windows 8 and WinRT and XAML. Be sure which version you are buying.

    Was this review helpful? Yes  No   Report this review
  • Anonymous

    Posted March 23, 2013

    Great reference.

    The style of the book is easy to follow. The example programs are full, complete, and much appreiated. Although you might have to typecast to make some of the code work with the newer versions of some of the functions.

    Was this review helpful? Yes  No   Report this review
  • Anonymous

    Posted April 24, 2003

    A thorough book on the Win32 API

    Get this book if you want to learn the Win32 API.

    Was this review helpful? Yes  No   Report this review
  • Anonymous

    Posted June 22, 2001


    Wonderful book! I'd recommend this hands-down!

    Was this review helpful? Yes  No   Report this review
  • Anonymous

    Posted September 13, 2000

    inconsistent so far

    This book is inconsistent so far. The very simple 'Welcome to Windows 98' program finally comes around in Chapter 3 before a meaningless diversion into Unicode. I type the basic windows program into Visual C++ 6, run it, and instead of it printing out 'Welcome to Windows98' like it should, it tells me I need Windows NT. I mean, what is the author thinking? Why would you need to run Windows NT if the program is supposed to be your first Windows 98 program. A few pages into the program explanation, the author says this program will not work in Windows 98 but doesn't present a workaround for Windows 98. Is this the Microsoft way? Anyway, if you don't have Windows NT, forget about this book even if it does say on the cover and in the text that Windows 98 is ok.

    Was this review helpful? Yes  No   Report this review
  • Anonymous

    Posted February 15, 2000


    If you've ever wanted to do any type of GOOD programs for Windows (Linux IS better though), buy this. It teaches you SOOO much about the infrastructure of Windows, how things work, etc., etc., etc.

    Was this review helpful? Yes  No   Report this review
  • Anonymous

    Posted January 28, 2000

    Must be included in required books list for all CS majors

    The only regret: Why, oh why I did not read it earlier!

    Was this review helpful? Yes  No   Report this review
  • Anonymous

    Posted February 27, 2009

    No text was provided for this review.

Sort by: Showing all of 8 Customer Reviews

If you find inappropriate content, please report it to Barnes & Noble
Why is this product inappropriate?
Comments (optional)