A cupholder for your computer

Michael Geary | Mon, 2004-10-11 15:06

I’ve never bought anything from Cyberguys, but the catalogs they keep sending me are so entertaining I’ll have to.

My favorite item in their new catalog is this combination cigarette lighter and cupholder that fits in a 5.25 inch drive bay:

A cigarette lighter and cupholder for your PC

Take the soda can out before you eject that CD!

Smoke getting on your display? No problem, use one of these stuffed animals with microfiber tummies:

Kangaroo screen cleaner

For your car, here’s an MP3 player with a USB socket on one end and a cigarette lighter plug on the other:

An MP3 player for your car's cigarette lighter outlet

Plug a USB flash drive with your music into the MP3 player, the player into your car’s cigarette lighter outlet, and you’re on the air (literally—it broadcasts to your car’s FM radio).

That USB drive in the picture comes from this Swiss Army knife:

USB Swiss Army knife

And here’s the niftiest little flashlight I’ve seen:

9 volt snap-on flashlight

Cyberguys has a bunch of more practical geek stuff too, but I get a kick out of these oddball items they manage to find.

(Updated 1/17/2005 to fix broken links)

Exploding capacitors and Radio Moscow

Michael Geary | Sat, 2004-10-09 01:14

Robert Scoble reminisces about listening to BBC on the shortwave, and plugging capacitors into the AC outlet to make them explode.

My capacitor trick was to take a big fat electrolytic and charge it up to 12 volts or so. Then I’d find an audience, and I would grab one terminal of the capacitor in each hand and pretend I was being electrocuted. After a great struggle I’d break free, then take a screwdriver and short out the terminals. Bam! Now that’s a spark!

Of course, this depended on my friends not knowing the difference between voltage (12 volts, no way it can hurt you) and current (virtually none through the high resistance of my skin, but plenty through the near-zero resistance of the screwdriver).

I discovered shortwave radio when I was about ten, traveling through Idaho with my mom and sister. I found a funny old radio in the motel room and tuned around, finding all kinds of radio stations I’d never heard of before.

“Mom! It’s Radio Moscow!”

“Sure, Mike, we’re in Idaho. That’s Moscow, Idaho.”

It really was Radio Moscow (USSR), but I don’t think she ever believed it.

Use FILE_SHARE_DELETE in your shell extension

Michael Geary | Wed, 2004-09-29 18:24

A Windows shell extension that provides information from the contents of a file has to open the file to do it. Opening a file locks it to some extent or another, depending on the file sharing flags you use. Even if you open the file for only a moment, that can be long enough to interfere with another program's use of the file.

What happens when someone drags a file from one folder to another, and you have a shell extension that renders thumbnails for the selected file type? Windows calls your IExtractImage interface and you start rendering the thumbnail. Then as soon as your customer releases the mouse, Windows tries to move the file to the new folder. If they move fast enough, this can happen while you've still got the file open to render the thumbnail. That results in this lovely message:

Error Moving File

If they're lucky, they'll try again and go a little slower, and it will work! You've finished rendering the thumbnail, closed the file, and Windows can move it with no problem.

There's an easy way to fix this for Windows NT, 2000, and XP. In the CreateFile() call that opens the file, use FILE_SHARE_READ | FILE_SHARE_DELETE in the dwShareMode parameter.

The MSDN documentation doesn't make it clear at all, but FILE_SHARE_DELETE works with MoveFile() in the same way it does with DeleteFile(). In other words, it gives you Unix-style delete/rename semantics. Even while you have the file open, Windows can delete it or rename it right out from under you, but you can keep reading it—your handle to the file remains valid until you close it.

So, in the case above, Windows moves the file to the destination folder without interference from your thumbnail code.

Mike Mascari ran a test of this and posted the results in the comp.databases.postgresql.hackers newsgroup:

Well, here's the test:

foo.txt contains "This is FOO!"
bar.txt contains "This is BAR!"

Process 1 opens foo.txt
Process 2 opens foo.txt
Process 1 sleeps 7.5 seconds
Process 2 sleeps 15 seconds
Process 1 uses MoveFile() to rename "foo.txt" to "foo2.txt"
Process 1 uses MoveFile() to rename "bar.txt" to "foo.txt"
Process 1 uses DeleteFile() to remove "foo2.txt"
Process 2 awakens and displays "This is FOO!"

On the filesystem, we then have:

foo.txt containing "This is BAR!"

The good news is that this works fine under NT 4 using just MoveFile(). The bad news is that it requires the files be opened using CreateFile() with the FILE_SHARE_DELETE flag set. The C library which ships with Visual C++ 6 ultimately calls CreateFile() via fopen() but with no opportunity through the standard C library routines to use the FILE_SHARE_DELETE flag. And the FILE_SHARE_DELETE flag cannot be used under Windows 95/98 (Bad Parameter). Which means, on those platforms, there still doesn't appear to be a solution. Under NT/XP/2K, AllocateFile() will have to modified to call CreateFile() instead of fopen(). I'm not sure about ME, but I suspect it behaves similarly to 95/98.

Even two years after Mike's post, the C runtime hasn't got much better. The _fsopen() and _sopen() functions claim to support file sharing, but neither one supports FILE_SHARE_DELETE. For a shell extension, that may not matter; you may just use the Win32 file I/O functions directly. If you want the buffering that the stream I/O functions provide, you can use CreateFile() to open the file with FILE_SHARE_DELETE, then _open_osfhandle() to get a C runtime file descriptor, and _fdopen() to open a stream from that.

Sorry, FILE_SHARE_DELETE doesn't work on 95, 98, or Me; you have to leave the flag off. I'm not sure how you fix this problem for those OSes.

Internet Explorer below 50%

Michael Geary | Tue, 2004-09-21 12:11

I just saw something amazing in my site stats: Of the browsers that AWStats recognizes (which is most of them), Internet Explorer accounts for fewer than 50% of the visits to mg.to in September.It’s been years since I’ve seen this happen on any site.

This doesn’t include RSS feeds, just browser visits.

Last month, the top browsers were:

70.0%  Internet Explorer
13.1%  Firefox
 5.7%  Mozilla
 4.4%  Opera
 3.3%  Safari

This month to date, they are:

49.7%  Internet Explorer
16.4%  Firefox
12.4%  Opera
10.9%  Mozilla
 4.5%  Safari

Of course, time will tell if this is a real trend or just the normal fluctuation of a new site. But still, it was cool to see.

Go team! :-)

The great home computer hoax

Michael Geary | Mon, 2004-09-20 04:44

Rich Manalang posted a hilarious picture yesterday of the home computer of 2004, as envisioned by RAND Corporation scientists 50 years ago.

This futuristic photo includes the control panel from a 60’s era nuclear submarine, along with an LA36 DECwriter II from 1974. Quite a feat for the RAND scientists of 1954.

Robert Scoble is not impressed. He’s unsubscribed from Rich’s feed, because he “can’t trust what goes on his blog anymore.”

I had a similar feeling of outrage the first time I saw Wallace and Gromit in A Grand Day Out. This is the story where Wallace runs out of cheese and builds a rocketship to go to the moon and get some. After all, Wallace says, “everyone knows the moon is made of cheese.”

Well, I found out later that the moon is NOT made of cheese! This just ruined the credibility of the makers of this movie. How dare they try to pull that kind of hoax! Plus, their “actors” didn’t even look real. I promptly decided I’d never watch another Wallace and Gromit movie again.

(But let me know when this one is released, OK?)

Update: I forgot to add a link to Scott Mayo, who posted the image that Rich found.

Invisible JavaScript functions

Michael Geary | Thu, 2004-09-16 16:24

If you’re writing document-level JavaScript code in a PDF file, any variables you declare or assign to outside a function are created in the global object, which is the PDF document.

Suppose we have a PDF with this code as a document script, which creates three variables in three different ways:

var myVar = 11;    // declare a variable
myNoVar = 22;      // assign to a global variable
this.myProp = 33// create a document property

If we load this PDF and examine its variables in the JavaScript console, we see that each of the three variables is defined both as a global variable and as a property of this, the document object. That seems surprising until you consider that in Acrobat JavaScript, the document object is the global object.

(Keyboard input is in bold, and we type Ctrl+Enter at the end of each line to evaluate the expression.) When you’re writing code, you don’t want to pollute the document object’s namespace needlessly. You can wrap up your code in a function, so that variables you create with a var statement are local to the function. Adobe Acrobat encourages this by creating a function for you when you add a document script. If you use Acrobat’s Document JavaScripts dialog to add a script called DocScript, you’ll get an empty function to fill in:
function DocScript()
Of course, you need to call this function, so you may end up with code like this:
function DocScript( doc )
    var myVar1 = 11;
    myNoVar1 = 22;
    doc.myProp1 = 33;

DocScript( this );
Besides creating a scope for local variables, the function lets the code refer to the document object as doc instead of this. We didn’t have to do that, but I like to be able to use doc.whatever instead of this.whatever for document properties. If we look at these variables in the console window, the results are different:
ReferenceError: myVar1 is not defined
As expected, myVar1 does not show up either in the global object or the document object; it is local to the function. myVar2 does (because we didn’t use var), as does myProp1 (because we created it as a property of the doc object). The difference between myVar1 and this.myVar1 is expected too. It’s an error to reference a variable name that does not exist, so myVar1 throws a ReferenceError exception. But it’s not an error to reference a nonexistent property of an object—the reference merely returns the undefined value. So this.myVar1 returns undefined without error. But we’ve still put one name in the document object:

function DocScript(doc) {
    var myVar1 = 11;
    myNoVar1 = 22;
    doc.myProp1 = 33;


function DocScript(doc) {
    var myVar1 = 11;
    myNoVar1 = 22;
    doc.myProp1 = 33;


To avoid even this bit of namespace clutter, we can define an anonymous function and call it on the spot:

(function( doc )
    var myVar2 = 11;
    myNoVar2 = 22;
    doc.myProp2 = 33;
)( this );

The console results for myVar2, etc. are the same as the previous example with the named function, so I won’t repeat them. But unlike the named function, we haven’t added any symbols at all to the document or global object.

Why the extra parentheses? function(){} is the simplest possible anonymous function, but if we try to call it using function(){}() it’s a syntax error. However, if we wrap the entire function inside a pair of parentheses, then we can follow that with () to call it: (function(){})() is legal JavaScript.

The same trick works when you want to add a nested scope inside a function. The way you would do this in C doesn’t work in JavaScript:

function noscope()
    var i = 1;
    console.println( i );

    // This {} does not introduce a scope as in C
        var i = 2;
        console.println( i );

    console.println( i );


That code prints:


because there is only one variable named i in the entire function, even though we’ve (incorrectly) tried to add an inner scope with its own variable i.

In fact, if you load that function into ActiveState Komodo, it puts a green squiggly line under the inner var i = 2; complaining “strict warning: redeclaration of var i”.

But if we use a nested anonymous function:

function withscope()
    var i = 1;
    console.println( i );

        var i = 2;
        console.println( i );

    console.println( i );


it prints:


which indicates that the nested function introduced a new scope.

Bad Eggs

Michael Geary | Fri, 2004-09-10 09:12

Do you have static objects that dynamically allocate memory at startup and carefully free it when your app exits? I’ve used them in a few projects—more than I’d want to admit.

If you’re throwing out a carton of bad eggs, you could take the eggs out of the carton and put them in the trash one by one, followed by the empty carton. But it’s quicker and easier to just toss the whole thing in the trash.

Static objects are bad eggs.

I suppose the C++ runtime has to toss them all in the trash—call their destructors—because it doesn’t know if those destructors really need to be run. But if they’re just going to free memory there ought to be some way to skip all that.

Did you forget your password AGAIN?

Michael Geary | Thu, 2004-09-09 16:56

If you’re old enough, you may remember when software tried to be “user friendly.” This was before we had Interaction Designers, Usability Consultants, and the like. Nobody was quite sure what “user friendly” was supposed to mean, but it sounded like a good idea.

Sometimes it meant “easy to learn and use”, and that was a good idea. Other times it meant “be friendly to the user”. That didn’t always work out so well.

Windows XP tries hard to be friendly, but there’s one place where it annoys the heck out of me. No, it’s not the doggie who does tricks when you search for a file.

It’s the error message when I mistype my password at the Welcome screen:

Did you forget your password AGAIN?

Every time I see that, I have to stop myself from reading it as “Did you forget your password AGAIN?” And I want to reply, “No, I didn’t forget my password, you blithering idiot, I mistyped it! Do you have a problem with that?”

Then I remember it’s just a computer, it doesn’t know any better, so I just have to excuse its poor manners.

Why you have to shred that hard drive

Michael Geary | Mon, 2004-08-16 13:41

This may be old news, but it was new news to me, at least in this kind of detail.

How they read your data after you’ve erased it.

I keep forgetting that hard drives don’t store bits, but store magnetism, a much less precise science.

Remember The Conversation, anyone?

IBM firmware update can wreck your ThinkPad

Michael Geary | Mon, 2004-08-16 02:01

Matt from the ThinkPad mailing list found this out the hard way: Do not install the latest Embedded Controller Update on your ThinkPad if you have a non-IBM replacement battery, or your machine will no longer work on battery power. This new firmware update checks the battery status in a way that fails with non-IBM batteries.

The version number for the bad update varies for different models, but check the release notes for a phrase like “Enhancement of the battery control.”

The problem affects at least the A30/A30p and A31/A31p models, and possibly others.

Matt’s report is here:

A30/p Embedded Controller Update = Danger, Will Robinson!

Adding insult to injury, the way to fix the problem is to downgrade to the previous version of the Embedded Controller firmware, but you to do this, you need:

  1. A copy of the previous version for your machine. (And where do you get that?)
  2. A floppy drive. (The non-diskette version doesn’t work.)
  3. A charged battery—which means a fresh IBM battery, because your ThinkPad no longer trusts the non-IBM battery!

I’m a huge ThinkPad fan; I just hope this battery incompatibility is an inadvertent bug and that IBM will fix it quickly.

WiFi Desert

Michael Geary | Fri, 2004-08-13 15:31

Following up on his Windows XP SP2 report, John Levanger explains how they get wireless Internet in the Afghan desert:

Technology has changed the way we do business in the military. It does seem somewhat bizarre to have unfettered internet access in the middle of nowhere. I can sit in my tent with my ThinkPad and email, IM, and browse the internet the same as if I was sitting in my living room back in Georgia. Myself and 39 other buddies purchased a satellite system from an outfit out of India. The ISP is actually in Germany and we get just about T1 bandwidth which is split up 40 ways if everyone is on (which doesn’t occur due to different work shifts). We “beam” it out via Wi-Fi to the different tents. Even our aircraft (I’m in a CH-47 Chinook unit) are on an intranet of sorts. The system called Blue Force Tracker allows secure tracking of all the different aircraft (and ground elements) within the theater real time. We can email between aircraft and to our headquarters during flight…all with satellite technology. I’ve hardly sent a snail mail letter since I’ve been here. SSG John Levanger Taskforce Diamondhead Kandahar, Afghanistan “People sleep comfortably at night in their beds only because rough men stand ready to do violence in their behalf”.

(From the ThinkPad mailing list.)

Cancel to Continue

Michael Geary | Thu, 2004-08-12 15:48

Seen in a web application, after I clicked a link to go to a different page:

Cancel to Continue

Energizer Project: Keeps Building and Building

Michael Geary | Thu, 2004-08-12 15:37

Did you ever have a Visual C++ project that won’t stop building? It builds OK, but if you start the debugger or do another Ctrl+Shift+B, it says it’s out of date and wants to build again? Every time?

This happened to me and I was stumped. There was nothing in the Output window to tell me what was wrong. It looked like a perfectly successful build:

------ Build started: Project: Test,
Configuration: Debug Win32 ------

Compiling resources...

Build log was saved at
Test - 0 error(s), 0 warning(s)

-------------- Done --------------

    Build: 1 succeeded, 0 failed, 0 skipped

The only problem was it compiled those resources every time. It never thought they were up to date.

I tried some Google searches. Nothing. This was annoying me, and the rest of my team.

Finally, out of sheer frustration, I Ctrl+clicked on that “Build log” link, like the helpful tooltip suggested.

Oh. Now it tells me:

Compiling resources...

Test : warning PRJ0041 : Cannot find missing
dependency 'ICON_FILE' for file 'Test.rc'.
Your project may still build, but may
continue to appear out of date until this
file is found.

That gives me a desperately needed clue. Looking at Test.rc, I see that I’d coded:

#ifdef DEMO
    #define ICON_FILE "Icons\Demo.ico"
    #define ICON_FILE "Icons\Full.ico"


Well, it sounded like a good idea at the time, honest.

The resource compiler has no problem with this, but apparently the dependency checker can’t handle it.

Changing it to this fixed it:

#ifdef DEMO
    1 ICON DISCARDABLE "Icons\Demo.ico"
    1 ICON DISCARDABLE "Icons\Full.ico"

The Moral:

  • The information you need may be hiding behind a link. Just because the Output window has always told you about build problems doesn’t mean it will tell you today.
  • If your product has an Output window that almost always provides complete information, fix it so it always does.

A Typical XP SP2 Upgrade

Michael Geary | Thu, 2004-08-12 07:21

Just your everyday XP SP2 installation report. Be sure to read to the end…

(From the ThinkPad mailing list.)

NULL hInstance Considered Harmful

Michael Geary | Wed, 2004-08-11 16:10

On a couple of occasions, I've converted large Windows applications from EXEs to DLLs which are loaded by stub EXEs. There are several reasons you might want to do this, and for the most part it's surprisingly easy. Most Windows code doesn't know or care if it's running in a DLL or EXE, as long as it has the right instance handle for any functions that load resources or the like.

There were a few Windows functions that gave us grief, though. These are the functions that accept a NULL hInstance to mean the current process. I used to think this was a nice convenience, but all the code that used a NULL hInstance had to be converted to take an explicit hInstance.

I searched for the functions that came to mind and fixed them:


But I forgot about an entire group of functions and had to find them the hard way, through some tedious debugging:


Are there more functions like this?