Why is GetWindowLongPtr returning a garbage value on 64-bit Windows?

A customer was running into problems with their application on 64-bit Windows 8. They claimed that on Windows 8, the Get­Window­Long­Ptr is returning a garbage pointer, which causes their program to crash. The same program works fine on 64-bit Windows 7. They asked the Windows team why they broke Get­Window­Long­Ptr.

An investigation of the customer's code quickly turned up the issue:

INT_PTR CALLBACK AwesomeDialogProc(
    HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  Awesome *pThis = (Awesome*)GetWindowLongPtr(hdlg, DWLP_USER);

  switch (uMsg) {
    pThis = (Awesome*)lParam;
    SetWindowLongPtr(hdlg, DWLP_USER, (LONG)pThis);
    return TRUE;

   case WM_COMMAND:
     if (pThis != nullptr) {
       // This line crashes with pThis = garbage nonzero value
       return pThis->OnCommand(wParam, lParam);
     return FALSE;

  return FALSE;

See if you can spot the problem.

The error is in the line that calls Set­Window­Long­Ptr. It takes the 64-bit pointer value pThis and casts it to a LONG, which is a 32-bit integer type. This truncates the pointer and throws away the upper 32 bits of data. Therefore, when read back, the pointer looks like garbage because the top 32 bits were set to zero (or to 0xFFFFFFFF, depending on the value of bit 31).

Windows 8 made some improvements to the memory manager, and a side effect was a seemingly harmless change to the way memory is allocated in 64-bit processes. As a result of the change, pointer values greater than 4GB are much more common, which means that the pointer truncation will actually destroy data. (In Windows 7, the default heap tended to hang out below the 2GB boundary, so the code merely truncated zeros, which is mostly harmless.)

What I found particularly interesting about this error is that the DWL_USER window long was specifically renamed to DWLP_USER in 64-bit Windows in order to force a build break. Therefore, developers had to go in and convert each separate use of [GS]et­Window­Long with DWL_USER to a version that used [GS]et­Window­Long­Ptr with DWLP_USER, being careful not to truncate the pointer.

This customer missed that last little bit about not truncating the pointer, and all they did was a global search/replace:


"There, I fixed it."

Comments (33)
  1. TRWTF is that a pointer is longer than a "LONG".

  2. Gabe says:

    I like how their first instinct is to assume that the Windows team made a gratuitous breaking change in a trivial OS function, rather than to consider what might be wrong with their product.

  3. John D says:

    What, no compiler truncation warning on the SetWindowLongPtr(hdlg, DWLP_USER, (LONG)pThis) cast to LONG ?  Don't ignore warnings.

  4. alegr1 says:

    >What, no compiler truncation warning on the SetWindowLongPtr(hdlg, DWLP_USER, (LONG)pThis) cast to LONG ?

    The 64 bit truncation warning was removed from recent compilers.

  5. Hi. The post's main subject was crystal clear but there are a couple of things I didn't quite understand. First, I don't get the meaning of the hyperlinks on "mostly harmless" and "There, I fixed it". Second, what's TRWTF? (Does it have anything to do with … you know, its last three letters?)

    {The first two should be self-explanatory. One is a link to a book titled "Mostly Harmless." Another is a link to a photo blog called "There, I fixed it." TRWTF is explained in Urban Dictionary, popularized by the Daily WTF blog I link to in my blogroll. I'm wondering if I'm being trolled… -Raymond]
  6. Ben Craig says:

    alegr1: Only sort of true


    In Visual Studio 2005, you could set /Wp64 for your 64-bit builds, and get great truncation warnings, with a very low false positive rate.

    In Visual Studios 2008, 2010, and 2012, you could set /Wp64 for your 64-bit build and get great truncation warnings, but you had to tolerate a "command line warning" for every translation unit, warning that the flag was deprecated.

    In Visual Studio 2013, /Wp64 no longer catches the truncation warnings, but they can be explicitly enabled on a warning-by-warning basis.

    I really don't understand why these warnings are not turned on with even low warning levels.  I think the lack of these flags being turned on results in much lower quality 64-bit software on Windows.

  7. @Matteo: Yes, I am; Still, do you have any answers? Or are you as much unaware in spite of being not new?

  8. @Fleet Command says:

    Detailed explanations ruin the joke (plus a passing familiarity with pop/internet culture would suffice to explain the references here).

  9. Timothy Byrd says:

    @Fleet Command: For TRWTF, you are right about the last three letters. The first two stand for "The Real", so it could be (politely) translated as "The real problem here…"

  10. Joshua says:

    OH MY. I wonder if I smell a compatibility shim coming (limit heap to < 2GB for 64 bit process).

  11. Looks like Fleet Commander is part of today's lucky ten thousand! http://xkcd.com/1053/

    "Mostly harmless" is an in-joke for fans of Douglas Adam's radio show/book series/movie "The Hitchhiker's Guide to the Galaxy". The Guide is a repository of all knowledge in the universe; due to space constraints, the entry for Earth was "Harmless." The character Ford Prefect did a great deal of research on Earth and submitted it; the updated entry was again edited down, this time to "Mostly harmless."


    "There, I fixed it" is a joke which refers to hacky/lazy fixes, with a connotation of disdain for standard practices and potential long-term problems. Example:

    Wife: Honey, the smoke detector keeps beeping. Can you take care of it?

    Husband: (takes smoke detector to the garage, runs over it, sweeps pieces into the trash) There, I fixed it.

    "TRWTF" stands for "The real WTF." This is an in-joke on TheDailyWTF.com. A submitter shows some surprising code or other development practice with an obvious defect; a commenter then replies "the real WTF is…" and then points to an error or "error" which predates the defect shown, often to humorous effect. Example:

    Submitter: I found this custom SQL date arithmetic which fails spectacularly in leap years.

    Commenter: TRWTF is that <database engine> does not support SQL-92's DATE type.

  12. Myria says:

    Sigh…  I really loved /Wp64 and am sad to see it go in Visual Studio 2013.  I work on a team in which the majority of developers like to use Edit and Continue, so that means they're developing in 32-bit rather than 64-bit.  I almost always build the project in 64-bit, partly because I'm more or less responsible for the Win64 port, so I often have to clean up the mess left by others.

    @Joshua: You should see the hack I did to work around the fact that an archaic disk file format was designed to be loaded as-is into memory with 32-bit "holes" for pointers.  I VirtualAlloc(MEM_RESERVE) 2 GB of address space then create a heap structure inside of it, committing as necessary.  Then pointers will fit.  (2 GB instead of 4 GB because the format also has 32-bit ptrdiff_t's to represent distances to other objects…  Lovely, isn't it?)

  13. @Maurits [MSFT]: Thanks a bunch. :)

    P.S. It's "Fleet Command" with no "er" at the end

  14. voo says:

    @Maurits Thanks for answering his questions. I was getting slightly frustrated by the number of people who seem to assume that everybody here is a native speaker from a similar cultural background.

    I assume I should start making jokes about Dinner for One and berate everybody for not getting it..

  15. Tom says:

    @voo – However both of his questions are trivially answered by the first Google hits for those terms, which takes less effort to do than asking a question here and fits with "smart questions" etiquette – i.e. the same procedure as every year ;-)

    (though if everyone went in for smart questions Raymond would have rather less to write about)

  16. John Muller says:

    If I recall correctly, that same thing happened with a lot of applications at the change from 16 bit to 32 bit windows.

  17. Kudryavka says:


    This behavior is Opt-in


    Therefore, no need shim for old application.

  18. ulric says:

    yep..  Windows 8 is causing a lot of 64 bit pointer truncation bugs to be uncovered, which sucks because our company, like many, doesnt make patches for older apps.  so they just get random crahes and then revert to windows 7.

    one of the issue I've found is a truncation bug in the open source lib PySide (pythin wrapper for Qt) but they are ignoring the bug reports about it.  there was anotherone in libtiff or some other OSS image library .  We had a dozen cracches, probably havent found them all.  the hate for the win8 has slowed fixes bacause many devevelopers refuse to use it.

  19. Cesar says:

    Well, the function is called "SetWindowLongPtr", so the intuitive thing to do is pass it a long. This gets a -6 on Rusty's API quality scale: "The name tells you how not to use it."

    This is also the kind of thing which can trip Unix developers, who are used to "long" being as long as a pointer, even on 64-bit (most 64-bit Unix systems use the LP64 convention, where int is 32 bits and long is 64 bits). I know I would have a hard time seeing it when reading the code; casting a pointer to a long is the traditional way to store a pointer in an integer variable, and I am trained to think of "long" as "64-bit except on a 32-bit system, where pointers are 32-bit too".

    The modern way is to use uintptr_t (which is always big enough to contain any pointer), but a lot of software is from before it existed, so we still see casts to long.

  20. Gabe says:

    Is this the kind of thing that linking with "/HIGHENTROPYVA:NO" will fix?

    [Not obvious to me, because HIGHENTROPYVA controls ASLR for executable images, but the heap is not in an executable image. But I would not be surprised if the kernel folks overloaded the flag to control some other things, too. -Raymond]
  21. Random832 says:

    @Cesar except "LONG_PTR" is the name of a type, and it is indeed specifically the type that this function was named to refer to (both the type and the function were newly introduced with the win64 stuff – the documentation says they're supported back to Windows 2000, but that's because the current version of the SDK #defines the functions to refer to the old names when compiling for win32.)

    "where GetWindowLong takes a LONG, GetWindowLongPtr takes a LONG_PTR" is the most intuitive thing in the world. The only thing saying otherwise is looking at the name blindly and seeing the word "long" without ever having heard of an older related function that _actually_ took a long.

  22. Joshua says:

    @Cesar: The previous correct way was to use ptrdiff_t which existed since at least ANSI C and is big enough to hold a pointer on all architectures where arbitrary pointer arithmetic works (i.e. all flat architectures and most segmented architectures).

  23. Random832 says:

    @Joshua: no architecture is required to allow arbitrary pointer arithmetic (i.e. subtraction of pointers into unrelated objects) to work, even if it is a flat architecture. Breaking arbitrary pointer arithmetic _solely by truncating the result of pointer subtraction_ is a legal choice.

  24. Anon says:


    TRWTF is refusing to support older software in any capacity.

    Imagine if bridges or skyscrapers worked like that.

    "We no longer provide any support for the Golden Gate, but we'll build you a new one right next to it at ten times the cost."

    "The foundation is shot, we'll just have to wait until the building collapses, then we'll put a new one up."

    [I bet if the transportation district called up McClintic-Marshall Construction asking them to fix a problem with the bridge, they'd say "Um, that's your problem now." -Raymond]
  25. Joshua says:

    @Random832: Legal, yes, but stupid. In flat architectures, the machine word size is the same as the pointer size.

    [I bet if the transportation district called up McClintic-Marshall Construction asking them to fix a problem with the bridge, they'd say "Um, that's your problem now." -Raymond]

    Troll bait.

  26. ulric says:

    @anon nobody makes QFEs for older software no longer on support, nobody.  especially for new operating systems.  the companies will tell you that new os is unsuported/uncertififed. it's your fault if you broke it by uograding your machine. you should upgrade to the new software as well.

    ofher subject,  it's DWORD_PTR I'm familiar with to cast pointers to dwords!  that dates way way back. win3.1 perhaps?

    sorry for all the typos! insane ipad keyboard

  27. Joshua says:

    @ulric: Which is why Windows is unsuitable for industrial use. 10 year lifetime for the OS == 40 years too short.

    Yes, I know this leaves VxWorks or the vendor supports the OS.

  28. Joker_vD says:

    @Joshua: You meant 55 years, of course. 2038 – 1983 (System V release) = 55 years.

    And I don't quite get why the existence of sowtware which was incorrectly ported to a different processor architecture means that Windows is unsuitable for industrial use. Did SVR4 made it to x64?

  29. 64 says:

    TRWTF is that long in VC++ on 64-bit windows is 32-bit. I know why this was chosen. I still think it was a stupid decision (.net and unix got it right).

  30. mikeb says:

    I never fully understood why /Wp64 was deprecated/removed.  Did it simply issue too many false positives?

    For debugging you can force memory allocations to start from high addresses (above the 4GB boundary for /LARGEADDRESSAWARE applications) by setting the registry value:

        HKLMSYSTEMCurrentControlSetControlSession ManagerMemory ManagementAllocationPreference

    to 0x100000. That causes the `MEM_TOP_DOWN` flag to be set on calls to `NtAllocateVirtualMemory ()`. This will help catch many pointer truncation problem during testing. I think it would have been good for the CRT's debug heap to set that flag for x64 applications automatically, but oh well.

    For details see Windows Internals 6th Ed. Part 2 Chapter 10 or "Introduction to x64 debugging, part 5" on http://www.nynaeve.net/?p=14

    Released programs with pointer truncation problems might be made runnable with an appcompat shim – I see shims named LimitLargeAddressAwareProcessTo3gb and GlobalMemoryStatus2GB that might help (I'm not an appcompat shim expert, so I don't know precisely what these shims do).

    Finally, editbin.exe can be used with the /LARGEADDRESSAWARE:NO option to patch a program (even a 64-bit program) to only get addresses that are below the 2GB threshold. Unfortunately, editbin isn't included with Windows. It's provided with MSVC and some (all?) Windows SDKs, so it's usefulness as something end-users can use to fix programs is pretty much limited to power users.

  31. Joshua says:

    @Joker_vD: The point is I should be able to get security patches somehow, not whether it's going to get ported to a new processor. The controllers need to keep running on the hardware they were built with until the hardware wears out without worrying about viruses.

  32. Ben Craig says:


    When compiling a 32-bit application with /Wp64, there were definitely too many false positives.  typedef's that varied on bitness (like size_t) had to get special annotation to tell /Wp64 that they were going to be large enough to hold a 64-bit pointer.  If you have your own typedefs, you likely wouldn't have annotated them.  In addition, once you get to the internals of a template, some of that annotation information is lost, so you start getting false positives there as well.

    I'm still not sure why /Wp64 wasn't made the default for 64-bit applications though.

Comments are closed.