Holy cow, I wrote a book!
the company authorized to make
Swiss Army merchandise,
has run into a bit of a problem:
Is there anything else
you could possibly think of putting on a pocketknife
for an outdoorsman? And even if you did, there's
no room for it. The pocketknife has already reached
the point where it's too unwieldy to carry around in
your pocket any more.
So they decided to think outside the box.
First, they let go of the "must fit in your pocket" concept.
Victorinox Swiss Champ XXLT.
Check out the picture of the knife in its display box.
The knife is as thick as it is long!
Second, they let go of the whole
"things for the outdoorsman" concept.
Now you can get the
Swiss Army Knife USB Drive
Swiss Army Knife CyberTool.
Swiss Army Knife. For the geek in your life. Embrace it.
If the program doesn't provide this information itself, Add/Remove Programs is forced to guess.
The problem is that there is no "obvious" way to map an entry in the Add/Remove Programs list to an actual program. Each entry in the list, for those who care about such things, comes from the HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall registry key. The only mandatory properties for an uninstallable program are the DisplayName and the UninstallPath. Everything else is optional.
Let's suppose Add/Remove Programs is given a program registration like this:
DisplayName=REG_SZ:"Awesome Program for Windows"
In order to get the "Last Used" and "Frequency" values, Add/Remove Programs needs to know the name of the EXE so it can ask the Start menu "Hey, how often did the user run this program, and when was the last time it happened?"
Notice that there are no clues in the registration above as to the identity of this EXE file.
So Add/Remove Programs starts guessing. It goes through all the programs on your Start menu and compares their names with the display name of the uninstallable item. It looks for Start menu items which share at least two words with the words in the DisplayName.
For example, if there were a Start menu item called "Pretty Decent Windows Program", this would count as a two-word match ("Windows" and "Program").
It then takes the one with the most matches and decides, "Okay, I guess this is it." Suppose for the sake of illustration that the best match is indeed "Pretty Decent Windows Program.lnk", which is a shortcut to "C:\Program Files\LitWare\Decent Program\Decent.exe". Add/Remove Programs would decide that "Awesome Program for Windows" should get the icon for "Pretty Decent Windows Program.lnk", that the frequency of use and most-recently-used information for "C:\Program Files\LitWare\Decent Program\Decent.exe" will be displayed for "Awesome Program for Windows".
But wait, there's more. There's also the program size. Add/Remove Programs looks in your "Program Files" directory for directories whose names share at least two words in common with the DisplayName. The best match is assumed to be the directory that the program files are installed into. The sizes are added together and reported as the size of "Awesome Program for Windows".
A program can add some properties to its registration to avoid a lot of this guessing. It can set an EstimatedSize property to avoid making Add/Remove Programs guess how big the program is. It can also set a DisplayIcon property to specify which icon to show for the program in the list.
But if a program omits all of these hints, the guess that Add/Remove Programs ends up making can often be ridiculously wide of the mark due to coincidental word matches. In my experience, Spanish suffers particularly badly from this algorithm, due to that language's heavy use of prepositions and articles (which result in a lot of false matches).
Yes, this is all lame, but when you are forced to operate with inadequate information, lame is the best you can do.
[July 15 2004: Emphasizing that this is done only if the program fails to provide the information itself. For some reason, a lot of people who link to this page fail to notice that little detail.]
A commenter asks why icon label have "those ugly color boxes"
when there is a background image.
The answer: Because the alternative would be worse.
Imagine if there were no solid background between the text and the
background image. You would end up with text against an
unpredictable background, which doesn't help readability.
Take the default background for Windows XP:
There are some places that are very light and other places that
are very dark. No matter what color you pick for the icon text,
it will look bad in one or the other place. If you decide to use
white, then the text becomes unreadable in the clouds.
If you decide to use black, then the text becomes unreadable in the
You lose either way.
The solution is to intercede a contrasting color to ensure
that the text is readable.
If your video card is powerful enough,
the contrasting color is used
only just around the strokes of text themselves, which lends
If shadows are not enabled, then a solid block of contrast is used.
(And for those of you who say, "Use white in the dark places and
black in the light places," what if there is a section of the wallpaper
that has a dark area
right next to a light area, and the text covers both?)
The Philip Morris Companies
renamed itself to the warm-and-fuzzy sounding
Gotta love the colorful abstract logo they've got.
(Psst, editors of the Altria home page:
It's "Whom We Fund". "Whom" with an "m".)
They claim the name comes from the Latin altus ("high")
but that doesn't explain where the "r" comes from.
Any similarity to "altruism" is purely unintentional, I am certain.
Would a tobacco company lie to me?
As Business 2.0 put it,
"[The renamed company] does not, however, stop producing tobacco, which
does not stop causing cancer."
Because Altria Healthcare's job is to help people improve
Anyway, I was reminded of their 1995 recall of
8 billion cigarettes out of concern that their customers may
because the cigarettes
allegedly contained the chemical methyl isothiocyanate (MITC).
Centers for Disease Control and Prevention investigated this problem
and concluded that while it was true that the recalled cigarettes
so too did cigarettes manufactured both before and after the recall,
as well as cigarettes by other manufacturers.
In other words, there was nothing wrong with those cigarettes.
Well, aside from the fact that they are cigarettes.
Michael Eriksen, chairman of the
CDC's Office on Smoking and Health noted that
the real problem is
"lighting organic material and sucking it into your lungs".
Somehow this way of describing smoking struck me as elegantly dry.
If you have a minimized window and want to know where it will
go when you restore it,
the GetWindowPlacement function will tell you.
In particular, the rcNormalPosition tells you where
the window would go if it were restored (as opposed to minimized or
One perhaps-non-obvious flag is WPF_RESTORETOMAXIMIZED.
This flag indicates that the window is currently minimized, but if
the user selects "Restore", it will restore to its maximized instead
of restored state.
(This may seem strange, but you see it every day without
even realizing. Take a window and maximize it. Now minimize it.
Now click the taskbar button to re-open the window. Notice that
it returns to its maximized state, not to its restored state.
Imagine how frustrated you would be if it returned to its restored
state instead. You'd have to keep re-maximizing the window.)
The GetWindowPlacement and SetWindowPlacement functions are
typically used by programs that wish to save/restore window
positioning information across runs.
If you are attending a presentation, you can tell whether
the person at the lectern is a manager or a programmer
by looking at their PowerPoint presentation.
If it's black-and-white, all-text, multimedia-free,
and rarely has more than ten
bullet points on a page, then the presenter is probably
If it's colorful, with graphics, animation,
and pages crammed with information bordering on illegibility,
then the presenter is probably a manager.
It's fun watching a manager try to rewind their presentation to
a particular page. As you step over pages, you still have to
sit through the animations, which means that instead of
"hit space five times" to go forward five pages, you have to
"hit space fifteen times, waiting three seconds between each press
of the spacebar" because each page has three animations
which you must sit through and experience again.
A commenter asks for help with an unresolved external.
One of my goals is to give you more insight into
how things work so you can solve problems yourself.
This particular problem - resolving the error
"Undefined symbol: '__stdcall(0) pl_pvcam_init (_pl_pvcam_init@0)'
referenced from '_main' in Acquisition.c:15" is one example of
something you can solve with the tips you've already learned.
First, let's look at the unresolved external itself.
From the article this comment was posted to,
you can see that the leading underscore and trailing @0
indicate that the function uses the __stdcall calling convention.
(This is confirmed by the linker's undeciration of the name.)
So your function "_main" wants the function pl_pvcam_init
with the __stdcall calling convention.
But it's not found in the library even though you linked to it.
If you look inside the library itself, you'll find the desired
symbols with some decoration. Decode that decoration.
(My psychic powers tell me that
when you do, you'll find that the decoration is
"_pl_pvcam_init", which is the __cdecl calling convention.)
So now you see the problem.
Your code is calling with the __stdcall calling convention,
but the function actually uses the __cdecl calling convention.
The calling conventions don't match up, so the linkage fails.
The solution, of course, is to fix the declaration of
the pl_pvcam_init function in the header file to specify
the correct calling convention. My psychic powers tell me
that the header file doesn't specify any calling convention at all,
which puts it at the mercy of the ambient calling convention for
your project, which appears to be __stdcall.
But the author of the header file expected __cdecl to be the
default calling convention.
Put explicit calling conventions on the functions and you should
be all set.
If you find yourself in a meeting with a mix of managers
and programmers, here's one way to tell the difference
Look at what they brough to the meeting.
Did they bring a laptop computer?
Score bonus points if the laptop computer is actually
during the meeting or if the laptop is special
in some way (e.g., it has a wireless card or it's a Tablet PC).
If so, then that person is probably a manager.
Did they come to the meeting empty-handed or with a spiral-bound
If so, then that person is probably a programmer.
It's not an infallible test, but it works with surprisingly
in the Seattle area who missed it last year,
Outdoor Cinema is screening
on Friday, July 9th.
Go see it. You won't be disappointed.
Assuming what you were expecting to see was Elvis fighting a mummy.
With the help of JFK. In a wheelchair.
If you were expecting something else, then okay maybe you'll be
disappointed after all.
Let's get the easy ones out of the way.
First, CoTaskMemAlloc is exactly the same as CoGetMalloc(MEMCTX_TASK) + IMalloc::Alloc, and CoTaskMemFree is the same as CoGetMalloc(MEMCTX_TASK) + IMalloc::Free. CoTaskMemAlloc and CoTaskMemFree (and the less-used CoTaskMemRealloc) are just convenience functions that save you the trouble of having to mess with CoGetMalloc yourself. Consequently, you can safely allocate memory via CoGetMalloc(MEMCTX_TASK) + IMalloc::Alloc, and then free it with CoTaskMemFree, and vice versa. It's all the same allocator.
Similarly, SHAlloc and SHFree are just wrappers around SHGetMalloc which allocate/free the memory via the shell task allocator. Memory you allocated via SHGetMalloc + IMalloc::Alloc can be freed with SHFree.
So far, we have this diagram.
Now what about those question marks?
If you read the comments in shlobj.h, you may get a bit of a hint:
// Task allocator API
// All the shell extensions MUST use the task allocator (see OLE 2.0
// programming guild for its definition) when they allocate or free
// memory objects (mostly ITEMIDLIST) that are returned across any
// shell interfaces. There are two ways to access the task allocator
// from a shell extension depending on whether or not it is linked with
// OLE32.DLL or not (purely for efficiency).
// (1) A shell extension which calls any OLE API (i.e., linked with
// OLE32.DLL) should call OLE's task allocator (by retrieving
// the task allocator by calling CoGetMalloc API).
// (2) A shell extension which does not call any OLE API (i.e., not linked
// with OLE32.DLL) should call the shell task allocator API (defined
// below), so that the shell can quickly loads it when OLE32.DLL is not
// loaded by any application at that point.
// In next version of Windowso release, SHGetMalloc will be replaced by
// the following macro.
// #define SHGetMalloc(ppmem) CoGetMalloc(MEMCTX_TASK, ppmem)
(Yes, those typos "guild" and "Windowso" have been there since 1995.)
This discussion strongly hints at what's going on.
When Windows 95 was being developed, computers typically had just 4MB of memory. (The cool people got 8MB.) But Explorer was also heavily reliant upon COM for its shell extension architecture, and loading OLE32.DLL into memory was a significant kick in the teeth. Under such tight memory conditions, even the loss of 4K of memory was noticeable.
The solution: Play "OLE Chicken".
The shell, it turns out, didn't use very much of COM: The only objects it supported were in-process apartment-threaded objects with no marshalling. So the shell team wrote a "mini-COM" that supported only those operations and use it instead of the real thing. (It helped that one of the high-ranking members of the shell team was a COM super-expert.) The shell had its own miniature task allocator, its own miniature binder, its own miniature drag-drop loop, everything it needed provided you didn't run any other programs that used OLE32.
Once some other program that used OLE32 started running, you had a problem: There were now two separate versions of OLE in the system: the real thing and the fake version inside the shell. Unless something was done, you wouldn't be able to interoperate between real-COM and fake-shell-COM. For example, you wouldn't be able to drag/drop data between Explorer (using fake-shell-COM) and a window that was using real-COM.
The solution: With the help of other parts of the system, the shell detected that "COM is now in the building" once anybody loaded OLE32.DLL, and it and transferred all the information it had been managing on its own into the world of real COM. Once it did this, all the shell pseudo-COM functions switched to real-COM as well. For example, once OLE32.DLL got loaded, calls to the shell's fake-task-allocator just went to the real task allocator.
But what is "OLE Chicken"? This is another variation of the various "chicken"-type games, perhaps the most famous of which is Schedule Chicken. In "OLE Chicken", each program would avoid loading OLE32.DLL as long as possible, so that it wouldn't be the one blamed for the long pause as OLE32.DLL got itself off the ground and ready for action. (Remember, we're talking 1995-era machines where allocating 32K would bring the wrath of the performance team upon your head.) [Typo fixed, 12:04pm]
Okay, so let's look at that comment block again.
The opening paragraph mentions the possibility that a shell extension does not itself link with OLE32.DLL. Option (1) discusses a shell extension that does use OLE32, in which case it should use the official OLE functions like CoGetMalloc. But Option (2) discusses a shell extension that does not use OLE32. Those shell extensions are directed to use the shell's fake-COM functions like SHGetMalloc, instead of the real-COM functions, so that no new dependency on OLE32 is created. Therefore, if OLE32 is not yet loaded, loading these shell extensions will also not cause OLE32 to be loaded, thereby saving the cost of loading and initializing OLE32.DLL.
So the completion of our diagram for 1995-era programs would be something like this:
Before OLE32.DLL is loaded:
After OLE32.DLL is loaded:
The final "Note" hints at the direction the shell intended to go. Eventually, loading OLE32.DLL would not be as painful as it was in Windows 95, and the shell can abandon its fake-COM and just use the real thing. At this point, asking for the shell task allocator would become the same as asking for the COM task allocator.
That time actually arrived a long time ago. The days of 4MB machines are now the stuff of legend. The shell has ditched its fake-COM and now just uses real-COM everywhere.
Therefore, the diagram today is the one with the equals-sign. All four functions are interchangeable in Windows XP and beyond.
What if you want to run on older systems? Well, it is always acceptable to use CoTaskMemAlloc/CoTaskMemFree. Why? You can puzzle this out logically. Since those functions are exported from OLE32.DLL, the fact that you are using them means that OLE32.DLL is loaded, at which point the "After" diagram above with the equals sign kicks in, and everything is all one big happy family.
The case where you need to be careful is if your DLL does not link to OLE32.DLL. In that case, you don't know whether you are in the "Before" or "After" case, and you have to play it safe and use the shell task allocator for the things that are documented as using the shell task allocator.
I hope this discussion also provides the historical background of the function SHLoadOLE, which today doesn't do anything because OLE is already always loaded. But in the old days, this signalled to the shell, "Okay, now is the time to brain-dump your fake-COM into the real-COM."