Holy cow, I wrote a book!
The GlobalAlloc function has a GMEM_SHARE flag.
What was it for?
In 16-bit Windows, the GMEM_SHARE flag controlled whether the
memory should outlive the process that allocated it.
By default, all memory allocated by a process was automatically
freed when that process exited.
Passing the GMEM_SHARE flag suppressed this automatic cleanup.
That's why you had to use this flag when allocating memory to be
placed on the clipboard or when you transfer it via OLE to
another process. Since the clipboard exists after your program
exits, any data you put on the clipboard needs to outlive the
program. If you neglected to set this flag, then once your program
exited, the memory that you put on the clipboard would be cleaned up,
resulting in a crash the next time someone tried to read that data from
(The GMEM_SHARE flag also controlled whether the memory could be
freed by a process other than the one that allocated it.
This makes sense given the above semantics.)
Note that the cleanup rule applies to global memory allocated
by DLLs on behalf of a process. Authors of DLLs had to be careful
to keep track of whether any particular memory allocation was
specific to a process (and should be freed when the process exited)
or whether it was something the DLL was planning on sharing across
processes for its own internal bookkeeping (in which case it shouldn't
Failure to be mindful of this distinction led to
bugs like this one.
Thank goodness this is all gone in Win32.
According to the Constitution of the State of New Jersey, Article II, paragraph 6:
No idiot or insane person shall enjoy the right of suffrage.
Why are constitutional articles labelled with Roman numerals? Makes it sound like the Super Bowl or something.
The state appellate court did rule a few years ago that being hospitalized for psychiatric treatment does not constitute being insane for the purpose of determining voter eligibility.
Today is Election Day in the United States. Don't forget to vote! (Void where prohibited.)
Back in the days of 16-bit Windows, the difference was significant.
In 16-bit Windows, memory was accessed through values called
"selectors", each of which could address up to 64K.
There was a default selector called the "data selector";
operations on so-called "near pointers" were performed relative to
the data selector.
For example, if you had a near pointer p
whose value was 0x1234 and your data selector was 0x012F, then when
you wrote *p, you were accessing the memory at
(When you declared a pointer, it was near by default. You had
to say FAR explicitly if you wanted a far pointer.)
Important: Near pointers are always relative to
a selector, usually the data selector.
The GlobalAlloc function allocated a selector that could be used
to access the amount of memory you requested.
(If you asked for more than 64K, then something exciting happened,
which is not important here.)
You could access the memory in that selector with a "far pointer".
A "far pointer" is a selector combined with a near pointer.
(Remember that a near pointer is relative to a selector; when
you combine the near pointer with an appropriate
selector, you get a far pointer.)
Every instance of a program and DLL got its own data selector,
known as the HINSTANCE,
which I described in an earlier entry.
The default data selector for code in a program executable
was the HINSTANCE of that instance of the program;
the default data selector for code in a DLL was the HINSTANCE
of that DLL.
Therefore, if you had a near pointer p and accessed it via
*p from a program executable, it accessed memory relative
to the program instance's HINSTANCE.
If you accessed it from a DLL, you got memory relative to your DLL's
The memory referenced by the default selector
could be turned into a "local heap" by calling the
LocalInit function. Initialing the local heap
was typically one of the first things
a program or DLL did when it started up.
(For DLLs, it was usually the only thing it did!)
Once you have a local heap, you can call LocalAlloc to allocate
memory from it.
The LocalAlloc function returned a near pointer relative to the default
selector, so if you called it from a program executable, it allocated
memory from the executable's HINSTANCE; if you called it from a DLL,
it allocated memory from the DLL's HINSTANCE.
If you were clever, you realized that you could use LocalAlloc to
allocate from memory other than HINSTANCEs. All you had to do
was change your default selector to the selector for some memory
you had allocated via GlobalAlloc, call the LocalAlloc function,
then restore the default selector. This gave you a near pointer
relative to something other than the default selector,
which was a very scary thing to have, but if you were smart and
kept careful track, you could keep yourself out of trouble.
Observe, therefore, that in 16-bit Windows, the LocalAlloc and
GlobalAlloc functions were completely different! LocalAlloc
returned a near pointer, whereas GlobalAlloc returned a selector.
Pointers that you intended to pass between modules had to be in the
form of "far pointers" because each module has a different default
selector. If you wanted to transfer ownership of memory to another
module, you had to use GlobalAlloc since that permitted the recipient
to call GlobalFree to free it. (The recipient can't use LocalFree
since LocalFree operates on the local heap, which would be the
local heap of the recipient - not the same as your local heap.)
This historical difference between local and global memory still
has vestiges in Win32.
If you have a function that was inherited from 16-bit Windows
and it transfers ownership of memory, it will take the form
of an HGLOBAL. The clipboard functions are a classic example
of this. If you put a block of memory onto the clipboard,
it must have been allocated via HGLOBAL because you are transferring
the memory to the clipboard, and the clipboard will call GlobalFree
when it no longer needs the memory.
Memory transferred via STGMEDIUM takes the form of HGLOBALs
for the same reason.
Even in Win32, you have to be careful not to confuse the local
heap from the global heap. Memory allocated from one cannot be
freed on the other. The functional differences have largely
disappeared; the semantics are pretty much identical by this
point. All the weirdness about near and far pointers disappeared
with the transition to Win32.
But the local heap functions and the global heap functions
are nevertheless two distinct heap interfaces.
I'm going to spend the next few entries describing some of the
features of the 16-bit memory manager. Even though you don't
need to know them, having some background may help you understand
the reason behind the quirks of the Win32 memory manager.
We saw a little of that today, where the mindset of the 16-bit
memory manager established the rules for the clipboard.
[Raymond is currently on vacation; this message was pre-recorded.]
The taskbar created all sorts of interesting problems, since the
work area was not equal to the entire screen dimensions.
(Multiple monitors created similar problems.)
"Why didn't the gui return the usable workspace
as the root window (excluding the taskbar)?"
That would have made things even worse.
Lots of programs want to cover the entire screen.
Games, for example, are very keen on covering the entire screen.
Slideshow programs also want to cover the entire screen.
(This includes both slideshows for digital pictures as well as
Screen savers of course must cover the entire screen.
If the desktop window didn't include the taskbar, then those
programs would leave a taskbar visible while they did their thing.
This is particularly dangerous for screen savers, since a user
could just click on the taskbar to switch to another program
without going through the screen saver's password lock!
And if the taskbar were docked at the top or left edge of the screen,
this would have resulted in the desktop window not beginning at
coordinates (0,0), which would no doubt have caused widespread havoc.
(Alternatively, one could have changed
the coordinate system so that (0, 0) was no longer the
top left corner of the screen, but that would have broken so many
programs it wouldn't have been funny.)
Before Explorer was introduced in Windows 95,
the Windows desktop was a very different place.
The icons on your desktop did not represent files;
rather, when you minimized a program, it turned into
an icon on the desktop.
To open a minimized program, you had to hunt for its icon,
possibly minimizing other programs to get them out of the way,
and then double-click it.
(You could also Alt+Tab to the program.)
Explorer changed the desktop model so that icons on your desktop
represent objects (files, folders) rather than programs.
The job of managing programs fell to the new taskbar.
But where did the windows go when you minimized them?
Under the old model, when a window was minimized, it displayed
as an icon, the icon had a particular position on the screen,
and the program drew the icon in response to paint messages.
(Of course, most programs deferred to DefWindowProc
which just drew the icon.)
In other words, the window never went away; it just changed its
But with the taskbar, the window really does go away when you
minimize it. Its only presence is in the taskbar.
The subject of how to handle windows when they were minimized
went through several iterations, because it seemed that no matter
what we did, some program somewhere didn't like it.
The first try was very simple: When a window was minimized,
the Windows 95 window manager set it to hidden.
That didn't play well with many applications,
which cared about the distinction between minimized (and visible)
and hidden (and not visible).
Next, the Windows 95 window manager
minimized the window just like the old days,
but put the minimized window at coordinates (-32000,
This didn't work because some programs freaked out if they found their
coordinates were negative.
So the Windows 95 window manager tried putting minimized windows at
coordinates (32000, 32000),
This still didn't work because some programs freaked out if they found
their coordinates were positive and too large!
Finally the Windows 95
window manager tried coordinates (3000, 3000),
This seemed to keep everybody happy.
Not negative, not too large, but large enough that it wouldn't show
up on the screen (at least not at screen resolutions that were
readily available in 1995).
If you have a triple-monitor Windows 98 machine lying around,
you can try this:
Set the resolution of each monitor to
1024x768 and place them corner-to-corner. At the bottom right
corner of the third monitor, you will see
all your minimized windows parked out in the boonies.
(Windows NT stuck with the -32000 coordinates and didn't
pick up the compatibility fixes for some reason.
I guess they figured that by the time Windows NT became
popular, all those broken programs would have been fixed.
In other words: Let Windows 95 do your dirty work!)
I had previously written on
how you can retrieve the text under the cursor,
and you may have noticed that it produces mixed results.
It works great with some programs but not with others.
It depends on the program in question.
Some programs were written with greater attention to
supporting screen readers than others.
Internet Explorer, for example, has excellent support
for ActiveAccessibility because browsing the web
is a great way for people with disabilities to get involved
in the world around them.
Other programs don't do quite as good a job.
the program we developed to demonstrate
various scrollbar techniques
does not handle
the WM_GETOBJECT message
and is not accessible.
So whether ActiveAccessibility works for any particular
program depends heavily on how much the author of that program
had accessibility in mind when they wrote it.
A colleague of mine nominated this code for Function of the Year.
(This is the same person who was the
first to report that a Windows beta used a suspect URL.)
I have to admit that this code is pretty impressive. Of all the ways
to check the operating system, you have to agree that sniffing
at an undocumented implementation detail of memory-mapped files
is certainly creative!
// following the typographical convention that code
// in italics is wrong
HANDLE hFile, hFileMapping;
BYTE *pbFile, *pbFile2;
hFile = CreateFile(szFile, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE,
0, 0, NULL);
pbFile = (PBYTE) MapViewOfFile(hFileMapping, FILE_MAP_WRITE,
0, 0, 0);
pbFile2 = (PBYTE) MapViewOfFile(hFileMapping, FILE_MAP_WRITE,
0, 65536, 0);
if (pbFile + 65536 != pbFile2)
Nevermind that the function also leaves a file locked and leaks
two handles and two views each time you call it!
What's more, this function may erroneously report FALSE
on a Windows NT machine if by an amazing coincidence the
memory manager happens to assign the second file view to the very
next 64K block of memory (which it is permitted to do since
address space granularity is 64K).
It can also erroneously report TRUE
on a Windows 95 machine if the MAIN.CPL file happens to be
smaller than 64K, or if you don't have write permission on the file.
(Notice that the program requests read-write access
to the MAIN.CPL file.)
This particular function is from a library
that is used by many popular multimedia titles.
The quickest way to detect whether you are running on a
Windows 95-series system or a
Windows NT-series system
is to use the hopefully-obviously-named function
return (GetVersion() & 0x80000000) == 0;
If you're writing a static library, you may have need to access
the HINSTANCE of the module that you have been
You could require that the module that links you in pass the
HINSTANCE to a special initialization function,
but odds are that people will forget to do this.
If you are using a Microsoft linker, you can take advantage of
a pseudovariable which the linker provides.
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
The pseudovariable __ImageBase
represents the DOS header of the module, which happens to
be what a Win32 module begins with.
In other words, it's the base address of the module.
And the module base address is the same as its HINSTANCE.
So there's your HINSTANCE.
If the Euro 2004 tournament were a video game, it might have looked like this. View recreations of the tournament highlights, and since the graphics are computer-generated, you can select among multiple camera angles, embody a specific player, or see what it's like to be the ball itself!
[Raymond is currently on vacation; this message was pre-recorded.]
Windows 95 Setup would notice that a file it was installing
was older than the file already on the machine and would ask you
whether you wanted to keep the existing (newer) file or to overwrite
it with the older version.
Asking the user this question at all turned out to have been a bad idea.
It's one of those
dialogs that ask the user a question they have no idea how to answer.
Say you're installing Windows 95 and you get the file version
conflict dialog box.
"The file Windows is attempting to install is older than the one
already on the system. Do you want to keep the newer file?"
What do you do?
Well, if you're like most people, you say,
"Um, I guess I'll keep the newer one,"
so you click Yes.
And then a few seconds later, you get the same prompt
for some other file. And you say Yes again.
And then a few seconds later, you get the same prompt
for yet another file. Now you're getting nervous.
Why is the system asking you all these questions?
Is it second-guessing your previous answers?
Often when this happens, it's because you're doing something bad
and the computer is giving you one more chance to change
your mind before something horrible happens.
Like in the movies when you have to type Yes five times
before it will launch the nuclear weapons.
Maybe this is one of those times.
Now you start saying No. Besides, it's always safer
to say No, isn't it?
After a few more dialogs (answering No this time),
Setup finally completes. The system reboots,
and... it bluescreens.
Because those five files were part of a matched set of files
that together form your video driver.
By saying Yes to some of them and No to others, you ended up
with a mishmash of files that don't work together.
We learned our lesson. Setup doesn't ask this question any more.
It always overwrites the files with the ones that come with the
operating system. Sure, you may lose functionality, but at least
you will be able to boot. Afterwards, you can go to Windows Update
and update that driver to the latest version.
Note, however, that this rule
does not apply to hotfixes and Service Packs.