I wonder if wparam
and lparam
having different signs have any important internal implications or is it purely cosmetic choice to make them at least somehow different from one-another, now that we have have both being 64 bit on most computers?

- 14,422
- 4
- 15
- 31

- 205
- 1
- 3
- 13
-
1If there ever was any reason, it's historical. They reason they are so now is because they were so in the past. – john Jun 19 '22 at 20:35
-
1Although this doesn't answer your question, here is an article [_What do the letters `W` and `L` stand for in `WPARAM` and `LPARAM`?_](https://devblogs.microsoft.com/oldnewthing/20031125-00/?p=41713) by Raymond Chen to shed some light on the origins of those types. – heap underrun Jun 19 '22 at 20:57
-
Expanding on Raymond Chen's post is [this answer](https://stackoverflow.com/a/3146691/1553090) highlighting that `WORD` is an unsigned and `LONG` is signed, and so it follows that for the sake of compatibility the extension to 32-bit and then 64-bit values should maintain the same signedness as the original types. – paddy Jun 19 '22 at 21:00
-
2Does "Microsoft is weird" count as an underlying reason? :) – JaMiT Jun 19 '22 at 21:00
-
1@paddy That's true enough, but it begs the question, why was `lParam` a _signed_ 32 bit value back in the good old 16-bit days (oh how I miss them). And even though I programmed under Windows as far back as Windows 1.0 (sorry, folks), I don't know the answer to that question. It was often used to represent (via a cast, of course, like everything else in the Windows API, it seems sometimes) a (segmented, 16:16) `FAR` pointer, so making it signed made no sense (to me) even then. Perhps JaMiT is right after all. – Paul Sanders Jun 19 '22 at 21:19
-
5In 16-bit Windows, the name for a 16:16 segmented pointer was "long pointer" and it was natural to store them in `long` variables. – Raymond Chen Jun 19 '22 at 23:22
1 Answers
There seem to be three major themes in the explanation. (Much of this has been culled from comments on the question, hence "community wiki".)
That's how it has always been done.
Even though Windows is now a 64-bit operating system, the history of LPARAM
and WPARAM
go back to its 16-bit days. In those days, LPARAM
was signed and WPARAM
was unsigned. In the interest of continuity, the signedness was preserved in newer versions of Windows.
In particular, having one signed and the other unsigned is not an attempt to make them "at least somehow different". The signedness comes from days when the types were already differentiated by one being larger than the other.
WPARAM is data
The "W" in WPARAM
stands for "word", as in one word of data. This is similar to std::byte
in that it is viewed as a collection of bits. An unsigned integer type imposes the fewest semantics on those bits, making it more appropriate for raw data than a signed type. Hence, WPARAM
was defined to be unsigned.
LPARAM is long
The "L" in "LPARAM" stands for "long" or "long pointer". A "long pointer" is a concept from the end of the 16-bit era. The relevant details for the current topic are that a long pointer occupied 32 bits, and that an int
was only 16 bits.
An LPARAM
was designed to hold a long pointer. Hence, the focus of LPARAM
was the size; it had been a massive 32 bits on a 16-bit platform.
At the time, "regular" 16-bit pointers were routinely cast to and from int
as needed. Why int
? Mostly because it worked. In int
form, the value was not even treated as a collection of bits, not even raw data; it was merely a chunk of storage from which a pointer could be recovered. Any 16-bit type would work, and int
has such a nicely short name. Portability was not a concern, as these programs were written specifically for 16-bit Windows. No deeper reason than being the right size and a short name.
Long pointers were treated similarly, but an int
was not large enough to hold a long pointer. Instead a long
was used. This time, any 32-bit type would work, and among them, long
had the shortest name. As a result, long
became the basis for LPARAM
. Not a necessary result, just a natural progression. Since long
is signed, so is LPARAM
.
One might say that LPARAM
is signed because someone decided that integer types in C would default to signed unless otherwise specified. Ever since then, the history of LPARAM
has followed a natural progression with no further decisions based upon whether or not something should be signed.

- 14,422
- 4
- 15
- 31
-
*"A "long pointer" is a concept that has not been needed since the end of the 16-bit era."* - The concept got revived for 32-bit Windows by way of the [Address Windowing Extensions](https://learn.microsoft.com/en-us/windows/win32/memory/address-windowing-extensions), allowing applications to access data caches larger than 4GiB. – IInspectable Jun 20 '22 at 06:13
-
@IInspectable Interesting. I had thought that 64-bit Windows had arrived soon enough that 32-bit Windows did not need to account for PCs with >4GiB of memory. Apparently not. – JaMiT Jun 20 '22 at 06:22
-
To be perfectly accurate, AWE uses something more akin to [near pointers](https://devblogs.microsoft.com/oldnewthing/20200728-00/?p=104012), with the "segment" ("window view") implied. This is still application of the concept of segmented memory, even though it's not really using "long pointers" (or "far pointers"). – IInspectable Jun 20 '22 at 09:00