Hey, I'm Dave, Welcome to my Shop.
Today in Dave's Garage, it's Part Two of the History of Windows Task Manager.
I'm Dave Plummer, it's original author, and I'm going to take you on a little technical
look back at the Task Manager.
In the first segment we covered the back story of how the Windows Task Manager came to be,
and in this segment, we'll take a look at some of the more interesting behind-the-scenes
technical issues before we turn our attention to the actual source code.
When putting a new block of code like Task Manager into the source tree, you can't just
stick a piece of shareware into an operating system and call it good, particularly when
that code will be somewhat mission critical.
There is a huge difference in required code quality and robustness between an app that
you cranked out over a weekend and an important part of the Windows operating system.
Well, usually there is.
It would take a lot of work to bring my early draft up to the standard required for inclusion
in the operating system, and that work would be part of my day job for the next couple
of months.
As you can imagine, when it comes to an app like Task Manager, the accuracy of the data
its reporting is paramount, and early in its development I was chasing an annoying bug
related to total CPU usage.
It was all calculated very precisely for each process based on the system accounting, but
when they were added up individually, very, very rarely, it would flash briefly greater
than 100 percent.
I stared at my code until I could barely see straight, but I just couldn't find a problem.
I convinced myself that the problem had to be in the kernel's accounting, but blaming
the kernel is almost akin to claiming "it must be a compiler bug" when you really don't
understand what your own code is doing.
The bar for blaming the kernel guys was very high, and when I did try, they were unmoved.
They were pretty certain it had to be my problem.
In order to try to catch this weird edge case, I placed an assertion in the code.
An assertion is a statement which verifies that a particular condition is always true,
and I added one to continually check and validate on every machine everywhere that the total
was 100 percent or less.
It would also re-check that total once per second.
My assertion would trigger if the total was ever more than 100 percent.
The plan was to run this through the entire gauntlet of nightly NT Stress tests across
the team.
What is an NT Stress test?
Well, each and every night every developer, before they left, would start a test suite
that put their PC to work running the day's latest test build of Windows under enormous
load.
It would call every API and do everything it could think of to destabilize or crash
the system so that we could attempt to catch those cases and debug through them and fix
them.
There were many people whose full-time job was entirely to devise new ways to abuse the
operating system in the hopes of breaking it.
These tests would run all night on every machine we had, all while each was connected to a
debugger.
Any oddity would be investigated by the developer responsible for that particular area.
They could connect to the machine's debugger remotely from anywhere, even from home, and
that gave them the opportunity to inspect the issue live.
Into this mix I added my assertion message that the CPU time was never greater than 100
percent in total.
If it ever was, an alert message would pop up on the user's desktop and if NT Stress
were running, it would stop right there the debugger.
And yet nothing ever happened.
We could never catch it happening during stress, and the few times it did happen live during
interactive daily use were harder to catch.
Since repro cases were so exceedingly rare, I got a little desperate and did something
unconventional: I put my home phone number in the message.
If this ever happened to anyone in the Company testing the product, I wanted to know about
it right away, no matter what.
In the meantime, I moved onto other issues and kind of forgot about the message.
That was unfortunate, because it means I also forgot to remove it, and my home phone number,
before we publicly shipped the Windows Beta far and wide around the world.
It meant that every time the issue manifested itself to anyone anywhere on the planet, my
home phone number would be displayed to the user.
Not smart.
A few weeks later we caught a case in the debugger on a machine at work.
I debugged it far enough to convince myself the problem had to be in the kernel, and I
handed the debugger connection off to them to investigate further.
Sure enough, there was, in fact, a tiny accounting error somewhere in the kernel that happened
very rarely that would push reported CPU time over 100, and Task Manager had just been dutifully
reporting that.
It wasn't my bug, and it was now fixed, both of which made me happy, and I removed my home
phone number from future versions of Windows.
25 years later I still have that same home number, and thankfully, no one has ever called.
When not working on Task Manager I was also deep in the midst of helping my team to port
the entire Windows 95 user interface, or shell, over to Windows XP.
Among other things, that meant making it work entirely with 16-bit Unicode text, and you'd
be surprised how much code breaks when characters are no longer one byte in size.
It really meant hand-reviewing almost every line of code and every algorithm, particularly
anything dealing with any text, which is just about everything.
We also rewrote a lot of the network-related code to be asynchronous since we were TCP/IP,
the modern protocol of then-new World Wide Web.
Let me give you but one example of an issue I ran into: The Windows 95 shell did autocompletion
of network names, a very handy feature.
So, if you typed SCR and there was a server named SCRATCH, it would suggest it automatically.
It did so by basically shouting on the network, known as a network broadcast: "Hello, is there
anyone out there that starts with SCR"?
With a broadcast like that, every machine on the entire network could hear you, so an
answer back or the lack of one could be immediately taken as an authoritative YES or NO.
By contrast, Windows NT was built around TCP/IP, which is asynchronous, meaning each letter
could therefore take up to 90 seconds to time out if no matches were found.
Worse, hitting a server named SCRATCH without proper credentials over and over for each
letter of its name as you typed it could be viewed as an attack of some sort, and you'd
be proactively locked out of that server before you could even finish typing its name.
This kind of thing was all because the Windows 95 code made assumptions that simply were
not true in world of the Internet, international text, and RISC processors.
In its defense, it was never supposed to.
It was thought that Windows NT would have its own unique user interface eventually,
but the Windows 95 design became so well received and dominant in the marketplace that it was
clear they would have to match, and ideally, be feature for feature.
And speaking of features, as if dealing with the Shell and Task Manager wasn't bringing
me enough to do, I also volunteered to write the Windows NT version of Space Cade Pinball.
Our Senior VP wanted to be able to show off how Windows NT could support gaming, and what
better way than to include a game that's fun and visually compelling yet still runs on
pretty much any computer, no matter how old and frail?
It's a rare opportunity to add a new game to Windows and mine was going to make Solitaire
look like Minesweeper.
Given all the original IP from Maxis, I thought it would be pretty smooth sailing, until I
discovered such complications as the sound engine it used being written entirely in fast
x86 assembly language with few comments, which effectively necessitated reverse engineering
it and then rewriting it from scratch in C++ so that it could work on all of our CPUs,
including the RISC platforms.
The nice thing about having worked on Space Cadet Pinball is the street cred it gave me
when volunteering at my kid's school to teach their computer labs.
Those little kids could care less about most anything I'd worked on with that one exception:
they all seemed to know and love Pinball.
And being able to make a little show of walking over and entering a "secret code" that only
I knew that gave them infinite lives?
Priceless.
I retired following Server 2003, but the people that have worked on Task Manager since have
done an exemplary job of growing it while still maintaining the core attributes of reliability,
responsiveness, and crisp painting that I instilled in the original, and I couldn't
be happier with how it looks and works today.
But still, it would be nice to know what files are locked by a particular process, or what
process has locked a particular file.
Which just goes to prove that there's always room for more features!
And the only problem with that, of course, is size.
The original Task Manager was literally under 100K including all code and resources.
It's now a dozen times larger, but such comparisons are unfair because it does so much more than
the original and it's now 64-bit.
It's a delicate balance of load times and memory footprint versus features and functionality.
One reason that it was important to maintain a small size is that Task Manager is a bit
like the Highlander - there can be only one.
Or at least there should only be one instance of it running at any time.
There are a number of different ways that you can launch Task Manager, so managing the
single instance problem really has to be up to the program itself.
Task Manager does this by looking for another instance as soon as it starts.
If it finds one, it sends a private message to the existing instance with a challenge
code.
The running instance must provide the correct response within 10 seconds.
Any failure or unexpected results along the way and Task Manager will launch a new instance
so that in theory you're never stuck without a usable task manager.
Even if one is a zombie, another will be created to take its place.
Your worst case is having multiple Task Mangers running.
Then, if you were bored, I suppose the two of them could fight it out in a Battle Royale
of process termination.
You could even write rules and form teams, and ultimately even leagues, perhaps.
Which gambit will you choose?
Should you end the WOW task first, or try to recursively terminate the shell?
It's your move.
Please be sure to invite me to your first convention.
Speaking of recursive termination, you'll notice we always used nonviolent imagery for
Task Manager right from the beginning.
You didn't kill a process, or even terminate it, you simply ended things peacefully.
That's not true behind the scenes in the code, however.
That could lead to some very unfortunately named functions as time went by.
My original function to end a process was named Kill Process, using the same terminology
as Unix had before me.
Then, a few years later, someone was tasked with adding the ability to make that recursive,
which they did.
And, in updating the name, they replaced the word Process with All Children, giving us
the rather disturbingly named Kill All Children function.
It's all internal, however, and none of this ever made it past the sanitizing action of
the compiler.
Task Manager takes a number of other defensive steps so that it has the best shot at providing
at least the basic functionality that you would normally find on the Processes page.
For example, even though it's not the first page, it's created first.
Then I check memory, and if I've gotten that far but memory is low, as in under eight megabytes,
then you get a severely reduced Task Manager with a much smaller memory footprint, but
one that is still able to do basic process reporting and management.
You should be able to get a Task Manager up and running with what today would be almost
no memory free.
Sometimes I took that ultra-conservative approach a little further than perhaps I need to.
In order to keep the footprint small, I wanted to avoid linking in the compiler runtime libraries,
as I was careful not to use any of the runtime functions in the first place.
Normally, you still need it to do housekeeping such as initializing static C++ objects, but
I opted to implement all that work myself, avoiding the need to pull in the compiler
runtimes entirely.
This kept the binary that much smaller, but it's a bit of an extreme and esoteric approach.
One nice little touch owes its existence to a story I'd heard.
Apparently, the GPS failed on a helicopter near Seattle and so the pilot needed to know
his current location in order to set his bearings on the map.
They came upon a low-rise office complex, and so they hovered outside a window and quickly
made up a sign that said, "WHERE ARE WE?".
The person inside the building furiously wrote up a sign of their own and held it up to the
pilot which read "YOU ARE INSIDE A HELICOPTER".
To the amazement of the passengers, the pilot then flew directly to the airport.
When questioned how he'd suddenly known where to fly, the pilot explained "When I saw an
answer that technically correct but totally unhelpful, I knew immediately we were outside
Microsoft headquarters, and I knew my way from there."
To address that sort of thing, Task Manager does some niceties such as massaging error
codes to make them more useful to the user.
To that end, then, when a system API returned a less than useful code such as "Invalid"
in response to an attempt to terminate or modify a process I would massage that message
into something at least a little more useful, such as "The operation is not permitted on
this process".
In the event it gets an error condition it doesn't recognize, it goes to some lengths
to get the system to parse the error into a meaningful message, with the aim being that
you never get a message like "ERROR: The Operation Completed Successfully".
In addition to the goals of small size and robustness, my most important personal goal
was actually flicker free painting and resizing.
Although the operating system provided all of the tools necessary, before Task Manager
I cannot think of another application with that many list columns and controls that could
be arbitrarily resized live in all directions without flicker or flashing.
It seems a minor thing until you've experienced it both ways.
I went to some great lengths to avoid even a single instance of flicker, and I'll cover
some of the specifics when we look at the actual code that does it.
The process list, for one, is a fairly complex affair that takes full advantage of most everything
the excellent system ListView control has to offer.
For example, if only a single cell of a single row has changed in the complete process view,
only that tiny little rectangle is invalidated and repainted.
In the initial incarnation of Task Manager, I did not want any rubber baby buggy bumpers
to protect the user from themselves.
By that, I mean that I wanted the operating system itself to be the arbiter of what you
were allowed to do, not me in the form of rules inside Task Manager.
If you wanted to terminate the whole Shell or even a process like Winlogon that would
cause the system to then bluescreen immediately - by design - you could do it as long as you
had the appropriate administrative system rights and credentials.
You could also do things like mark a CPU-bound process as real-time priority and effectively
hang the system if you so wished.
That led to some bad press however as a few smarmy computer journalists showed how you
could, as super user given the correct rights, crash a Windows machine with Task Manager
in a single click.
Never mind that you could delete the main hard drive partition as well, so it's kind
of a silly point, but it made for bad PR.
Thus, from that point forward Task Manager has had a few kid gloves to prevent you from
doing anything too foolish to yourself, but I somewhat miss the days when it was a sharper
blade.
And to extend that metaphor, Task Manager has always gone to great lengths to be as
powerful as possible.
For example, under normal circumstances even the administrator cannot terminate a process
in another user's logon session.
Task Manager can do it, however, by first checking to see if you have the DEBUG privilege
on your account.
If you do, you would be able to attach a debugger to that process and then force it to exit,
so Task Manager does the work of enabling that privilege and terminating the process
for you.
There's no point in pretending you couldn't do it, so it just goes ahead and does it for
you.
Another idiosyncrasy of the code is that it contains a number of functions and controls
named Dave.
For example, there's Daves Frame Window Procedure, Init Daves Controls, Daves Frame Class, Daves
Group Class, and so on.
This was not so much a doff of the cap to my own megalomania as a side effect of the
code having started its life as a pet project.
These are all cases where the operating system provided a version of a control, such a group
box, but I super-classed and customized that behavior.
To distinguish my version from the system's original one, I simply added my name, Dave,
as a prefix.
It made sense when I was working alone on it, but I likely should have updated those
names when we brought the code in house.
It was probably a Priority 3 item in Raid, which is what we called our bug tracking database.
What often happens to trivial changes in the real world is that being trivial, people procrastinate
on them until they actually have the spare time to work on trivial stuff, but by then
it's too late in the product cycle to accept even trivial changes that don't fix an important
bug, and naming isn't an important bug.
And so, sometimes trivial but harmless items never get fixed and as a result, the Dave
classes have persisted across the ages.
Most people know how to start Task Manager.
What they may not know is that there are at least ten ways that you can start Task Manager,
just in case!
You can press Control Alt Delete and then pick Task Manger from that menu.
You can press Windows X and pick it from the Power User menu.
You can right-click the task bar and select it from there.
You can launch it from the Start Menu.
You can bring up Start/Run and type taskmgr and that will launch one for you, or you can
enter that into a command window for the same result.
You could even browse to taskmgr.exe in the system32 folder and double-click on it to
run it manually.
You could create a shortcut to Task Manger and launch the shortcut.
My personal favorite, however, is one I added to the logon process, since it's by definition
always running if you're logged in.
Simply press Control Shift Escape which will signal the logon manager to start a fresh
Task Manager for you even if everything else is gone or bunged.
This is also a handy shortcut if you're working in a VM because you don't then have to deal
with the special ctrl-alt-del key sequence being captured by the host.
Trust me, though: Ctrl Shift Escape is the way the kids are all doing it now.
Until they were updated sometime around Windows 8, the graphs in Task Manager were state of
the art.
For 1995, that is.
They've long since been updated after I retired, and they look great again, though I do wish
there were a Dark Mode for Task Manager.
Perhaps there will be soon enough.
One feature that didn't last more than one version, however, was the set of 7-segment
LED style meters that I used for CPU and Memory.
Quaint at the time, perhaps, they were problematic to localize into other fonts or for right-to-left
languages and so on, and so they were replaced with normal text by the time Windows XP was
released.
Task Manager has one final trick up its sleeve for resiliency: if it detects absolutely anything
amiss or weird about the saved state, it will completely reset itself to factory defaults
automatically.
If for some reason it gets into a state where it's broken and doesn't know it, you can force
Task Manager to do a factory reset by starting the program and then, before it has a chance
to open its main window, holding down CTRL, SHIFT, and ALT at the same time.
You have to be pretty fast on today's hardware to get it that quickly, but it might just
be the one trick that saves the day on a corrupted system.
Well, I hope you've enjoyed this little technical retrospective on the Windows Task Manager.
In the third installment we'll take a look at the actual code behind the curtain and
see what secrets the Task Manager source code has in store for us.
Thanks for joining me here today in Dave's Garage!
I'd be flattered if you'd consider subscribing to my channel if you found this episode informative
or entertaining, and if you turn on the bell and personal recommendations, then know you
know that you won't miss future installments.
If you like this kind of nostalgic tale, I'd appreciate if you would then leave me a Like
on the video so I know there's an appetite for this sort of thing!
I'll see you soon in Episode three.
And finally, just to be clear, I retired from Microsoft more than 15 years ago and while
they graciously provided certain permissions that made this video possible, I am not an
official nor a spokesperson for them, and all opinions herein are mine only.
Cheers!