What happens to WPARAM, LPARAM, and LRESULT when they travel between 32-bit and 64-bit windows?


The integral types WPARAM, LPARAM, and LRESULT are 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. What happens when a 32-bit process sends a message to a 64-bit window or vice versa?

There's really only one choice when converting a 64-bit value to a 32-bit value: Truncation. When a 64-bit process sends a message to a 32-bit window, the 64-bit WPARAM and LPARAM values are truncated to 32 bits. Similarly, when a 64-bit window returns an LRESULT back to a 32-bit sender, the value is truncated.

But converting a 32-bit value to a 64-bit value introduces a choice: Do you zero-extend or sign-extend?

The answer is obvious if you remember the history of WPARAM, LPARAM, and LRESULT, or if you just look at the header file.

The WPARAM is zero-extended, while LPARAM and LRESULT are sign-extended.

If you remember that WPARAM used to be a WORD and LPARAM and LRESULT used to be LONG, then this follows from the fact that WORD is an unsigned type (therefore zero-extended) and LONG is a signed type (therefore sign-extended).

Even if you didn't know that, you could look it up in the header file.

typedef UINT_PTR WPARAM;
typedef LONG_PTR LPARAM;
typedef LONG_PTR LRESULT;

UINT_PTR is an unsigned type (therefore zero-extended) and LONG_PTR is a signed type (therefore sign-extended).

Comments (6)
  1. Adam Rosenfield says:

    I'd assume that for messages where either WPARAM or LPARAM is a pointer, the kernel marshals the pointed-to data between the different address spaces?  In which case, no truncation or sign/zero-extension takes place, but instead the address spaces get remapped as appropriate.  That only works for known messages, though, where the kernel knows that the parameters are pointers.

  2. Random832 says:

    @Adam, well, yeah, (well, i suspect it's user32 that does it, not "the kernel") the same as on 32-bit windows. Each process has its own address space, after all – and even within a process messages with string parameters have to [potentially] be converted between ANSI and Unicode.

  3. Joshua says:

    The Win32 marshaling (I think USER32 is what does it although I don't know) only applies to standard messages, so don't depend on WM_USER+X doing it.

  4. SimonRev says:

    Actually for messages in the system range (< WM_USER), marshalling is exactly what happens, and is documented.  Of course for those messages, Windows understands what the data points to and so can perform an appropriate copy.

    Naturally, for messages outside that range Windows has no idea what the data is about and does not provide marshalling.

  5. Arlie says:

    The kernel/user32 has no business marshaling what the data points to.  It has no idea what the data is, how big it is, and whether that data contains *more* pointers.  If you want a marshaling/RPC subsystem, then use one, but that's not what MSG is for.

  6. zahical says:

    And, of course, we have the "poorman's RPC" generic WM_COPYDATA message. (though it's a bit harder to use on Vista+ because of UIPI)

Comments are closed.