Use elementsof(sz), not sizeof(sz)

Michael Geary | Fri, 2004-08-06 18:36

I had to fix a bug recently where my shell extension was crashing another application when you used that app’s File Open dialog.

This application has a thumbnail view of the selected file in the File Open dialog, which they generate the same way as Windows Explorer: by loading a shell extension for the selected filetype and calling its IExtractImage interface. It’s a fairly weird protocol: First they call your IPersistFile::Load to give you the filename, then you give them back the same filename when they call IExtractImage::GetLocation. Finally they call IExtractImage::Extract and that’s when you generate the thumbnail.

But, after my GetLocation method returned, the application silently exited. What could be wrong? My code worked fine in Explorer.

GetLocation is a typical function that takes a character string buffer and length along with some other parameters (omitted here):

HRESULT GetLocation(
    LPWSTR pszPathBuffer,
    DWORD cchMax, etc. );

I noticed that this other app was giving me an unusually large file pathname buffer, 520 characters or 1040 bytes to be exact. This number sounded strangely familiar (and not just because of this).

Then I realized what happened. I’ve never seen the source code for this app I was crashing, but I just know it looked like this:

WCHAR szPath[MAX_PATH];
pExtractImage->GetLocation(
    szPath, sizeof(szPath), etc. );

Oops. The cchMax argument to GetLocation is a length in characters, but sizeof gives you the size in bytes. And we’re talking WCHAR here, so each character is two bytes. MAX_PATH is 260, making szPath 520 bytes long, the number that they passed into my code.

One way to fix the problem is:

WCHAR szPath[MAX_PATH];
pExtractImage->GetLocation(
    szPath, MAX_PATH, etc. );

That gives correct code, but I never like seeing MAX_PATH repeated like this. sizeof is in the right spirit, actually measuring the array length instead of repeating a constant, but it measures the wrong thing, bytes instead of characters (array elements).

I like to code this with the elementsof macro, defined as:

#define elementsof( array ) 
    ( sizeof(array) / sizeof((array)[0]) )

Then you can just use elementsof instead of sizeof:

WCHAR szPath[MAX_PATH];
pExtractImage->GetLocation(
    szPath, elementsof(szPath), etc. );

elementsof is handy anytime you need the length of a character string array or any array.

Of course, I didn’t have the luxury of fixing this code at its source (other than reporting the bug to the program’s authors). So I worked around it by checking for the bogus 520 character cchMax and cutting it back to 260 (MAX_PATH) characters.

Submitted by Stephane Lajoie (not verified) on Fri, 2004-08-06 23:18.

Small typo in the last code block: elementsof(pszPath) should be elementsof(szPath).

Submitted by Michael Geary (not verified) on Sat, 2004-08-07 02:07.

Thanks for the sharp eye, Stephanie. That’s what I get for posting in the wee hours! Fixed now.

Submitted by asdf (not verified) on Mon, 2004-08-23 09:53.

I like to use:

template <class T, unsigned S> char (& lengthof(T (&)[S])) [S];
#define lengthof(a) (sizeof(lengthof(a)))
 

That way it protects against doing a lengthof on a pointer instead of an array. Doesn’t work for local types though because of the way C++ templates work but you run into those rarely and easy to work around.