Time is the Simplest Thing...

Introduction

Time is a Simple Thing...which is actually the title of a science fiction story by Clifford D. Simak; my edition was published in 1961. Fairly unmemorable is my recollection, something that can't often be said about his work.

But in fact, time isn't simple, not in operating systems. The number of startlingly naive assumptions that I've seen made about time are astonishing. This essay attempts to clear things up.

Windows is not a Real Time Operating System

This is the most common misconception. There is not, nor has there ever been, an attempt to make Windows a real-time operating system. If you have the slightest belief that this is possible, forget it. Windows, like most other general-purpose operating systems, has only a vague notion of time, and accuracy is not a concept it cares terribly much about. A true Real-Time Operating System (RTOS) is designed with time as its most important parameter. Any time you believe Windows might support real-time constraints, remember to recite this mantra: "Windows is not a Real Time Operating System, Windows is not a Real Time Operating System, ..." and repeat it until you understand it.

There are Real Time versions of Windows

A company called VenturCom (http://www.vci.com/) produces a real-time OS kernel that allows Windows NT to run on top of it. They also have real-time Windows CE. This is an add-on. Note that other than knowing of the existence of this company, I know no other details other than the ones I learned talking to them at a trade show. So I neither endorse nor recommend this product; but I don't have any reason to say it is not what they claim it to be. No data. Make your own evaluation. Go to their Web site and learn everything I know.

Normal Timing

There are many things that can control what happens with timing. Most of these apply to ordinary timing and to multimedia timers. I will state here that I know virtually nothing about the multimedia timers in Win32, but what I do know is that everything that can affect timing normally can interfere with them as well.

System Clock Quantum

The first, and one of the most significant, impacts on timing is the system clock quantum. The system clock ticks at a predetermined rate, and anything that is going to happen happens when those clock ticks happen. Between clock ticks, in essence, there is no way for the system to have any idea what time it is. The clock timings for Windows are shown below.

Operating System

System Clock

Windows 95, 98

55 ms

Windows NT, Uniprocessor HAL

10 ms

Windows NT, Multiprocessor HAL

15 ms

Windows NT 4.0 Alpha

7.5 ms

No matter what time you request, you will not get a resolution better than the above times. This means that if, on Windows 98, you request a Sleep(10) to sleep 10 milliseconds, you might find that you don't get control back for 55ms. This means your loop will execute 18 times per second, not 100 times as you might expect. The way this works, or at least a working explanation of it, is that there is a queue of events which are to be scheduled at a certain time. When the timer ticks, that queue is examined, and any event whose time has expired is marked for execution. When it next requests a sleep time, a time is computed for when it should be awakened, and the event is scheduled for that time. The next time the timer ticks, the event will be scheduled if its time has expired.

Gating error

Gating error is a common phenomenon when you have discrete sampling intervals. This was first documented when the early automated event counters, used to measure nuclear decay events, were being constructed; I think this dates back to the late 1940s. It may have been known before that, but my readings on history of technology suggest that the phenomenon was certainly identified as something interesting at that time.

You see this phenomenon if your car has an odometer which records only whole miles (as many modern ones do). You live 3.3 miles from a destination. Some days when you travel there, your odometer reports 3 miles, sometimes it reports 4. Why? If it was really reading 18002.8 miles, adding 3.3 miles gives 18006.1, but you see 18002 go to 18006, so it looks like 4 miles. Other days, you might start at 18143.1 miles and adding 3.3 miles gives 18146.4 miles, so you see 18143 go to 18146, or 3 miles.

If you schedule a sleep for 10 ms more than 10 ms before the next 55-ms clock tick of Win9x, you will find that it occurs less than 55ms. In fact, applying normal statistical sampling techniques, you will find that your probability of hitting the middle of a sample will give you a normal distribution around 55/2ms, or 27.5ms. So mostly you will see 27.5ms, with a few rare instances taking only 10ms and a few rare instances taking 55ms. But this is three times slower than you expect. If you were trying to do an animation, it says that you will run no better than 18 frames per second, but the intervals between those frames can vary by a factor of more than 5, from as little as 10ms to as long as 55ms. This is not real-time by any stretch of the imagination. Essentially, no matter what interval you choose, you round up to 1/2 the quantum above it and expect that will be the best you can do, on the average. It is more pronounced at shorter intervals because the error is a larger percentage of the total time interval.

While it is a little better on NT, running between 10ms and 15ms quantization, there are other considerations that can make this unrealizable.

Scheduling Latency

Saying your thread is schedulable because its wait time has completed does not imply that it will run immediately. The scheduler has to get around to running it. Even if it is the highest-priority feasible thread, it has to make its way through the scheduler.

Paging

If the thread has been idle for a while, and was the only feasible thread in the process, your process has become a candidate for being paged out. The first thing that happens when a process becomes schedulable is that its "working set", the collection of pages that are the likeliest ones to be used by the process, are guaranteed to be in memory. This can take tens of milliseconds for each page on a reasonable system. If the working set is large, it can be hundreds of milliseconds between the time the timer expires and the thread can really execute.

Priorities

Threads have priorities. The Windows schedulers are very simplistic, because over the years we have found that simple schedulers work best. What this means is that on a single-processor system, the highest priority feasible thread always runs. A feasible thread is one which is not blocked on I/O, a message wait, a synchronization object, or a timer wait, and all of whose pages are in memory and ready for use (that's all of the pages of the working set, not necessarily all of the pages of the process, which can be very, very large, larger than physical memory). So if your timer expires, and there is a higher-priority thread ready to run, your thread won't get scheduled. In fact, it won't even be looked at until that higher-priority thread blocks. This could take a very long time. Certainly hundreds of milliseconds is not out of the question. Note that even if the high-priority thread exhausts its timeslice, if there is no other thread to run at the same priority, that thread will be scheduled again. And if any thread can preempt it, that thread is at the same or higher priority, either preempting it because it is now its turn ("round-robin within priority level"), or preempting it because it is already a higher priority. A higher-priority thread will preempt any running thread, taking away the rest of its timeslice.

In Windows NT, there are a number of important kernel threads, all of which are running at a much higher priority than user applications. The entire file system, for example, uses high-priority kernel threads. Once one of these threads starts running, it will run "to completion", that is, until it suspends itself on a block of some sort. And your thread will wait.

There are a couple quirks in the simplistic scheduler model, primarily to give better responsiveness to users. These add to the confusion.

Foreground/Background priority

Any foreground process will have its threads boosted to 2 units above the background priority. This makes the program you are working on more responsive to the mouse and keyboard than a program that is running in the background, for example, checking email.

Priority Boots

When a thread leaves an I/O wait condition, it is given a one-time-only priority boost for its first timeslice after coming out of wait. This allows it to preempt other threads. Some of the priority boosts, from the device driver header file, are shown in the table below. Note that these are not necessarily absolute boosts in priority (their significance is carefully not documented, so we don't know if they are simple integers added to the current priority or they are converted to some fractional-priority boost). But what this means, for example, is that a background program that comes out of I/O wait on an audio device is very likely to be able to exceed the priority of your foreground task that is simply doing a long computation. And moving the mouse is going to give your foreground thread, the one talking to the GUI, priority over the background thread of your foreground task that is talking to a serial port. (It doesn't know the difference between a background or foreground thread; all it knows is the thread that was waiting came out of a mouse wait, so that thread gets boosted).

Designation

Priority Boost

IO_NO_INCREMENT

0

IO_EVENT_INCREMENT

1

IO_CD_ROM_INCREMENT

1

IO_DISK_INCREMENT

1

IO_PARALLEL_INCREMENT

1

IO_SEMAPHORE_INCREMENT

1

IO_VIDEO_INCREMENT

1

IO_NAMED_PIPE_INCREMENT

2

IO_NETWORK_INCREMENT

2

IO_SERIAL_INCREMENT

2

IO_MAILSLOT_INCREMENT

4

IO_KEYBOARD_INCREMENT

6

IO_MOUSE_INCREMENT

6

IO_SOUND_INCREMENT

8

This says that if you have a background task that is controlling something, that it will almost never be able to compete with the foreground task. Your scheduling delays can be arbitrarily long.

Interrupts

Hardware interrupts preempt everything. In addition to the interrupts themselves, the NT I/O architecture uses a mechanism called the deferred procedure call (DPC) to handle most of the I/O operations at an interruptible level. However, this interruptible level is a software level higher than all schedulable threads. So while the DPC mechanism allows NT to work with devices that have high interrupt rates and low latency tolerance, the DPC mechanism further reduces the guarantees that an application has about running.

And interrupts and the DPC can request, at least in NT, the file system threads be scheduled, adding further delays.

Defective Hardware Design/Implementation

There was a significant defect in either the specification or the implementation of IDE 1.0-compliant devices. I'm not sure which it was. But it means that the IDE disk and CD-ROM drivers of your machine compensate for this by keeping the application program locked out for as long as 1500 ms (that's 1.5 seconds!) while they try to compensate for real or imagined hardware defects. The general advice is that if you have any need for a responsive system, you must not have any IDE devices on your machine. Use SCSI-only secondary storage, CD-ROMs, and/or DVDs.

WM_TIMER

Many programmers try to depend on WM_TIMER events coming in at fairly small intervals (a typical question shows a SetTimer with an interval of 10ms, followed by the question "Why don't I get my timer messages quickly enough?", usually in the same message stating that Win9x is the platform).

WM_TIMER messages cannot come in faster than the scheduling quantum effects. That is, just like the Sleep() calls, you will see a uniform distribution with a normal curve centered on half the quantum interval. But it is far worse than that for WM_TIMER.

There are two "special" messages in the "message queue" of a Windows application. One of these is WM_PAINT, a message which is never in the message queue. What happens is that in the window description a bit is set indicating there is a need to paint. When there are no other messages in the message queue, and no WM_TIMER to send, Windows simulates a WM_PAINT message! It is never, ever in the queue. Doing a SendMessage of WM_PAINT will have unpredictable results. This is because in the process of synthesizing the WM_PAINT message, Windows also computes the clipping region and the clipping rectangle for the OnPaint handler and prepares to handle the BeginPaint/EndPaint requests. Without these actions, the behavior of the OnPaint handler is undefined. Hence, if you want a window painted now, you call the UpdateWindow function to force the WM_PAINT to be created and processed immediately. This is the equivalent of sending a WM_PAINT message yourself, but does all the setup and teardown correctly.

The other special message, as suggested above, is WM_TIMER. When a timer fires, some information is placed in the kernel's window object to indicate this condition. When there are no other messages in the queue, but there is a timer request pending, the WM_TIMER message is sent. This means that if you are pushing the mouse around rapidly, or you are using PostMessage, the WM_TIMER message won't be seen for an arbitrarily long time. The WM_TIMER message is synthesized "on the fly" as needed. So even after your thread is scheduled, it can take quite a long time before the WM_TIMER is seen!

There is a way around this, which I used. It is a particularly useful method when you are trying to do panning with the mouse, that is, when the mouse gets near the edge of a control, some sort of scrolling begins and continues as long as the mouse is in that region. The problem here is that the mouse is actually moving, meaning it will interfere with the reception of the WM_TIMER messages. So what I did was use SetTimer with a timer callback, which is not subject to the WM_TIMER restriction, and from the OnTimer handler I did a PostMessage back to the main GUI thread. Since this was a genuine message, it would be handled in FIFO order, and the scrolling became smooth again.

Measured data

I have a friend with whom I've worked in the past who does a lot of real-time computer music work. He has actually done experiments, and has found delays measured on older processors (I don't recall the speed, but it was in the 200MHz days) of up to 400ms from the time the condition at the hardware device generated the interrupt until the user application was dispatched. This is real-time if you are monitoring the temperature in a hot water heater, but not real-time if you are doing computer music. The audio perceptual system is the most precise one we have. An ordinary person can easily detect a 30ms irregularity in a "steady" drumbeat; a professional drummer can detect a 10ms irregularity. Anyone, without training, can detect a 2ms synchronization error between two sharp percussion sounds. Win9x, NT and 2000 are simply not capable of doing this from the application level, by better than two orders of magnitude.

How do I handle small intervals?

Basically, at application level you can't. Not without a real-time add-on like the one from VenturCom. For some tasks, such as those which require fast response to interrupts, what you do is write a device driver (there are several books out on how to do this, including one I co-authored: Developing Windows NT Device Drivers, by Dekker & Newcomer). But at high sustained interrupt rates you get into other problems, such as sucking down so many CPU resources in your driver that no program can run; it doesn't matter if, in the short term, you can meet the desired latency requirements if the application never has time to compute the next data packet to send.

But even drivers can't handle timing intervals of less than the system clock quantum. The very worst possible piece of hardware (which we actually describe in the book, without identifying the turkeys responsible for its existence) required the driver take a regular 1ms interrupt to refresh the onboard RAM of the card. This is simply not possible in any general-purpose NT environment. There is a distinct danger in letting hardware engineers design products without consulting with the software people who have to deal with the consequences of the hardware design. These are the same people who design PCI cards that only require 95% of the total available PCI bus bandwidth to meet the desired data rate, and require 100MB of contiguous physical memory to store the data. Some things are not just not doable in a general-purpose operating system.

Many designers deal with this by making intelligent peripherals. Boards which contain their own CPU and which contain all the really time-sensitive code. Some of these processors are programmed using one of the many embedded-system real-time kernels. Others are just programmed "bare". In some cases, the software for the attached microprocessor is loaded into the card explicitly when the driver starts up; in other cases, it is in ROM or reprogrammable FLASH memory and is never, or rarely, changed.

Measuring time

There are a number of API calls to retrieve time values. Some of these have resolutions in the range of milliseconds, or 100s of nanoseconds. Most of these are simulations. For example, while there is an API call to give you the current time, in milliseconds, in fact it basically tells you the number of ticks that have occurred in the system timer, multiplied by the system timer interval. Thus, you won't see resolution better than 55ms on Win9x. It doesn't tell you how much of that elapsed time was actually spent doing your program. As a performance counter, it is completely meaningless.

QueryPerformanceCounter returns a reasonably accurate rendering of time, at least on Intel platforms. This is because on Intel platforms it returns the contents of a "cycle counter", converted to timing units based on the processor frequency. Because the cycle counter actually counts the number of CPU cycles, it gives a reasonable approximation of real time. However, executing the same loop twice may give different results because the presence or absence of data in the cache will change the number of cycles required to execute the program. So it is at best an approximation, and you have to do some more careful statistical analysis to figure out what is really going on.

The Bottom Line

Windows is not a real-time operating system. Windows is not a real-time operating system. Windows is not a real-time operating system.

What this means is that if you schedule a timer callback, a WM_TIMER, or a Sleep, you will see the event somewhere between the time you specified, and several weeks from now (my favorite example is the terabyte image processing algorithm which uses asynchronous, non-blocking I/O and runs at high-priority so it "finishes faster". Nothing else will run until it finishes!)

While you can often deal with some time-critical events in restricted contexts, the operating system, like Unix, Linux, and every other general-purpose OS that preceded it, makes no guarantees about real-time performance. You must not depend on this, particularly in any environment in which the failure to meet real-time constraints can cause something to fail in an unpleasant fashion.

This is not a design failure of Windows. Complaining that Windows doesn't do real-time is about as meaningful as complaining that the socket wrench you bought at the hardware store can't do a decent weld, and is lousy for driving nails. If you choose the wrong tool for the job, it is not the fault of the designer of the tool. No general-purpose operating system is a real-time system, any more than a good real-time system makes a good general-purpose operating system. Misusing a tool will only get you in trouble, and only a poor craftsman blames his tools. A really bad one chooses the wrong tools for the job, and then blames the tools.

The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.

About the Author

Comments and Discussions

I am a developer of video products and as such I have used several video cards. In the process I learned that a card can produce callbacks that are extremely accurate.
Calling back the OS at 59.94 times a second has been done. My only disappointment is I cant find a simple low cost timer board that does the same thing. As far as what MICROSOFT wants, I could care less. As far as the analogy of this is generic etc. Well a PC would not have a sound card or many other inovations if we only built what the OS was designed for.

Developers make solutions everyday that have created fantastic results that have nothing to do with what Micrsoft wants. So if anyone knows of a PCI card that has a low cost timer on it that I can program to call my application to create realtime windows for me please email me. I do software not hardware so PCI hardware is a bit out of my relm.

Wanting real time window seems as easy as adding your a piece of hardware to give the finger to MS and what they want.

Actually, having a programmable timer board soes absolutely nothing useful for user space. Just because you get an interrupt, say, once every hundred microseconds does NOT mean your app will run every 100us. The best you can do is cause it to be scheduled, and some indeterminate time later it will run. DPC times, scheduler overheads, preemption by higher-priority threads, etc. Having your own interrupts from hardware changes almost nothing in application space.

Dear all,
I just begin with VC++ so anyboy known how to use WM_TIMER please show me add WM_TIMER function in this source code as below:void DlgComputing::OnButtonSendtorobot()
{
int rowData, rowOutput;
int tmpi[3];

on button click:
collect information to send
send first piece of information (n = 0)
set n to 1
set timer

on timer:
send nth piece of information
if n > limit kill timer
You must also disable the button so that during this interval it cannot initiate other actions. See my essay on dialog control management

"(my favorite example is the terabyte image processing algorithm which uses asynchronous, non-blocking I/O and runs at high-priority so it "finishes faster". Nothing else will run until it finishes!)"

How does one create a software application that makes sure "Nothing else will run until it finishes"...
I can't get windows to stop handing over control from my application to other windows applications/processes that are running in the background...

My application communicates with a PCI/Altera Card through the PC's PCI bus to send or collect data. The problem, it seems, is that when my application runs and trys to collect data from multiple devices connected to the PCI card, it drops data here and there... so if I manually set the process priority of my application to the highest it works ok on one PC but not on a different PC(same OS, windows 2000)

It's a bit of an oversimplification for Windows. The Balance Set Manager sees that every app will get at least one timeslice every 3-4 seconds. Note that raising a task's priority to 15 will severely interfere with the ability of the system to do ANYTHING else, including run the task manager, and in some cases the consequences can be serious, such as lost email and the like.

Note that you will *always* be preempted for file system and other kernel activities. However, if you can create a "real-time" process, then you can set the priority to 31. It will own the machine. However, serious malfunctions can ensue because, for example, you've shut down the file system, along with pretty much everything else. Generally, prohibiting other tasks from running is considered poor programming, somewhere between tasteless and actively dangerous. If the user doesn't want those tasks to run, the user will not have started them. If they are system tasks, then they're important, and need to run.

If you are losing data, it strongly suggest the design of the card is at fault. A real piece of hardware expected to run in real environments (as opposed to those environments which exist only in the fevered imagination of the card's designer) must be able to handle a couple hundred milliseconds' delay at all times. Problems like this are invariably the fault of inadequate design, because hardware designers are, to put it mildly, totally clueless about software, and are convinced that if their card can work on a standalone machine, it will work on any machine. Short of forcing the job to run in the realtime queue, with possible severe impact on the overall system reliability (seriously, I mean reliability...if the file system is shut down, you can overflow internal buffers and potentially lose data that way as well, which will probably result in a bluescreen condition when the operation fails internally), there isn't much hope in compensating for inadequate hardware design.

I've worked with hardware for most of my career, and it is truly amazing how naive many otherwise-experienced hardware designers are. Furthermore, they apparently *like* being ignorant of reality, because it allows them to design less-expensive cards because the card doesn't have to have any complexity required for dealing with any form of reality the rest of us live in.

Thank goodness i wasn't totally going insane... I've spent many a day trying to figure out what i've done wrong in my software. You have been very helpful with your reply as well as this article that you have written and I thank you for it.

One last thing, how does one raise a task's priority within the application's code? Or am I doomed to have to go into the task manager everytime I run my application to set the process priority?
If you know of any good artilces or books i can read over and learn how to do this it will be much appreaciated.

Note however that changing a threads priority to real-time will cause all other threads in ring-3 to become starved. I have in the past configured Windows boxes with Explorer removed and dedicated to running a specific process however.

Windows CE would be a better choice for applications requiring real-time.

Sorry if this question appears a little out of the scope of this article. But I thought the author (or readers) of this excellent article should know the things I don't know about the internal functions of Win32:

I have written a simple multithreaded application with only one backround thread running concurrently to the main GUI thread. (This app was written with Delphi but this is not relevant I think).

The background thread (running at tpLowest priority level) performs some PostMessage calls to the message queue of a dummy Window allocated by the Main GUI thread. That means that these user messages will be handled by a WndProc routine executed in the context of the main GUI thread.

Suppose (to simplify) that the backgroud thread is posting a burst of 100 messages per second during 1 second and then waits for 1 minute before sending a new burst of 100 messages.

Suppose that the WndProc routine executed by the main VCL thread requires ~100 msec. to handle each user message sent by the background thread to do some computation and/or update of the display and saving some information to disk...

So, the main GUI thread will require ~10 seconds to handle the burst of 100 messages occuring each minute...

The problem is that, as you mention in your article, the WM_PAINT (and WM_TIMER) messages are special and are considered less prioritary than the other messages in the Windows message queue. Actually that means that during the 10 seconds where the main GUI thread is handling the user messages posted by my background thread, no WM_PAINT messages will be handled. That means that the display will stay frozen until the burst of 100 posted messages will be completely handled !

Even more problematic, I noticed that the mouse messages as well seem to be unhandled during this time, because the GUI is completely frozen during these 10 seconds ! The mouse cursor can be moved but the clicks on buttons are not handled as long as unhandled user messages remain in the Windows message queue !

This is very problematic in my application. Is there something I could do, sometimes, in the WndProc message handling routine of the main GUI thread to force Windows to handle the mouse/paint events if I don't want my user interface do appear frozen during the 10 seconds ?

Note, that this behaviour remains the same whatever the priority level I set to the background thread, even tpLowest...

Thank you very much for any idea and/or theoretical explanations in that concern.

I have written a multimedia application which runs corretly on Windows NT/ 2000 / XP with processor speeds up to about 1.4GHz.

It relies on a WM_TIMER messages of 50ms in a hight priority thread for audio output (I have to do some tricks to compensate delays caused by display update etc., but normally you don't hear drop-outs).

On fast processors (I have tried 1.8 and 2 GHz Pentium 4 on 2000 and XP Home) the timer is 62.x ms instead of 50.x.

Is this a bug in windows (yes I know windows is not designed for this) or simply a different timer resolution?

Probably. One or the other, or neither, or both. Time is unreliable in general-purpose operating systems. Always has been, always will be, until the OS designers start recognizing realtime constraints, which seems unlikely in the near term. You could be seeing a scheduling problem, or a timer resolution problem, or a conflict with a kernel thread, or who-knows-what. Essentially, if you have timing problems in any Windows app, the only answer is "yeah, that's what operating systems do". It is no different on any of the dozen or more operating systems I've used over the last four decades.
Joe

What about timing based on a hardware interrupt? In my case, I'd like to know a way to synchronize signals to a port with the vertical refresh, and it is important that no vsync is missed. Is this possible in XP?

Well, there is (a) no way to detect a hardware interrupt, such as vertical retrace, from an application program and (b) even if you could, it wouldn't do any good because you would still have thread priorities in the way; the minimum delay under optimal conditions would be several tens of milliseconds, and under typical conditions hundreds of milliseconds. Now, if you really need high-precision timing based on interrupts, you can certainly build the logic into the interrupt handler for your device, and put a timer onto your card. But if your device is not the display, you can't get the interrupts from *another* device in your device handler.

Bottom line: this was barely possible in MS-DOS, and for most practical purposes (unless you are writing a display driver) it is impossible in standard NT/2000/XP. Note: DirectDraw and the like seem to have a provision for actions to be taken on vertical retrace, but that's an area I know practically nothing about.

I was just looking around this site and thats when this article came up as the random pick. Boy!

But I have this to say :-

Giving Sleep(1) is still better than Sleep(55)
When you give Sleep(55) you are ensured of 55 secs [other factors not considered] but when you give Sleep(1) you stand a good chance of getting anything between 1 and 54 milli-seconds [which is what you want if you want the shortest sleep possible]

What I mean is that when you want to get back control as soon as possible you better give Sleep(smallest number you can think of) instead of Sleep(clock-tick rate of your OS)

Regards and thanks for a brilliant piece of writing,
Nish

p.s. I don't think I could have got that info from any book out there. Like I said,Way Cool Joe

Thanks for the very positive feedback! It is always nice to know that these essays are useful!

And you are right. To sleep the minimum time required, indeed, choose the shortest time that is appropriate. Your thread will not run for *at least* that amount of time. The problem comes up when you want that to be the *maximum* amount of time, which is what most programmers are expecting. For example, one correspondent was dismayed when he was not getting 100 messages per second from his SetTimer(...,10,...) call. That's what prompted me to write this particular article.
joe

Although I was aware that WM_PAINT was a special message in that it was only ever sent when the queue was empty, I was not aware that sending it yourself gave unpredictable results
This could explain why I have not been able to track down an error in an app of mine. After a long period of time (10+ mins) it appears that a resource leak kills off my apps drawing ability. The symptoms being that the whole screen goes grey - it affects all other running apps as well until mine is shut down. I have done extensive checking on the code and have made sure that here are no resource leaks etc (even Boundschecker says there arn't any) to my knowledge.

So it looks like you may have pointed me to the problem I have spent many hours trying to trackdown. It could be that this point should be posted in an artical of its own as I think it is very significant, and having it buried in something unrelated can make it hard to find.

I would like to say that I have read many of your articals here and find then very useful and informative, even if they only reinforce knowledge I already had. Its good to know someone as experienced as you thinks the same way about many programming issues.

Thank you for all the work you have put in in the open source effort and for sharing your considerable expertise

Conspicuous by its absence here is any call to deselect the pen from the DC. When the destructor for the CPen is called as you leave scope, a DeleteObject is invoked but nothing actually happens; the pen remains allocated. In Win9x, the pool of avaiable GDI resources is shared among all processes, so eventually it fills up.

There are two approaches:
{
CPen pen(...);
int save = dc.SaveDC();
...anything you want here...
dc.RestoreDC(save);
}
which I find easiest, or

The problem is that there are something around 30 characteristics of a DC you may want to modify, and keeping track of the old pen, the previous old pen, the original old pen, the current old pen, and the old pen about to be, then multiplied by all the objects you may wish to modify (background color, text color, brush, region, clipping region, bitmap, mapping mode, window origin, window extent, viewport origin, viewport extent, ROP, current position, among others) results in an unmaintainable mess in the second form. I just use SaveDC/RestoreDC extensively (check out my CSaveDC class, somewhere here on codeproject, which makes this easier).

I do actually make use of SaveDC/RestoreDC in all of my regular drawing code, nesting as required. I saw your other artical which proposed your SaveDC/RestoreDC encapsultion class, which is a very nice way of handling this.

The actual symptoms I described with the whole screen being painted grey affects all OS versions as far as I can see. I know for certain it happens on 2000/NT and 98.

I will run a check through my code for the other characteristics of DC's. I always make sure I restore fonts/brushes/pens etc in DC's it could be I have missed a problem with background colours; transparency states; mapping modes etc also.

When I tried to debug the problem, I put breakpoints on every occurances of a paint routine and WM_PAINT in the WndProc procedure but was unable to trap the code that actually did the drawing of this grey brush. Other symptoms was that windows were drawn in the wrong place etc, even in other apps.

I did a check through my app for calls to SendMessage and WM_PAINT and only turned up one, in a module that wasn't actually being called in cases where this problem has manifested itself.

Any other suggestions that may be the cause of this? I am sure it is resource realted in some way.