Holy cow, I wrote a book!
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."
Reaching back into the history bucket...
Some people have discovered
that strange things happen if you name your DLL "security.dll".
The reason is that there is already a system DLL called "security.dll";
Security Support Provider Interface DLL,
and it used to go by the name "security.dll", though nowadays
the name "secur32.dll" is preferred.
If you look into your system32 directory, you'll see both
"security.dll" and "secur32.dll" in there.
And if you're handy with an export dumper, you'll see that "security.dll"
is just a bunch of forwarders to "secur32.dll".
If you browse through the MSDN documentation, you'll see that everybody
talks about "secur32.dll" and hardly any mention is made of its
Okay, here's where the history comes in.
Wind back to Windows 95.
Back in those days, the
Security Support Provider Interface was implemented in two different DLLs.
The one you wanted depended on whether you are running
Windows NT or Windows 95.
On Windows 95, it was called "secur32.dll", but on Windows NT,
it was called "security.dll".
This was obviously a messed-up state of affairs, so the Windows NT
folks decided to "go with the flow" and rename their security DLL to
"secur32.dll". This was probably for application compatibility reasons:
Applications that were written to run on Windows 95 and were never
tested on Windows NT just went straight for "secur32.dll" instead of
loading the correct DLL based on the operating system.
Okay, so now pop back to the present day.
When you put a DLL called "Security.dll" in your application directory,
the rules for the order in which DLLs are searched for checks
the application directory before it checks the system directory.
As a result, anybody in your application
who wants "Security.dll" will get your version
instead of the system version.
Even if the system version is the one they really wanted.
That's why overriding the system's Security.dll with your own
results in a bunch of SSPI errors. Components you are using in your
program are trying to talk to SPPI by loading "security.dll" and
instead of getting the system one, they get yours.
But yours was never meant to be a replacement for "security.dll";
it's just some random DLL that happens to have the same name.
You would have had the same problem if you happened to name your DLL
something like "DDRAW.DLL" and some component in your program tried
to create a DirectDraw surface.
"Security.dll" has the disadvantage that it has a simple name
(which people are likely to want to name their own DLL), and its
importance to proper system functionality is not well-known.
(Whereas it would be more obvious that creating a DLL called
"kernel32.dll" and putting it in your application directory is going
to cause nothing but trouble.)
The air must be thinner the higher up the management chain you go,
or maybe it just gives you more opportunities to look stupid.
Like this message:
From: <some upper manager>
Subject: <some subject>
I will try to keep this relatively brief as I know how busy everyone is.
<... 4-page message follows...>
I will try to keep this relatively brief as I know how busy everyone is.
<... 4-page message follows...>
If this is brief, I'd hate to see a "somewhat lengthy" message.
It's the continuing balance between ease-of-use and generality.
At a literal level, you can't use the same tree items in multiple
places in the tree, because then various properties would become
ambiguous, properties like TVGN_PARENT or TVIS_EXPANDED.
(If a tree could be in two places, then it would have two parents,
Of course, this problem could have been solved by separating the
item content from the item presence. So instead of just having
an HTREEITEM, there would be, say, HTREENODE and HTREENODECONTENTS.
The node would represent a physical location in the tree,
and the item contents would represent the contents of that node:
its name, icon, etc.
Sure, that could have been done, but remember the balance.
You're making the common case hard in order to benefit the rare case.
Now everybody who is manipulating treeviews has to worry about
twice as many objects (what used to be one item is now a node plus contents).
This is generally not the balance you want to strike when
designing an interface.
When you design an interface, you want to
make the common case easier than the rare case.
A program that wants this separation can, of course, do the
separation manually. Put all the
contents in a separate shareable structure and have your HTREEITEMs
refer to that shared structure in their lParams.
This is more work for the program, but now the cost is being
shouldered by the one who wants the extra functionality.
Some people wondered about
my fascination with Germanic languages
and asked why I didn't branch out
to other language families.
It's basically laziness.
I grew up speaking English (and a little of the Holo dialect,
most of which has by now vanished from disuse), then studied German
in high school and college, and most recently added Swedish in preparation
for a trip there.
Swedish was easy to pick up because it nestles nicely
between German and English. And that's when I realized that laziness
was the key: If you always pick a language close to the ones you
already know, it will not be so hard to learn.
So my list of languages follows a chain of closely-related languages,
so each one can be used as leverage for the next. Except for Icelandic,
which strikes me as "like German, before the Germans decided to simplify
their grammar" - that has its own appeal. I saved it for last.
I have colleagues who speak Dutch and Afrikaans,
so learning those languages would allow me to confuse and
annoy them. Because that's the main reason for learning a
language: To confuse and annoy.
(One of my South African colleagues describes Afrikaans as
"the language you get when you throw a bunch of Dutchmen
into the bush and have them chased by lions for a few hundred
I have a former colleague who has since
returned to Denmark. We always teased him about his native
country and language when he was around,
and he was a good sport about it. He's the one who taught
me the phrase
gang til for prins Knud".
I removed Danish from the list of Germanic languages
partly to tease him from afar and partly because the
strange Danish pronunciation scares me.
But for now, my pan-Germanic ambitions are on hold.
As the Swedes out there
I've started studying Mandarin Chinese
Even though I grew up with a tonal language (Holo has seven
tones, as opposed to just the four of Mandarin),
I never got very good at pronouncing the tones,
even though I can hear the difference easily in most cases.
So I'm in the embarrassing position of speaking badly and recognizing it
Update: With some help from my father, I think I figured out
the Mandarin third tone, which was the only one I had been
having trouble with. The trick: The way the books explain how
the third tone works does not match the way people
pronounce it in real life.
But the way the books explain it is so deeply ingrained in
the way people think about the pronunciation of the tone
that they continue to insist that's
how it's done even though it isn't.
Those of you who dislike the Links folder have probably tried
to delete it, only to discover that it keeps coming back.
Why is that?
This is Internet Explorer trying to do some auto-repair.
It noticed that the Links folder is missing, so it figures,
"Gosh, it must be corrupted! I'd better fix the problem
by creating a replacement."
People complain that computers can't perform self-repair,
and then when the software tries to perform self-repair, they get mad.
"But I wanted it to stay broken."
You can't win.
The way to indicate, "Yes, I know about the Links folder,
but I don't want to use it" is
to hide it.
This is extraordinarily similar to a problem some people have
with Device Manager. They don't want Windows to use a particular
device, so they delete it. And then it gets re-detected and added back.
Because when you delete the device, you're saying, "Forget everything
you know about this device." Then when it gets re-detected, Windows
says, "Gosh, here's a device I've never seen before! The user must have
bought it recently. Let me add it to the device tree so the user can
In other words, Windows behaves the way it does because the alternative
is even worse: You buy a device, plug it in, and nothing happens.
If you have a device that you don't want Windows to use,
go into the Device Manager and Disable it rather than deleting it.
This means, "Yes, I know about this device, but I don't want to use it."