Why do I sometimes see redundant casts before casting to LPARAM?


If you read through old code, you will often find casts that seem redundant.

SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"string");

Why was "string" cast to LPSTR? It's already an LPSTR!

These are leftovers from 16-bit Windows. Recall that in 16-bit Windows, pointers were near by default. Consequently, "string" was a near pointer to a string. If the code had been written as

SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)"string");

then it would have taken the near pointer and cast it to a long. Since a near pointer is a 16-bit value, the pointer would have been zero-extended to the 32-bit size of a long.

However, all pointers in window messages must be far pointers because the window procedure for the window might very well be implemented in a different module from the sender. Recall that near pointers are interpreted relative to the default selector, and the default selector for each module is different. Sending a near pointer to another module will result in the pointer being interpreted relative to the recipient's default selector, which is not the same as the sender's default selector.

The intermediate cast to LPSTR converts the near pointer to a far pointer, LP being the Hungarian prefix for far pointers (also known as "long pointers"). Casting a near pointer to a far pointer inserts the previously-implied default selector, so that the cast to LPARAM captures the full 16:16 far pointer.

Aren't you glad you don't have to worry about this any more?

Comments (25)
  1. Shmuel Baron says:

    Raymond, did you mean LPCSTR? casting a literal to non-const string pointer is not a nice thing to do. Also the docs say LB_ADDSTRING takes an LPCTSTR anyway.

  2. Tom Seddon says:

    I don’t think they had const back then.

    (String literals are always convertible to char *, anyway, but you may get odd (if obvious :) results if you modify them.)

  3. SteveM says:

    We had const back then. We just didn’t use it ;-)

  4. Shmuel Baron says:

    Yeah, there is no harm using LPSTR instead of LPCSTR, and the internal function that gets this message probably casts the LPARAM to a LPCTSTR anyway and doesn’t modify it. It just hurts my eyes (and heart) when i see literals being cast to non-const :)

  5. asdf says:

    "string" is actually a const char[7], which implicitly can get casted to char * during standard conversions.

  6. lowercase josh says:

    If you’re passing an object of some kind (perhaps derived from a win32 structure), you might still need multiple casts. The bottom line is that C casts don’t only serve one function and sometimes you need to combine their properties, which requires multiple casts. In C++ this would be more clear (if uglier), since you’d need a static_cast and a reinterpret_cast.

  7. Nobody important says:

    Wait, LPARAM is a long? I always assumed it was a long pointer to an ARAM struct!!!

  8. Shmuel: Remember that Raymond is talking about the Win16 API and not the Win32 API which is what you find on MSDN these days.

    [I tried to find some site keeping the Win16 SDK reference docs, but couldn’t find any. So I may be wrong, of course. But I found at least two sites which contains sample code similar to Raymond’s example above:

    * http://hem.passagen.se/basscad/windows/kap3.htm

    * http://homepage1.nifty.com/nogue/wincho07.htm

    (the first one is in Swedish and the second in some other language which I’m not familiar with)]

  9. Chris Becke says:

    Writing COM objects in C++ frequently require … frequently its conventient at least to use multiple casts to take a class pointer, cast it to an interface and THEN to the void* that far too many COM APIs need.

    I never did like the *new* C++ casting operators.

  10. Tom Seddon says:

    Doesn’t everything convert correctly to void * in C++? I’m pretty sure you never need a cast to void *. You may need other casts along the way, though (if you want a pointer to a specific base of an object of a multiply-inherited type, say?)

  11. Arun Philip says:

    Off-topic:

    Andreas: http://homepage1.nifty.com/nogue/wincho07.htm is in Japanese.

    Not that I know Japanese, but IE detected the encoding as Japanese (Shift-JIS)

  12. Raymond Chen says:

    In C++, (A*)p and (A*)(void*)p can give different results. Back in the old days, you had to use (void*) to force the second interpretation. (Nowadays you would use reinterpret_cast…)

  13. Skywing says:

    I think you mean pointers to member functions, Normand? Non-member function pointers should cast to void* implictly.

  14. Re: C++, casting and COM:

    Ah, you’re doing it the old fashioned difficult way.

    The newfangled approach is this:

    typedef <typename T> HRESULT MyQI(IUnknown *pIn, T **pOut) {

    return pIn->QueryInterface(__uuidof(T), (void **) ppOut);

    }

    And now you’ve got all the gross badness in one place.

  15. Pavel Lebedinsky says:

    You don’t even need to define your own version of QI. IUnknown has had a template QueryInterface for quite some time:

    // From unknwn.h

    MIDL_INTERFACE("00000000-0000-0000-C000-000000000046")

    IUnknown

    {



    template<class Q>

    HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp)

    {

    return QueryInterface(__uuidof(Q), (void **)pp);

    }

  16. Norman Diamond says:

    11/3/2004 12:13 PM Tom Seddon

    > Doesn’t everything convert correctly to

    > void * in C++?

    Everything except function pointers.

    > I’m pretty sure you never need a cast to

    > void *.

    For the examples discussed here, you are correct. But in the examples discussed here, when multiple casts used to be necessary (i.e. were not redundant), perversely it does help readability to code the additional redundant cast to (void *).

    > You may need other casts along the way,

    Yes. Quoting that line because of this:

    11/3/2004 12:23 PM Raymond Chen

    > In C++, (A*)p and (A*)(void*)p can give

    > different results

    Yes, but was that intended to be a reply to Mr. Seddon? It looks to me like an unrelated fact. Am I misreading something?

    11/3/2004 4:30 PM Michael Grier

    > return pIn->QueryInterface(__uuidof(T),

    > (void **) ppOut);

    You are still abusing the calling convention. This has been discussed before and the fact has not changed. Even on a byte-addressed machine the language still does not guarantee defined behavior. You must declare a separate variable of type void *, pass the address of that variable in an argument, and then cast the received value to the actual type you want to use.

  17. Chris Becke says:

    "Now, I claim that people who abuse IUnknown::QI to return a pointer to the inner object which isn’t necessarily reference counted are abusing the CCs but this usage in particular is part of the core of the COM ABI."

    Eeek! No one was meant to know about that!

    I can state, with more personal experience than I’d like to admit, that this isn’t actually such a hot idea.

  18. Norman:

    Actually it does not abuse the calling convention. This is part of the COM ABI. See Raymond’s post last week or so about the crazy interface definition macros.

    I used to, like a good standard C++ doo-bee write this:

    void *pvReturned = NULL;

    hr = pIFoo->QueryInterface(IID_IBar, &pvReturned);

    // handle errors

    pIBar = reinterpret_cast<IBar *>(pvReturned);

    At the binary level (calling conventions are a binary interface contract, not a source interface contract), the contract is that the pointer-sized thing that the 2nd parameter points to will be filled in with an interface pointer corresponding to the type passed in the first parameter.

    Now, I claim that people who abuse IUnknown::QI to return a pointer to the inner object which isn’t necessarily reference counted are abusing the CCs but this usage in particular is part of the core of the COM ABI.

  19. Norman Diamond says:

    11/3/2004 10:24 PM Skywing

    > I think you mean pointers to member

    > functions, Normand?

    It doesn’t matter if the function is a member or not.

    > Non-member function pointers should cast to

    > void* implictly.

    No. A pointer to a non-function can be converted to type void* and back (and for backwards compatibility with pre-standard C, can be converted to type char* and back). A pointer to a function cannot be. Furthermore there exist hardware and C language implementations where an attempt to convert a function pointer to type void* and back really do destroy the pointer.

    11/4/2004 7:03 AM Michael Grier [MSFT]

    > Norman:

    > Actually it does not abuse the calling

    > convention.

    Yes it does. (void*) is universal in the sense of being able to point to any non-function and be cast back into the appropriate pointer type for it. A struct or union or class is included. The question of whether a member of a struct or union or class is included depends on the type of the member, i.e. function or non-function.

    (void**) is not universal in that sense. (void**) only has to be capable of pointing to (void*). And even more so, it doesn’t have to be capable of pointing to a function.

    In C, function pointers are universal with respect to each other (by a separate statement in the standard) but not with respect to non-function pointers. (SomeFunctionType*) only has to be capable of pointing to a function, it doesn’t have to be capable of pointing to a (void*) pointer. In C++, this gets more complicated for member functions, but still the incompatibility with (void*) just gets that much stronger.

    Anyway, (void**) doesn’t point to (int*) and doesn’t point to (SomeStruct*) and doesn’t point to (int(*()). If you lie about a (SomeStruct*) variable, cast its address to type (void**) and try to shove a (void*) value into your variable, you’re asking for trouble.

    I already reminded you yesterday what the correct technique is, and posted more details a few months ago when Mr. Chen made the same mistake (right after a series of essays concerning abuse of calling conventions). In fact I checked some MSDN pages when Mr. Chen made the same mistake, and the pages which I saw got it right.

    Use or abuse of reference counts do not turn undefined behavior into defined behavior. The pointer values themselves are already corrupt.

  20. Raymond Chen says:

    The Win32 ABI requires that all simple pointers have the same representation. Using (void**) as the "generic pointer to pointer" is legal according to Win32 (though not legal in general, as you point out).

  21. Michael says:

    But it would be better to avoid doing it, as some future version of the program might break spectacularly.

    So to prevent this MS would have to support this feature for backward compatibility ;)

    > Wait, LPARAM is a long? I always assumed it was a long pointer to an ARAM struct!!!

    :)) This is funny. Technically, LPARAM is not a pointer. It is a LONG, which can accept different values (or pointers) depending on the message.

  22. Michael J Smith says:

    The ANSI/ISO standard for C++ guarantees that you can cast a non-function pointer to void * and back without any loss of information.

    It makes no promises about function pointers. That doesn’t mean that they won’t work – it just means that they might not. A function pointer might be bigger than a void *, or it might have some extra info encoded. If you have an architecture where code and data are segregated (by hardware) you could possibly even get a segfault by casting one to the other.

    As Raymond pointed out above, on Win32 it happens to work. But it would be better to avoid doing it, as some future version of the program might break spectacularly.

  23. Norman Diamond says:

    11/4/2004 9:09 PM Raymond Chen

    > The Win32 ABI requires that all simple

    > pointers have the same representation.

    Hmm, I guess that has the desired effect. In fact I guess this had to be done in order to get Visual Basic to work.

    Does Win64 make a similar guarantee?

  24. Raymond Chen says:

    Yes, Win64 also requires that all simple pointers have the same representation.

Comments are closed.

Skip to main content