March, 2012

  • The Old New Thing

    Why does Explorer ignore seconds when sorting by Date Modified?

    • 39 Comments

    A customer reported that Explorer appears to be ignoring the seconds when sorting by Date Modified. The customer was kind enough to include detailed steps to reproduce the problem.

    Start with a folder with several files, sorted by Date Modified.

    Name Date modified Type
    TPS 06-05-2012 Report 6/12/2012 7:00 AM Contoso Document
    TPS 05-29-2012 Report 6/5/2012 11:30 AM Contoso Document
    TPS 05-22-2012 Report 5/29/2012 10:17 AM Contoso Document
    TPS 05-15-2012 Report 5/22/2012 2:35 PM Contoso Document
    TPS 05-09-2012 Report 5/15/2012 11:26 AM Contoso Document
    TPS 05-02-2012 Report 5/9/2012 10:31 AM Contoso Document

    Right-click on the newest file, select Copy.

    Right-click on the blank column on the right, select Paste. This will create a file with the same name, but with "- Copy" appended.

    Press F5 to refresh the view and note the sort order. The copy appears at the top of the list.

    Name Date modified Type
    TPS 06-05-2012 Report - Copy 6/12/2012 7:00 AM Contoso Document
    TPS 06-05-2012 Report 6/12/2012 7:00 AM Contoso Document
    TPS 05-29-2012 Report 6/5/2012 11:30 AM Contoso Document
    TPS 05-22-2012 Report 5/29/2012 10:17 AM Contoso Document
    TPS 05-15-2012 Report 5/22/2012 2:35 PM Contoso Document
    TPS 05-09-2012 Report 5/15/2012 11:26 AM Contoso Document
    TPS 05-02-2012 Report 5/9/2012 10:31 AM Contoso Document

    Highlight the newly-created file, hit F2, and give the document a different name, and also remove the "- Copy" suffix. Hit Enter to accept the operation.

    Press F5 to refresh the view again. Notice that the file that you just renamed, which is the newest file in the folder (it having just been created seconds ago) appears second in the list.

    Name Date modified Type
    TPS 06-05-2012 Report 6/12/2012 7:00 AM Contoso Document
    TPS 06-12-2012 Report 6/12/2012 7:00 AM Contoso Document
    TPS 05-29-2012 Report 6/5/2012 11:30 AM Contoso Document
    TPS 05-22-2012 Report 5/29/2012 10:17 AM Contoso Document
    TPS 05-15-2012 Report 5/22/2012 2:35 PM Contoso Document
    TPS 05-09-2012 Report 5/15/2012 11:26 AM Contoso Document
    TPS 05-02-2012 Report 5/9/2012 10:31 AM Contoso Document

    It appears that Explorer is ignoring the seconds in the Date Modified column and sorting only by the hour and minute.

    It's an interesting theory the customer came up with, but the customer was fooled by the fact that he ran the experiment shortly after modifying the TPS 06-05-2012 Report document, so that the real behavior was masked.

    When you copy a file, the system preserves the date-modified timestamp. The Date modified column is not ignoring the seconds; in fact, it's comparing them quite carefully, but since the timestamps of the original and the copy are the same, the timestamps compare equal. And when the items compare equal according to the sort criteria, Explorer falls back to sorting by name, and the fallback sort is always ascending.

    The confusion would have been cleared up if the Date modified column used the long time format instead of the short time format, but that only pushes the problem to files whose timestamps are fractional seconds apart. You have two files which show up as "6/12/2012 7:00:12 AM" and don't realize that one of them is "6/12/2012 7:00:12.02 AM" and the other is "6/12/2012 7:00:12.89 AM". My guess is that geeks won't be satisfied until Explorer shows the full 64-bit FILETIME so you can see the difference down to the 100-nanosecond interval.

    (If you want to see the seconds, you can look on the file's property sheet.)

  • The Old New Thing

    Why doesn't the Maximize button maximize across all monitors?

    • 42 Comments

    Cheong wonders why there isn't a way for the Maximize button to maximize a window across multiple monitors. (Troll asks a similar question: Why doesn't Windows support spanned mode for multiple monitors?)

    We tried it that way at first. And we quickly discovered why it was a bad idea.

    Having multiple monitors behave as a single giant display surface creates a bunch of problems, because there's this annoying line down the center that breaks up everything it touches. Everything on one side of the line is on one monitor and everything on the other side of the line is on the other monitor. (And add additional annoying lines if you have more than two monitors.) If that line intersects text, then you have letters chopped in half, and then you have to mentally glue the two pieces back together in order to read them.

    What's even worse, the broken-up-text problem shows up more often than you might expect, because a lot of programs like to do things like center their dialog boxes. The result is that nearly every dialog box that you see is perfectly bisected by the annoying line. The dialog boxes consistently appear in the worst possible location.

    (Accessibility note: The text in this table cell is identical to the text in the previous cell.)

    Having multiple monitors behave as a single giant display surface creates a bunch of problems, because there's this annoying line down the center that breaks up everything it touches. Everything on one side of the line is on one monitor and everything on the other side of the line is on the other monitor. (And add additional annoying lines if you have more than two monitors.) If that line intersects text, then you have letters chopped in half, and then you have to mentally glue the two pieces back together in order to read them.

    What's even worse, the broken-up-text problem shows up more often than you might expect, because a lot of programs like to do things like center their dialog boxes. The result is that nearly every dialog box that you see is perfectly bisected by the annoying line. The dialog boxes consistently appear in the worst possible location.

    It gets worse if the two monitors do not have exactly the same dot pitch, because it means that no matter how you position your monitors, you will never get all the lines of text to line up perfectly. If you carefully align the monitors so that, say, the baselines of the first lines of text match up, you'll find that the bottom lines don't. Not only do your eyes have to navigate the horizontal gap between the monitors, they also have to navigate the vertical gap created by the pixel density mismatch. Which is even harder because the vertical gap varies from line to line.

    It's like that trick where you put a pencil in a glass of water and observe it from the side. Now imagine a glass filled with pencils, and each pencil refracts differently. And now imagine each pencil is a line of text you're trying to read.

    (Accessibility note: The text in this table cell is identical to the text in the previous cell.)

    It gets worse if the two monitors do not have exactly the same dot pitch, because it means that no matter how you position your monitors, you will never get all the lines of text to line up perfectly. If you carefully align the monitors so that, say, the baselines of the first lines of text match up, you'll find that the bottom lines don't. Not only do your eyes have to navigate the horizontal gap between the monitors, they also have to navigate the vertical gap created by the pixel density mismatch. Which is even harder because the vertical gap varies from line to line.

    It's like that trick where you put a pencil in a glass of water and observe it from the side. Now imagine a glass filled with pencils, and each pencil refracts differently. And now imagine each pencil is a line of text you're trying to read.

    Wait, I'm not finished yet. Things get still worse if your two monitors are not the same size. In that case, the virtual screen is larger than the visible region. For example, my monitor arrangement has a landscape monitor on the left and a portrait monitor on the right, with the bottoms of the monitors aligned.

       Virtual monitor   
       
    Monitor 2
       
    Virtual
    Monitor
       
    Monitor 1

    If a window were maximized across the virtual screen, the contents of the upper left corner would not be visible at all!

    Now, there may be specific cases where it would be meaningful to maximize a window across the virtual monitor, and if a program wants to do that, it can certainly implement that on its own. But even for a picture-viewing application, maximizing across the virtual monitor may not be a great idea: Pictures often have people in the center, and then you end up with somebody's head cut in half.

  • The Old New Thing

    This isn't Highlights magazine: Sort keys and why they change

    • 25 Comments

    Some time ago, Ry Jones gave some examples of Quotable Raymond, including the following:

    How to make a good doc bug report:

    1. Don't embed pictures. ... This isn't Highlights magazine.

    What Ry didn't realize is that his "..." totally misrepresented the message. There were actually two separate items, but he combined them into one and replaced the missing parts with "...". I already ranted some time ago about embedding pictures. Today I'll rant about the second item, which is turning a bug report into the Spot the difference between these two pictures game in Highlights magazine.

    A customer reported that the LCMap­String function was returning incorrect results on Windows 7. To illustrate, they included a sample program with the Windows XP and Windows 7 output.

    #include <windows.h>
    
    char bad[] = "... a very long string...";
     
    char dest[10000];
     
    int main()
    {
        int nChr, i;
     
        nChr = LCMapStringA(1033,  /* English (US) */
                      LCMAP_SORTKEY,
                      bad,
                      -1,    /* source is NULL terminated */
                      dest,  /* destination */
                      sizeof(dest));  /* dest size */
        /* print out returned number of bytes as hexadecimal */
        for (i = 0; i < nChr; i++)
            printf("%02x ", (unsigned char)dest[i]);
        printf("\n");
    }
    

    On Windows XP, we get the correct sort key:

    0e 48 0e 7c 0e 8a 0e 21 0e 51 07 02 0e 32 0e 7e 0e 91 0e 9f 0e 51 07 02 0e 1a 0e 7c 0e 48 0e 7c 0e 8a 07 02 0e 91 0e 32 0e 99 07 02 0e 02 0e 51 0e 21 0e 99 07 2f 07 02 0e 0a 0e 7c 0e 70 0e 91 0e 21 0e 0a 0e 99 0e 21 0e 99 0e 9f 0e 8a 07 02 0e 02 0e 1a 0e 32 0e 7e 0e 32 0e 91 0e 32 0e 0a 0e 32 0e 70 0e 25 07 02 0e 21 0e 48 0e 32 0e 99 07 2f 07 02 0e 91 0e 21 0e 1a 07 02 0e 1a 0e 7c 07 02 0e 21 0e 32 0e 9f 0e 91 0e 51 0e 7c 0e 1a 07 02 0e 99 0e 21 0e 51 0e 7e 0e 7c 0e 8a 07 02 0e 32 0e 70 0e 0a 0e 32 0e 1a 0e 32 0e 1a 0e 9f 0e 70 0e 99 07 02 0e 9f 0e 99 07 02 0e 48 0e 02 0e 09 0e 7c 0e 8a 0e 21 07 02 0e 21 0e 99 07 02 0e 1a 0e 7c 0e 48 0e 7c 0e 8a 0e 21 07 02 0e 51 0e 02 0e 25 0e 70 0e 02 07 02 0e 02 0e 48 0e 32 0e 89 0e 9f 0e 02 07 33 07 02 08 14 07 02 0e 9f 0e 99 07 02 0e 21 0e 70 0e 32 0e 51 07 02 0e 02 0e 1a 07 02 0e 51 0e 32 0e 70 0e 32 0e 51 07 02 0e a2 0e 21 0e 70 0e 32 0e 02 0e 51 07 2f 07 02 0e 89 0e 9f 0e 32 0e 91 07 02 0e 70 0e 7c 0e 91 0e 99 0e 8a 0e 9f 0e 1a 07 02 0e 21 0e a6 0e 21 0e 8a 0e 0a 0e 32 0e 99 0e 02 0e 99 0e 32 0e 7c 0e 70 07 02 0e 9f 0e 48 0e 48 0e 02 0e 51 0e 0a 0e 7c 07 02 0e 48 0e 02 0e 09 0e 7c 0e 8a 0e 32 0e 91 07 02 0e 70 0e 32 0e 91 0e 32 07 02 0e 9f 0e 99 07 02 0e 02 0e 48 0e 32 0e 89 0e 9f 0e 32 0e 7e 07 02 0e 21 0e a6 07 02 0e 21 0e 02 07 02 0e 0a 0e 7c 0e 51 0e 51 0e 7c 0e 1a 0e 7c 07 02 0e 0a 0e 7c 0e 70 0e 91 0e 21 0e 89 0e 9f 0e 02 0e 99 07 33 07 02 0e 1a 0e 9f 0e 32 0e 91 07 02 0e 02 0e 9f 0e 99 0e 21 07 02 0e 32 0e 8a 0e 9f 0e 8a 0e 21 07 02 0e 1a 0e 7c 0e 48 0e 7c 0e 8a 07 02 0e 32 0e 70 07 02 0e 8a 0e 21 0e 7e 0e 8a 0e 21 0e 2c 0e 21 0e 70 0e 1a 0e 21 0e 8a 0e 32 0e 99 07 02 0e 32 0e 70 07 02 0e a2 0e 7c 0e 48 0e 9f 0e 7e 0e 99 0e 02 0e 99 0e 21 07 02 0e a2 0e 21 0e 48 0e 32 0e 99 07 02 0e 21 0e 91 0e 91 0e 21 07 02 0e 0a 0e 32 0e 48 0e 48 0e 9f 0e 51 07 02 0e 1a 0e 7c 0e 48 0e 7c 0e 8a 0e 21 07 02 0e 21 0e 9f 07 02 0e 23 0e 9f 0e 25 0e 32 0e 02 0e 99 07 02 0e 70 0e 9f 0e 48 0e 48 0e 02 07 02 0e 7e 0e 02 0e 8a 0e 32 0e 02 0e 99 0e 9f 0e 8a 07 33 07 02 0e 21 0e a6 0e 0a 0e 21 0e 7e 0e 99 0e 21 0e 9f 0e 8a 07 02 0e 91 0e 32 0e 70 0e 99 07 02 0e 7c 0e 0a 0e 0a 0e 02 0e 21 0e 0a 0e 02 0e 99 07 02 0e 0a 0e 9f 0e 7e 0e 32 0e 1a 0e 02 0e 99 0e 02 0e 99 07 02 0e 70 0e 7c 0e 70 07 02 0e 7e 0e 8a 0e 7c 0e 32 0e 1a 0e 21 0e 70 0e 99 07 2f 07 02 0e 91 0e 9f 0e 70 0e 99 07 02 0e 32 0e 70 07 02 0e 0a 0e 9f 0e 48 0e 7e 0e 02 07 02 0e 89 0e 9f 0e 32 07 02 0e 7c 0e 23 0e 23 0e 32 0e 0a 0e 32 0e 02 07 02 0e 1a 0e 21 0e 91 0e 21 0e 8a 0e 9f 0e 70 0e 99 07 02 0e 51 0e 7c 0e 48 0e 48 0e 32 0e 99 07 02 0e 02 0e 70 0e 32 0e 51 07 02 0e 32 0e 1a 07 02 0e 21 0e 91 0e 99 07 02 0e 48 0e 02 0e 09 0e 7c 0e 8a 0e 9f 0e 51 07 33 01 01 12 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 12 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 12 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 12 01 01 81 fb 06 82 00

    but on Windows 7 the output is wrong:

    0e 48 0e 7c 0e 8a 0e 21 0e 51 07 02 0e 32 0e 7e 0e 91 0e 9f 0e 51 07 02 0e 1a 0e 7c 0e 48 0e 7c 0e 8a 07 02 0e 91 0e 32 0e 99 07 02 0e 02 0e 51 0e 21 0e 99 07 2f 07 02 0e 0a 0e 7c 0e 70 0e 91 0e 21 0e 0a 0e 99 0e 21 0e 99 0e 9f 0e 8a 07 02 0e 02 0e 1a 0e 32 0e 7e 0e 32 0e 91 0e 32 0e 0a 0e 32 0e 70 0e 25 07 02 0e 21 0e 48 0e 32 0e 99 07 2f 07 02 0e 91 0e 21 0e 1a 07 02 0e 1a 0e 7c 07 02 0e 21 0e 32 0e 9f 0e 91 0e 51 0e 7c 0e 1a 07 02 0e 99 0e 21 0e 51 0e 7e 0e 7c 0e 8a 07 02 0e 32 0e 70 0e 0a 0e 32 0e 1a 0e 32 0e 1a 0e 9f 0e 70 0e 99 07 02 0e 9f 0e 99 07 02 0e 48 0e 02 0e 09 0e 7c 0e 8a 0e 21 07 02 0e 21 0e 99 07 02 0e 1a 0e 7c 0e 48 0e 7c 0e 8a 0e 21 07 02 0e 51 0e 02 0e 25 0e 70 0e 02 07 02 0e 02 0e 48 0e 32 0e 89 0e 9f 0e 02 07 33 07 02 08 14 07 02 0e 9f 0e 99 07 02 0e 21 0e 70 0e 32 0e 51 07 02 0e 02 0e 1a 07 02 0e 51 0e 32 0e 70 0e 32 0e 51 07 02 0e a2 0e 21 0e 70 0e 32 0e 02 0e 51 07 2f 07 02 0e 89 0e 9f 0e 32 0e 91 07 02 0e 70 0e 7c 0e 91 0e 99 0e 8a 0e 9f 0e 1a 07 02 0e 21 0e a6 0e 21 0e 8a 0e 0a 0e 32 0e 99 0e 02 0e 99 0e 32 0e 7c 0e 70 07 02 0e 9f 0e 48 0e 48 0e 02 0e 51 0e 0a 0e 7c 07 02 0e 48 0e 02 0e 09 0e 7c 0e 8a 0e 32 0e 91 07 02 0e 70 0e 32 0e 91 0e 32 07 02 0e 9f 0e 99 07 02 0e 02 0e 48 0e 32 0e 89 0e 9f 0e 32 0e 7e 07 02 0e 21 0e a6 07 02 0e 21 0e 02 07 02 0e 0a 0e 7c 0e 51 0e 51 0e 7c 0e 1a 0e 7c 07 02 0e 0a 0e 7c 0e 70 0e 91 0e 21 0e 89 0e 9f 0e 02 0e 99 07 33 07 02 0e 1a 0e 9f 0e 32 0e 91 07 02 0e 02 0e 9f 0e 99 0e 21 07 02 0e 32 0e 8a 0e 9f 0e 8a 0e 21 07 02 0e 1a 0e 7c 0e 48 0e 7c 0e 8a 07 02 0e 32 0e 70 07 02 0e 8a 0e 21 0e 7e 0e 8a 0e 21 0e 2c 0e 21 0e 70 0e 1a 0e 21 0e 8a 0e 32 0e 99 07 02 0e 32 0e 70 07 02 0e a2 0e 7c 0e 48 0e 9f 0e 7e 0e 99 0e 02 0e 99 0e 21 07 02 0e a2 0e 21 0e 48 0e 32 0e 99 07 02 0e 21 0e 91 0e 91 0e 21 07 02 0e 0a 0e 32 0e 48 0e 48 0e 9f 0e 51 07 02 0e 1a 0e 7c 0e 48 0e 7c 0e 8a 0e 21 07 02 0e 21 0e 9f 07 02 0e 23 0e 9f 0e 25 0e 32 0e 02 0e 99 07 02 0e 70 0e 9f 0e 48 0e 48 0e 02 07 02 0e 7e 0e 02 0e 8a 0e 32 0e 02 0e 99 0e 9f 0e 8a 07 33 07 02 0e 21 0e a6 0e 0a 0e 21 0e 7e 0e 99 0e 21 0e 9f 0e 8a 07 02 0e 91 0e 32 0e 70 0e 99 07 02 0e 7c 0e 0a 0e 0a 0e 02 0e 21 0e 0a 0e 02 0e 99 07 02 0e 0a 0e 9f 0e 7e 0e 32 0e 1a 0e 02 0e 99 0e 02 0e 99 07 02 0e 70 0e 7c 0e 70 07 02 0e 7e 0e 8a 0e 7c 0e 32 0e 1a 0e 21 0e 70 0e 99 07 2f 07 02 0e 91 0e 9f 0e 70 0e 99 07 02 0e 32 0e 70 07 02 0e 0a 0e 9f 0e 48 0e 7e 0e 02 07 02 0e 89 0e 9f 0e 32 07 02 0e 7c 0e 23 0e 23 0e 32 0e 0a 0e 32 0e 02 07 02 0e 1a 0e 21 0e 91 0e 21 0e 8a 0e 9f 0e 70 0e 99 07 02 0e 51 0e 7c 0e 48 0e 48 0e 32 0e 99 07 02 0e 02 0e 70 0e 32 0e 51 07 02 0e 32 0e 1a 07 02 0e 21 0e 91 0e 99 07 02 0e 48 0e 02 0e 09 0e 7c 0e 8a 0e 9f 0e 51 07 33 01 01 12 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 12 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 12 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 12 01 01 ff 82 82 12 00

    This is what I meant by the Spot the difference between these two pictures game. Who wants to sit there and compare two 900-byte sequences byte-by-byte? (On top of that, the customer liaison forwarded the question to an internal peer-to-peer discussion group. Peer-to-peer discussion groups do not come with a service level agreement, and if you ask a question which makes heavy demands on the reader just to understand the question, don't be surprised if nobody answers.)

    If you want an explanation of why two things are different, you need to point out where the difference is. This often happens with screen shots, where a customer includes two different screen shots and asks for an explanation of why they are different, without indicating where the difference is. Are they complaining that the font is different? That ClearType is enabled in one screen shot but not the other? The hard drive in the first screen shot has more free space than the one in the second screen shot?

    Anyway, back to the sort keys. (As a refresher, you can read Michael Kaplan's explanation of sort keys.) The documentation for LCMap­String says

    dwMapFlags [in]

    Flags specifying the type of transformation to use during string mapping or the type of sort key to generate. For detailed definitions, see the dwMapFlags parameter of LCMapStringEx.

    If you follow the instructions and consult the dwMapFlags parameter of the LCMapStringEx function, you find

    LCMAP_SORTKEY Produce a normalized sort key. If the LCMAP_SORTKEY flag is not specified, the function performs string mapping. For details of sort key generation and string mapping, see the Remarks section.

    If you follow the instructions and go to the Remarks section looking for LCMAP_SORTKEY, you find

    The application can use LCMapString or LCMapStringEx to generate a sort key. To do this, the application specifies LCMAP_SORTKEY for the dwMapFlags parameter. For more information, see Handling Sorting in Your Applications.

    (A link to Handling Sorting in Your Applications also exists on the LCMapString page, but let's say you didn't see that.)

    If you follow the instructions and read the Handling Sorting in Your Applications page,

    Use Sort Versioning

    A sorting table has two numbers that identify its version: the defined version and the NLS version. Both numbers are DWORD values, composed of a major value and a minor value. ...

    Note For a major version, one or more code points are changed so that the application must re-index all data for comparisons to be valid. For a minor version, nothing is moved but code points are added. For this type of version, the application only has to re-index strings with previously unsortable values.

    And there you have your answer. Sort keys generated by different major versions are not compatible. I don't know what NLS version for English was included with Windows XP, though I did check that on Windows Vista SP2 it is major version 0x0405 (minor version 0) and on Windows 7 it is major version 0x601 (minor version 1). So whatever it was on Windows XP, it was presumably less than or equal to 0x0405, which means that it's definitely different from the major version on Windows 7.

    And like the documentation says, when the major version changes, you need to regenerate your sort keys because sort keys from different major versions are not compatible.

    Michael Kaplan discussed other issues arising from changing the major version as well as various changes to the sort keys over time.

    If you find yourself using a program that relies on sort keys remaining stable even across major versions, you can try applying the EmulateSorting compatibility fix.

  • The Old New Thing

    How do I make it so that users can copy static text on a dialog box to the clipboard easily?

    • 20 Comments

    Given that you have a Win32 dialog box with static text in an LTEXT control, how do you make it so that users can easily copy that text to the clipboard?

    The traditional solution is to create a borderless read-only edit control (which draws as static text by default). Add it to the tab order by setting the WS_TABSTOP style, and maybe even give it a keyboard accelerator for accessibility.

    Starting in Windows Vista, version 6 of the common controls provides an alternative. (A less accessible alternative, mind you.) Static text controls automatically copy their contents to the clipboard when you double-click them if you set the SS_NOTIFY style.

    Let's try it:

    #include <windows.h>
    #include <windowsx.h>
    #include <commctrl.h>
    
    #pragma comment(linker, \
     "\"/manifestdependency:type='Win32' "\
     "name='Microsoft.Windows.Common-Controls' "\
     "version='6.0.0.0' "\
     "processorArchitecture='*' "\
     "publicKeyToken='6595b64144ccf1df' "\
     "language='*'\"")
    
    INT_PTR CALLBACK DlgProc(
        HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
     switch (uMsg) {
     case WM_INITDIALOG:
      return TRUE;
     case WM_COMMAND:
      switch (GET_WM_COMMAND_ID(wParam, lParam)) {
      case IDCANCEL:
       EndDialog(hdlg, 0);
       break;
      }
     }
     return FALSE;
    }
    
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       LPSTR lpCmdLine, int nShowCmd)
    {
     return DialogBox(hinst, MAKEINTRESOURCE(1), NULL, DlgProc);
    }
    
    // resource file
    #include <windows.h>
    
    1 DIALOG 50, 50, 100, 50
    STYLE DS_SHELLFONT | WS_SYSMENU
    CAPTION "Sample"
    FONT 8, "MS Shell Dlg"
    BEGIN
     LTEXT "Sample text 1",100,10,10,80,10,SS_NOTIFY
     LTEXT "Sample text 2",101,10,20,80,10,SS_NOTIFY
     LTEXT "Sample text 3",102,10,30,80,10
    END
    

    Run this program and double-click on the text controls, and observe that the text gets copied to the clipboard, or at least it does for the first two, since I set the SS_NOTIFY style on them.

    Now, when the double-click to copy feature was added to the static control, there was no way to suppress it. The STN_DBLCLK notification is documented as ignoring its return code, so it would be a compatibility problem if suddenly it started studying its return code so that the parent could respond "No, I handled the click, don't do your default action." Instead, if you want to disable the double-click to copy feature on a SS_NOTIFY static control, you have to subclass the static control and eat the clicks yourself.

    LRESULT CALLBACK SuppressCopyOnClick(
        HWND hwnd, UINT uMsg, WPARAM wParam,
        LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
    {
     switch (uMsg) {
     case WM_LBUTTONDBLCLK: return 0; // eat the double-click
     case WM_NCDESTROY:
      RemoveWindowSubclass(hwnd, SuppressCopyOnClick, uIdSubclass);
      break;
     }
     return DefSubclassProc(hwnd, uMsg, wParam, lParam);
    }
    
    INT_PTR CALLBACK DlgProc(
        HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
     switch (uMsg) {
     case WM_INITDIALOG:
      SetWindowSubclass(GetDlgItem(hdlg, 101),
                        SuppressCopyOnClick, 0, 0);
      return TRUE;
     ...
    

    "Why would you add a style that enabled a feature, and then disable the feature?"

    Maybe you want some other aspects of the feature but not the copy-on-double-click behavior. Maybe somebody else is adding the SS_NOTIFY style behind your back. For example, a UI framework might add it automatically to all static controls.

    And actually, in that UI framework case, you probably want the STN_DBLCLK notification to be fired when a double-click occurs, because you added an OnDoubleClick handler to your class. You just don't want the copy-to-clipboard behavior. We can fix that by firing the notification in our subclass procedure.

     case WM_LBUTTONDBLCLK:
      if (GetWindowStyle(hwnd) & SS_NOTIFY) {
       FORWARD_WM_COMMAND(GetParent(hwnd), GetDlgCtrlID(hwnd), hwnd,
                          STN_DBLCLK, SendMessage);
      }
      return 0; // message handled
    

    To illustrate this change, we'll make our dialog box beep when it gets a double-click notification. In real life, of course, you would do whatever you want to happen on the "double click on a static control" event. Actually, in real life, the code that responds to the STN_DBLCLK lives inside your framework, and it turns around and raises an OnDoubleClick event, but for simplicity, we'll just code it inline.

     case WM_COMMAND:
         switch (GET_WM_COMMAND_ID(wParam, lParam)) {
         case IDCANCEL:
            EndDialog(hdlg, 0);
            break;
         case 100:
         case 101:
         case 102:
          switch (GET_WM_COMMAND_CMD(wParam, lParam)) {
          // Obviously we would do something more interesting here
          case STN_DBLCLK: MessageBeep(MB_OK); break;
          }
         }
    

    Each of the static controls on the dialog behaves differently. The first one is SS_NOTIFY with no subclassing, so double-clicking copies the text to the clipboard and also beeps. The second one is SS_NOTIFY with subclassing to disable the copy-to-clipboard, so double-clicking merely beeps. And the third one doesn't have the SS_NOTIFY style at all, so it neither copies the next nor responds to double-click.

Page 3 of 3 (24 items) 123