• The Old New Thing

    The mystery of the icon that never appears


    A customer reported a problem showing an icon on their dialog box.

    We verified that this code does execute during the handling of the WM_INIT­DIALOG message. No assertion fires, yet no icon appears either.

    SHFILEINFO sfi = { 0 };
    DWORD_PTR dwResult = SHGetFileInfo(m_pszFile, &sfi,
                                       sizeof(sfi), SHGFI_ICON);
    assert(dwResult != 0);
    m_hico = sfi.hIcon;
    assert(m_hico != nullptr);
    assert(GetDlgItem(hdlg, IDI_CUSTOMICON) != nullptr);
    SendDlgItemMessage(hdlg, IDI_CUSTOMICON,
                       WM_SETICON, ICON_BIG, (LPARAM)m_hico);
    assert(SendDlgItemMessage(hdlg, IDI_CUSTOMICON,
                       WM_GETICON, ICON_BIG, 0) == (LPARAM)m_hico);

    Our dialog template says

      ICON "", IDI_CUSTOMICON, 10, 10, 0, 0

    The customer did some helpful preliminary troubleshooting:

    • Verify that the code does indeed execute. It sounds obvious, but some people forget to check this. They get distracted trying to figure out why a function isn't working, when in fact the root cause is that you forgot to call the function in the first place.
    • Verify that the SHGet­File­Info call succeeded. That rules out the case that the static control is displaying nothing because you didn't give it anything to display.
    • Verify via Get­Dlg­Item that the control you're trying to talk to really does exist. That rules out the case that you are talking to an empty room. (For example, maybe you added the control to the wrong template.)
    • Verify via WM_GET­ICON that the attempt to change the icon really worked.

    The problem is that the customer is using the wrong icon-setting message.

    The WM_SET­ICON message lets you customize the icon that is displayed in the window's caption bar. For this to have any effect, your window naturally needs to have the WS_CAPTION style. If you don't have a caption, then telling the window manager, "Please display this icon in my caption" is mostly a waste of time. It's like signing up for a lawn-mowing service when you don't have a lawn.

    The message to change the icon displayed inside a static control is STM_SET­ICON.

    SendDlgItemMessage(hdlg, IDI_CUSTOMICON,
                       STM_SETICON, (LPARAM)m_hico, 0);

    Red herring: Some of you may have noticed that the customer set their control size to 0×0. "You aren't seeing an icon because you set the control to zero size!" But since this control was not created with SS_REAL­SIZE­CONTROL or SS_CENTER­IMAGE, the control will resize itself to match the size of the icon.

    Here's a sample program to show both types of icons set on the same window, so you can see the difference.

    #include <windows.h>
    #include <commctrl.h>
    LRESULT CALLBACK SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
        LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
     switch (uMsg) {
     case WM_NCDESTROY:
      RemoveWindowSubclass(hwnd, SubclassProc, 0);
     return DefSubclassProc(hwnd, uMsg, wParam, lParam);
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       PSTR lpCmdLine, int nShowCmd)
     HWND hwnd = CreateWindow("static", nullptr,
                   SS_ICON | SS_CENTERIMAGE,
                   CW_USEDEFAULT, CW_USEDEFAULT,
                   CW_USEDEFAULT, CW_USEDEFAULT,
                   nullptr, nullptr, hinst, nullptr);
     SetWindowSubclass(hwnd, SubclassProc, 0, 0);
     HICON hicoCaption = LoadIcon(nullptr, IDI_EXCLAMATION)
     SendMessage(hwnd, WM_SETICON, ICON_BIG,
     HICON hicoClient = LoadIcon(nullptr, IDI_QUESTION);
     SendMessage(hwnd, STM_SETICON,
                 reinterpret_cast<LPARAM>(hicoClient), 0);
     MSG msg;
     while (GetMessage(&msg, NULL, 0, 0)) {
     return 0;

    We create a top-level static window, which is highly unusual, since static controls are nearly always children of some other window. I'm doing this specifically to show the two different icons. You don't want to do this in a real program.

    The static control has the SS_ICON style, because we want it to display an icon, and the SS_CENTER­IMAGE style, because we just want it to center the icon in its client area without resizing. (We will control the size.)

    We subclass the window so that we can post a quit message to exit the program when the window is destroyed, which the user can do by pressing Alt+F4. (Hey, this is just a demo program. Catching clicks on the × button is just extra code that will distract from the purpose of the demonstration. Heck, this entire subclass thing is already distracting from the purpose of the demonstration!)

    We load up two icons, an exclamation point, which we set as our caption icon, and a question mark, which we put in our client area. (We could have used the Static_Set­Icon macro in windowsx.h to send the STM_SET­ICON message, but I did it manually just to make the message explicit.)

    Run the program, and there you can see the two different types of icons: The exclamation point goes in the caption, and the question mark goes in the client area.

  • The Old New Thing

    How do I change among the three levels of play in Space Cadet Pinball?


    Many many years ago, a customer presumably was taking advantage of the unlimited support part of their support contract when they asked

    In the documentation for Space Cadet Pinball, it says...

    The game is divided into three levels of play: basic, intermediate, and advanced. The objective of all levels is to achieve the highest point total. The more advanced the level of play, the greater the point reward.

    How does one advance to the next level of play? The documentation doesn't explain.

    The level of play being described in the documentation refers not to any particular in-game option or accomplishment. It's describing three different styles of playing the same game.

    • Basic level: You try not to lose the ball.
    • Intermediate level: You earn more points by hitting the targets in the right order.
    • Advanced level: You earn even more points by completing missions.

    It's like how you can play tennis on multiple levels.

    • Basic level: Chase the ball when it is hit to you, and return it over the net.
    • Intermediate level: Anticipate where the ball will be hit, and return it to where your opponent will have difficulty reaching it.
    • Advanced level: Plan multiple-stroke attacks. Identify your opponent's weakness and adjust your style of play to take advantage of it.

    In other words, the answer is not in the game. The answer is inside you.

  • The Old New Thing

    Enumerating bit sequences with isolated zero-bits via the Fibonacci recurrence


    Today's Little Program enumerates bit sequences of a particular length subject to the constraint that you cannot have consecutive 0-bits. This may sound kind of arbitrary, but it is important in magnetic media storage, because you cannot go too long without a flux reversal (traditionally represented by a 1); otherwise, the read head's clock starts to drift and gets out of sync with the data. (The read head uses flux reversals both for signaling and for clock synchronization.)

    Let's say that an allowable bit sequence is one that contains no consecutive 0-bits.

    The recurrence for enumerating these types of constrained bit sequence is the Fibonacci recurrence:

    F(n) = F(n − 1) + F(n − 2)

    The way to see this is to study the last digit in an allowable bit sequence.

    If the last digit is a 1, then if you delete it, you have an allowable bit sequence that is one digit shorter. Conversely, you can take any allowable bit sequence of length n − 1 and tack a 1 onto it, and you'll have an allowable sequence.

    If the last digit is a 0, then if you delete it, you also have an allowable bit sequence that is one digit shorter, but not all allowable bit sequences of length n − 1 can be reached in this way. Allowable bit sequences that end in 0 cannot be reached because adding another 0 would result in two consecutive 0-bits, which is disallowed. Therefore, the last digit of the truncated bit sequence must be 1, and what you really have is an allowable bit sequence of length n − 2, followed by a hard-coded 1.

    Okay, now that we understand the enumerative justification for the recurrence, we can proceed to write the code that generates it.

    function GCR(n, f) {
     if (n == 0) { f(""); return; }
     if (n < 0) { return; }
     GCR(n-1, function(s) { f(s + "1"); });
     GCR(n-2, function(s) { f(s + "10"); });
    GCR(8, console.log);

    The test run calculates all 8-bit allowable sequences. But wait, there's a bug: It shows only the sequences that begin with a 1.

    If you're Steve Wozniak, then this bug is a feature, because the Apple II floppy drive also required the first bit to be set, so our bug exactly matches the hardware requirements.

    But let's fix our bug. Where did it come from?

    Our recursive step missed a base case: The single-digit bit sequence 0 is allowable, but we rejected it because we thought that it needed to be separated from the null string by a 1, to protect against the null string ending in 0. But the null string doesn't end in zero, so this protection was unnecessary.

    Repairing our base case:

    function GCR(n, f) {
     if (n == 0) { f(""); return; }
     if (n == 1) { f("0"); f("1"); return; }
     GCR(n-1, function(s) { f(s + "1"); });
     GCR(n-2, function(s) { f(s + "10"); });
  • The Old New Thing

    Even if you're the President, your mother still has the power to embarrass you


    Last year, in honor of Mother's Day (the United States version), the John F. Kennedy Library shared a letter sent by President Kennedy to his mother.

    Mrs. Kennedy had contacted Premier Khrushchev asking for an autographed photo, copies of which were subsequently forwarded to the White House so that the President could sign them as well. President Kennedy tries to express in the politest language he can muster that the mother of a sitting president directly contacting a foreign dignitary is "subject to interpretations", and that in the future, it would be greatly appreciated if she would let the White House clear any future such contacts.

    It so happened that this particular letter-writing incident occurred very close to the Cuban Missile Crisis. I can imagine President Kennedy burying his hand in his face upon realizing that his mother may have inadvertently exacerbated a major international crisis, just by doing what moms do.

  • The Old New Thing

    When was the WM_COPYDATA message introduced, and was it ported downlevel?


    Gabe wondered when the WM_COPY­DATA message was introduced.

    The WM_COPY­DATA message was introduced by Win32. It did not exist in 16-bit Windows.

    But it was there all along.

    The The WM_COPY­DATA message was carefully designed so that it worked in 16-bit Windows automatically. In other words, you retained your source code compatibility between 16-bit and 32-bit Windows without having to do a single thing. Phew, one fewer breaking change between 16-bit and 32-bit Windows.

    As Neil noted, there's nothing stopping you from sending message 0x004A in 16-bit Windows with a window handle in the wParam and a pointer to a COPY­DATA­STRUCT in the lParam. Since all 16-bit applications ran in the same address space, the null marshaller successfully marshals the data between the two processes.

    In a sense, support for the WM_COPY­DATA message was ported downlevel even before the message existed!

  • The Old New Thing

    How can you use both versions 5 and 6 of the common controls within the same module?


    Commenter Medinoc was baffled by the statement that the decision to use the visual-styles-enabled version of the common controls library is done on a window-by-window basis. " Isn't it rather on a per-module basis, depending on each module's manifest? If it is indeed on a per-window basis, how does one choose?"

    Whether a particular call to Create­Window (or one of its moral equivalents) gets the classic version of the control or the visual-styles-enabled version of the control depends on which activation context is active at the point of the call. If an activation context with version 6 of the common controls is active, then you get the control from version 6 of the common controls. Otherwise, you get the classic control.

    If you use the ISOLATION_AWARE_ENABLED macro, then including commctrl.h turns on a bunch of macros that take all your calls to Create­Window and related functions, and converts them into something like this:

    HWND CreateWindow_wrapped(... parameters ...)
     HWND hwnd = nullptr;
     ULONG_PTR ulCookie;
     if (ActivateActCtx(ModuleContext, &ulCookie)) {
      hwnd = CreateWindow(... parameters ...);
     DeactivateActCtx(0, ulCookie);
     return hwnd;

    where Module­Context is a global variable that holds the activation context you specified in your manifest.

    In other words, any time your code tries to create a window, the wrapper macros activate your v6 manifest, create the window, then deactivate the manifest.

    Remember that nobody walks the stack looking to see who the caller is. The return address is not reliable. (And checking the return address doesn't help for dynamically-generated code anyway.) The way to know which activation context is active is for somebody to actually come out and set it.

    Back to the question: The way you choose whether you want a classic or a visual-styles-enabled version of a control is by deciding whether or not to have the v6 manifest active when you call Create­Window.

    A common mistake is that people will call a function that requires a v6 manifest, such as Task­Dialog, but they will forget to activate the v6 manifest before calling. The result is that they call into version 6 of the common controls, but when the common controls library tries to create its task dialog, it fails because the v5 context is active, and the v5 context does not have a task dialog control.

  • The Old New Thing

    Why does saving a file in Notepad fire multiple FindFirstChangeNotification events?


    Many people have noticed that the Read­Directory­ChangesW and Find­First­Change­Notification functions (and therefore their BCL equivalent File­System­Watcher and WinRT equivalent Storage­Folder­Query­Result) fire multiple FILE_ACTION_MODIFIED events when you save a file in Notepad. Why is that?

    Because multiple things were modified.

    Notepad opens the file for writing, writes the new data, calls Set­End­Of­File to truncate any excess data (in case the new file is shorter than the old file), then closes the handle. Two things definitely changed, and a third thing might have changed.

    • The file last-modified time definitely changed.
    • The file size definitely changed.
    • The file last-access time might have changed.

    It's therefore not surprising that you got two events, possibly three.

    Remember the original design goals of the Read­Directory­ChangesW function: It's for letting an application cache a directory listing and update it incrementally. Given these design goals, filtering out redundant notifications in the kernel is not required aside from the performance benefits of reduced chatter. In theory, Read­Directory­ChangesW could report a spurious change every 5 seconds, and the target audience for the function would still function correctly (albeit suboptimally).

    Given this intended usage pattern, any consumer of Read­Directory­ChangesW needs to accept that any notifications you receive encompass the minimum information you require in order to keep your cached directory information up to date, but it can contain extra information, too. If you want to respond only to actual changes, you need to compare the new file attributes against the old ones.

    Bonus chatter: Actually, the two things that changed when Notepad set the file size are the allocation size and the file size (which you can think of as the physical and logical file sizes, respectively). Internally, this is done by two separate calls into the I/O manager, so it generates two change notifications.

  • The Old New Thing

    Letting the boss think your project is classier than it really is


    Once upon a time, there was a team developing two versions of a product, the first a short-term project to ship soon, and the other a more ambitious project to ship later. (Sound familiar?) They chose to assign the projects code names Ren and Stimpy, in honor of the lead characters from the eponymous cartoon series.

    Over time, the two projects merged, and the code name that stuck was Ren.

    When the project came up in a meeting with Bill Gates, it was mentioned verbally but never spelled out, and since Bill wasn't closely tuned into popular culture, he mapped the sound /rɛn/ not to the hairless Mexican dog but to the Christopher Wren, architect of St. Paul's Cathedral. In follow-up email, he consistently referred to the project by the name "Wren".

    The Ren team liked the fact that their name gave the boss the impression that the project was going to be a masterpiece of architectural beauty, so they never told him he got the name wrong.

    Even though it has nothing to do with the story: The project in question is the one that eventually became known to the world as Outlook.

  • The Old New Thing

    Warehouse holding 1000 cans of surströmming burns to the ground, insert punch line here


    A warehouse in Sweden holding 1000 cans of surströmming burned to the ground last week. No people were injured.

    The cans of surströmming, already prone to violent decompression under normal conditions, exploded over a period of six hours. Some of them turned into projectiles and shot through the air.

    No information on what effect this will have on the supply of surströmming ice cream. I knew you were wondering.

  • The Old New Thing

    Getting the location of the Close button in the title bar


    Today's Little Program locates the × button in the corner of the window and, just to show that it found it, displays a balloon tip pointing at it.

    Let's start with the program from last week, the one that displays a balloon tip, then make these changes:

    BOOL GetCloseButtonCenter(HWND hwnd, POINT *ppt)
     TITLEBARINFOEX info = { sizeof(info) };
     if (!SendMessage(hwnd, WM_GETTITLEBARINFOEX, 0, (LPARAM)&info))
        return FALSE;
     if (info.rgstate[5] & (STATE_SYSTEM_INVISIBLE |
                                STATE_SYSTEM_OFFSCREEN |
                                STATE_SYSTEM_UNAVAILABLE)) return FALSE;
     ppt->x = info.rgrect[5].left +
                 (info.rgrect[5].right - info.rgrect[5].left) / 2;
     ppt->y = info.rgrect[5].top +
                 (info.rgrect[5].bottom - info.rgrect[5].top) / 2;
     return TRUE;
      case TEXT(' '):
        if (GetCloseButtonCenter(hwnd, &pt)) {
          SendMessage(g_hwndTT, TTM_TRACKPOSITION, 0, MAKELPARAM(pt.x, pt.y));

    Instead of positioning the balloon at the cursor position, we put it at the center of the Close button. We use the WM_GET­TITLE­BAR­INFO­EX message to obtain information about the window title bar, specifically checking information about the Close button. After verifying that it is visible and on-screen and enabled, we calculate its center point and return success.

    The WM_GET­TITLE­BAR­INFO­EX message is new for Windows Vista. Next time, we'll cook up a method that works on Windows 2000 and Windows XP.

Page 9 of 429 (4,284 items) «7891011»