What clock do MSG.time and GetMessageTime use?

The MSG structure has a field called time which is a DWORD. There is also a function Get­Message­Time which returns a LONG. Both are documented as returning the time the message was generated, but the types are different. Are these time units comparable?

Yes, they are the same thing. They all use the 32-bit timer provided by the Get­Tick­Count function. Sorry about the inconsistency in signed/unsigned-ness. Feel free to cast between them; they are fundamentally the same thing.

Whether you prefer the signed or unsigned version depends on what you intend to do with the calculation, specifically, how you want to treat the case where the events occurred out of the expected order.

If you want to see whether some amount of time has elapsed, then you probably want to cast the return value of Get­Message­Time to DWORD:

if ((DWORD)GetMessageTime() - dwStartTime < VALUE) {

The unsigned test checks that the message time is on or after the start time, but not more than VALUE milliseconds after it. If the message time comes before the start time, then the test fails.

On the other hand, if you want to see which of two events occurred earlier, then you want to use a signed subtraction:

if (GetMessageTime() - (LONG)dwStartTime < VALUE) {

The signed test checks that the message time is no later than VALUE milliseconds after the start time.

Note that language lawyers may note that the above calculation relies on two's-complement overflow, and signed arithmetic overflow is specifically called out by the C language as undefined behavior. Therefore, a stricter language-compliant version of the above test would be

if ((LONG)((DWORD)GetMessageTime() - dwStartTime) < VALUE) {

The above version still relies on twos-complement arithmetic, but the Win32 ABI requires twos-complement arithmetic, so we're safe there at least.

Comments (13)
  1. skSdnW says:

    The MSDN page for Get­Message­Time mentions it wraps around to zero but they talk about the maximum value for a long integer, perhaps this could be updated to make it clear the number is unsigned.

    [Get­Message­Time uses a signed integer, so it wraps from 0x7FFFFFFF to 0x80000000. -Raymond]
  2. John Doe says:

    @Raymond, I'll have to agree with @skSdnW, the current doc says:

    "The return value from the GetMessageTime function does not necessarily increase between subsequent messages, because the value wraps to zero if the timer count exceeds the maximum value for a long integer."

  3. John Ludlow says:

    Yes – what Raymond is saying seems to be correct in terms of how the value would wrap (without having tested this myself!) but it's not what the documentation is saying.

    I'd suggest this is an error in the documentation and it should be updated to reflect reality,

  4. Wolf Logan [Bing IPE] says:

    …unless I'm misreading the intent of GetMessageTime(). On reflection, it's likely that the docs for GetMessageTime() were cut-and-pasted from Get­Tick­Count(), without accounting for the change in return type.

    Yuck. In that case, I agree there's an issue with the GetMessageTime() docs; they don't seem to match the return type correctly.

  5. Anil says:

    I wouldn't take the notion of relying on undefined behavior lightly, as there have been several high-profile cases (somewhat) recently of code behaving differently because of a compiler removing undefined behavior to remove or modify code. See blog.llvm.org/…/what-every-c-programmer-should-know.html

  6. JDT says:

    C++ really does feel like a minefield sometimes, doesn't it?

  7. Wolf Logan [Bing IPE] says:

    Stop — you're both right!

    The docs say that the returned value wraps, so that you'll never see a "negative" value from GetMessageTime(); but *operations on the underlying type* (LONG) will wrap as Raymond describes.

  8. David says:

    What's this fancy new Windows Application Brogramming Interface?

  9. AC says:


    Not sure if serious or tongue-in-cheek. I like "Brogramming", anyways.

    But of course it's the Application Binary Interface, which is not the same as the API.

  10. @Anil says:

    There is no undefined behavior, except in the specific instance where the author points it out, and provides an alternative implementation that does not rely on undefined behavior.

  11. Myria says:

    Someone's blog had this example of the problem of signed integer overflow in C, but I don't remember where. =/

    uint64_t MultiplyWords(uint16_t x, uint16_t y)


     uint32_t result = x * y;

     return result;


    One compiler's object code returned 0xFFFFFFFFFFFE0001 for Multiply(0xFFFF, 0xFFFF), and it wasn't a compiler bug – what the heck?  It's because of extremely subtle invocation of the undefined behavior rules of signed integers in C.

    Values smaller than "int" get promoted to "int" during an arithmetic operation.  This occurs regardless of whether the source type is signed or unsigned, annoyingly.  So the uint16_t's get cast to int; int32_t is assumed in this example.  Since the "from" type is unsigned, the values get zero-extended.  Now the multiply happens.  The compiler thus assumes that this cannot overflow – in other words, cross INT_MAX – and just emits a normal 32-bit multiply (mul or imul on x86; it doesn't matter, since the high half is discarded).

    Next is the uint32_t.  In the case of the compiler in question, it noticed that because the multiply cannot overflow, and because the input values are never signed, the multiplication result cannot be negative.  Therefore, casting from int32_t to uint32_t does nothing.  The optimizer removes the cast entirely – now the type of the "x * y" is int32_t instead of uint32_t.

    Finally, it returns the uint64_t.  The compiler's optimizer in this case was *not* as smart, not noticing that again the int32_t cannot be negative due to its previous assumptions.  Therefore, it sees a cast from int32_t to uint64_t, and emits a sign extension (cdq or movsxd on x86).

    When the input is 0xFFFF * 0xFFFF, the multiply results in the 32-bit value 0xFFFE0001.  The sign extension results in 0xFFFFFFFFFFFE0001.

    This shows that always using unsigned integers whenever possible still won't save you.  I wish C/C++ weren't like this – I'd take slightly slower run-time performance over hard-to-find bugs and security holes any day.

  12. Danny says:

    "Sorry about the inconsistency in signed/unsigned-ness. Feel free to cast between them; they are fundamentally the same thing"

    No they are not. This is clear evidence that someone inside Microsoft is working at that time machine otherwise wouldn't need for negative time. Ray, you lied to us all the time, you do have it after all :P

  13. 640k says:

    Time modulo 49 days are useless. Have to write 100 SLOCs for handling every operation because of it. (or 1 SLOC if you omit lf – worse)

    [Most user interface actions are complete in less than 49 days. -Raymond]

Comments are closed.

Skip to main content