• The Old New Thing



    You may have noticed that the numerical value of the DS_SHELLFONT flag is equal to DS_FIXEDSYS | DS_SETFONT.

    #define DS_SETFONT          0x40L   /* User specified font for Dlg controls */
    #define DS_FIXEDSYS         0x0008L

    Surely that isn't a coincidence.

    The value of the DS_SHELLFONT flag was chosen so that older operating systems (Windows 95, 98, NT 4) would accept the flag while nevertheless ignoring it. This allowed people to write a single program that got the "Windows 2000" look when running on Windows 2000 and got the "classic" look when running on older systems. (If you make people have to write two versions of their program, one that runs on all systems and one that runs only on the newer system and looks slightly cooler, they will usually not bother writing the second one.)

    The DS_FIXEDSYS flag met these conditions. Older systems accepted the flag since it was indeed a valid flag, but they also ignored it because the DS_SETFONT flag takes precedence.

    This is one of those backwards-compatibility exercises: How do you design something so that it is possible to write one program that gets the new features on new systems while at the same time degrading gracefully on old systems?

  • The Old New Thing

    What's the deal with the DS_SHELLFONT flag?


    It indicates that you want the Windows 2000 default shell font. But that doesn't mean that you're going to get it.

    In order to indicate that you would like the "Windows 2000" look for your dialog, you have to do three things and hope for a fourth:

    1. Use a DIALOGEX template instead of a DIALOG template.
    2. Set the DS_SHELLFONT flag in your dialog styles.
    3. Set the dialog font to "MS Shell Dlg".
    4. Hope that you're running on Windows 2000 or later on a system that has the new Windows 2000 dialog font enabled.

    If all four conditions are satisfied, then your dialog gets the "Windows 2000" look.

    If any condition fails, then you get the "classic" dialog font. Note that the fourth condition is not within your program's control. Consequently, you have to make sure your dialog looks good in both the "classic" dialog font and the new one.

    For property sheet pages, things are more complicated.

    It would be visually jarring for there to be a mix of fonts on a property sheet. You wouldn't want the "Advanced" button to be in MS Sans Serif but the "Apply" button in Tahoma. To avoid this problem, the property sheet manager looks at all the pages in the property sheet. If they are all using the "Windows 2000" look, then the property sheet uses the "Windows 2000" look also. But if there is even a single page that does not use the "Windows 2000" look, then the property sheet reverts to the "classic" look and also converts all the "Windows 2000"-look pages to "classic" look.

    This way, all the pages in the property sheet have the "classic" look instead of having a mishmash of some pages with the classic look and some with the Windows 2000 look.

    That's why you will occasionally find that a shell property sheet has reverted to the "classic" look. Some shell extension infected the property sheet with a page that does not have the "Windows 2000" look, and for the sake of visual consistency, the property sheet manager set all the pages on the property sheet to "classic" look.

    This is another reason it is important that you test your property sheet pages both with the "Windows 2000" look and the "classic" look. If your property sheet page ends up sharing a property sheet with a non-"Windows 2000"-look page, your page is going to be reverted to "classic" look.

  • The Old New Thing

    Why is breadth-first searching better for file system tree walking?


    Earlier, Eric Lippert discussed one scenario where breadth-first searching is better than depth-first searching. Today, I'll tell you about another.

    If you go back to the old MS-DOS file enumeration functions, you'll find that there is a "Find first file" function and a "Find next file" function, but no "Find close" function. That's because the MS-DOS file enumeration functions maintained all their state in the find structure. The FAT file system was simple enough that the necessary search state fit in the reserved bytes and no external locking was necessary to accomplish the enumeration. (If you did something strange like delete a directory while there was an active enumeration on it, then the enumeration would start returning garbage. It was considered the program's responsibility not to do that. Life is a lot easier when you are a single-tasking system.)

    However, moving these functions to a network protocol or a non-FAT file system created problems. When enumerating files over a network, the server needs to allocate additional memory to track the enumeration, memory which it needs to free when the enumeration is complete. Similarly, non-FAT file systems may have additional state to track that (1) doesn't fit in the reserved space in the find structure, (2) shouldn't be stored in the reserved space (because it would compromise security), or (3) can't be stored in the reserved space (because the kernel needs to update it asynchronously when the state of the directory changes).

    Since MS-DOS has no "Find close" function, how do these alternate file systems know when it is safe to free the memory associated with the file search? [Context clarified - 10am] Clairevoyance is not yet a viable option, so the file system is forced to guess.

    Typically, a file enumeration is considered "abandoned" if it is not used for "a long time" or if too many pending file enumerations are in progress.

    Let's see how depth-first search compares to breadth-first search on these points.

    In a typical depth-first search, you recurse into a subdirectory as soon as you see it. Thus, if you are, say, six levels deep in recursion, you will have six active enumerations, one for each level. The most recent one is most active, and the one at the root is least active. This means that the enumeration at the root is likely to be considered "abandoned" by the server because it hasn't been used for the longest time, and because there is a lot of other enumeration activity going on in the meantime.

    On the other hand, with a breadth-first search, when a directory is found, it is not processed immediately but is rather put on a list for later consideration. Consequently, there is only one active enumeration, and it runs to completion before the next enumeration begins. This means that the enumeration is unlikely to be considered "abandoned" by the server.

    This problem is not purely theoretical. If you performed a depth-first search on a large directory tree on a network server from a MS-DOS program, you often found that the enumeration ended prematurely because the older enumerations became abandoned by the server. You also saw this from a Win32 program if you were enumerating against an older network server that was designed for MS-DOS clients (and which therefore doesn't implement FindClose).

    You can still use depth-first enumeration while avoiding the abandonment problem. Instead of recursing into a directory immediately after enumerating it, save it on a list. When you are finished enumerating the files in a directory, go back and process the list. You're still enumerating depth-first, but you are delaying the recursive call until after the directory enumeration has completed.

    Explorer uses this "delayed recursion" trick to avoid the problem of prematurely-terminated enumerations when walking through directories on these old servers.

  • The Old New Thing

    The history of the Windows PowerToys


    During the development of Windows 95, as with the development of any project, the people working on the project write side programs to test the features they are adding or to prototype a feature. After Windows 95 shipped, some of those programs were collected into the first edition of the Windows 95 Power Toys.

    As I recall, the first edition contained the following toys:

    This was a handy internal tool which also served as a test of the shell folder design.

    CDAutoPlay, DeskMenu, FlexiCD, QuickRes
    These were side toys originally written by shell developers for their own personal use.

    Command Prompt Here, Explore From Here
    These were proof-of-concept toys which tested the shell command extension design.

    Round Clock
    This was a program to test regional windows.

    Shortcut Target Menu
    This was a feature idea that didn't quite make it.

    I wasn't around when the decision was made to package these toys up and ship them, so I don't know what the rule was for deciding what was PowerToy-worthy and what wasn't. Nor do I know where the name PowerToy came from. (Probably somebody just made it up because it sounded neat.)

    Upon the enormous success of the PowerToys, a second edition was developed. This time, people knew that they were writing a PowerToy, as opposed to the first edition of the PowerToys which was merely cobbled together from stuff lying around. The second edition of the Windows 95 PowerToys added FindX, Send To X, the Telephony Locator Selector, XMouse, and Tweak UI.

    Later, the kernel team released their own set of toys, known as the Windows 95 Kernel Toys. Alas, the original blurb text is not on the Microsoft downloads site, but here's an archived copy. (In reality, it was I who wrote all of the Kernel Toys, except for the Time Zone Editor, which came from the Windows NT Resource Kit. I also wrote the somewhat whimsical original blurb.)

    This was all back in the day when it was easy to put up something for download. No digital signatures, no virus checking, no paperwork. Just throw it up there and watch what happens. Today, things are very different. Putting something up for download is a complicated process with forms to fill out in triplicate and dark rooms with card readers. I wouldn't be surprised if an abandoned salt mine in Montana were somehow involved.

    Nowadays, every team at Microsoft seems to have their own PowerToys, trading on the good name of the Windows shell team who invented the whole PowerToys idea. (As far as I can tell, we don't get any royalties from other divisions calling their toys "PowerToys".) A quick check reveals the following PowerToys available for download from Microsoft; I may have missed some.

    (Plus, of course, the Windows XP PowerToys, which does come from the shell team. The Internet Explorer team originally called their stuff PowerToys, but they later changed the name to Web Accessories, perhaps to avoid the very confusion I'm discussing here.)

    What's frustrating is that since they are all called "PowerToys", questions about them tend to go to the shell team, since we are the ones who invented PowerToys. We frequently have to reply, "Oh, no, you're having a problem with the XYZ PowerToys, not the classic Windows PowerToys. We're the folks who do the classic Windows PowerToys."

    Even the blog name "PowerToys" has been co-opted by the Visual Studio team to promote their Powertoys for Visual Studio 2003.

    Some people claim that Tweak UI was written because Microsoft got tired of responding to customer complaints. I don't know where they got that from. Tweak UI was written because I felt like writing it.

    That page also says that sometimes PowerToys vanish without warning. That's true. A few years ago, all the Windows XP PowerToys were taken down so they could be given a security review. Some of them didn't survive and didn't come back. Other times, a PowerToy will be pulled because a serious bug was found. Since PowerToys are spare-time projects, it can take a very long time for a bug to get fixed, tested, and re-published. For example, the HTML Slide Show Wizard was pulled after a (somewhat obscure) data-loss bug was found. Fixing the bug itself took just a few days, but testing and filling out all the associated paperwork took six months.

    There's no moral to this story. Just a quick history lesson.

  • The Old New Thing

    How to detect programmatically whether you are running on 64-bit Windows


    To detect programmatically whether your 32-bit program is running on 64-bit Windows, you can use the IsWow64Process function.

    Do not do as some people do and hard-code the list of 64-bit processors. You'd think that after the hard-coded list of 64-bit processors changed the first time (when x64 was added to ia64), people would have learned their lesson.

    But how do you detect programmatically from your 64-bit process whether you are running on 64-bit Windows? Easy.

    BOOL Is64BitProcessRunningOn64BitWindows()
     return TRUE;

    The fact that your 64-bit program is running at all means that you are running on 64-bit Windows! If it were a 32-bit machine, your program wouldn't be able to run.

    It's like asking the question, "Is the power on?" If there were no power, your program wouldn't be able to ask the question.

    Of course, if you want a single source code base that can be compiled both as a 32-bit program and as a 64-bit program, you have a tiny amount of work to do.

    BOOL Is64BitWindows()
    #if defined(_WIN64)
     return TRUE;  // 64-bit programs run only on Win64
    #elif defined(_WIN32)
     // 32-bit programs run on both 32-bit and 64-bit Windows
     // so must sniff
     BOOL f64 = FALSE;
     return IsWow64Process(GetCurrentProcess(), &f64) && f64;
     return FALSE; // Win64 does not support Win16

    I threw in a branch for 16-bit programs if you're crazy enough to be still writing 16-bit Windows programs.

  • The Old New Thing

    Why did the Win64 team choose the LLP64 model?


    Over on Channel 9, member Beer28 wrote, "I can't imagine there are too many problems with programs that have type widths changed." I got a good chuckle out of that and made a note to write up an entry on the Win64 data model.

    The Win64 team selected the LLP64 data model, in which all integral types remain 32-bit values and only pointers expand to 64-bit values. Why?

    In addition to the reasons give on that web page, another reason is that doing so avoids breaking persistence formats. For example, part of the header data for a bitmap file is defined by the following structure:

    typedef struct tagBITMAPINFOHEADER {
            DWORD      biSize;
            LONG       biWidth;
            LONG       biHeight;
            WORD       biPlanes;
            WORD       biBitCount;
            DWORD      biCompression;
            DWORD      biSizeImage;
            LONG       biXPelsPerMeter;
            LONG       biYPelsPerMeter;
            DWORD      biClrUsed;
            DWORD      biClrImportant;

    If a LONG expanded from a 32-bit value to a 64-bit value, it would not be possible for a 64-bit program to use this structure to parse a bitmap file.

    There are persistence formats other than files. In addition to the obvious things like RPC and DCOM, registry binary blobs and shared memory blocks can also be used to transfer information between processes. If the source and destination processes are different bitness, any change to the integer sizes would result in a mismatch.

    Notice that in these inter-process communication scenarios, we don't have to worry as much about the effect of a changed pointer size. Nobody in their right mind would transfer a pointer across processes: Separate address spaces mean that the pointer value is useless in any process other than the one that generated it, so why share it?

  • The Old New Thing

    Capturing the current directory from a batch file


    Sometimes people go to great lengths to get information which is available in a much simpler way. We saw it a few days ago when we found a 200+-line C# program that could be replaced with a 90-byte batch file. Here's another example of a rather roundabout way of capturing the current directory from a batch file.

    The easy way is to use the %CD% pseudo-variable. It expands to the current working directory.

    set OLDDIR=%CD%
    .. do stuff ..
    chdir /d %OLDDIR% &rem restore current directory

    (Of course, directory save/restore could more easily have been done with pushd/popd, but that's not the point here.)

    The %CD% trick is handy even from the command line. For example, I often find myself in a directory where there's a file that I want to operate on but... oh, I need to chdir to some other directory in order to perform that operation.

    set _=%CD%\curfile.txt
    cd ... some other directory ...
    somecommand args %_% args

    (I like to use %_% as my scratch environment variable.)

    Type SET /? to see the other pseudo-variables provided by the command processor.

  • The Old New Thing

    Control how much network bandwith Automatic Updates will use


    By default, the Background Intelligent Transfer Service (BITS) which is used by Automatic Updates will use idle network bandwidth for downloading updates. This is normally not a problem.

    One case where it can be a problem is you have a large LAN that shares a single DSL connection. BITS doesn't see that that DSL connection is shared. Consequently, each computer on the LAN will be using its idle network bandwidth to download updates and the total of all the LAN computers doing this will oversaturate the DSL connection. [Typo fixed. 31-Jan-05.]

    Another example where this can be a problem is if you have a network card that connects to a hardware firewall which in turn uses a dial-up modem to connect to the Internet. (For example, you might connect through a classic Apple AirPort which is in turn connected to a modem.) BITS sees your fast network card and can't see that there is a bottleneck further downstream. As a result, it oversaturates the dial-up connection.

    To tweak the BITS settings, you can fire up the Group Policy Editor by typing "gpedit.msc" into the Run dialog. From there, go to Computer Configuration, Administrative Templates, Network, then Background Intelligent Transfer Service. From there you can configure the maximum network bandwidth that BITS will use. You can even specify different BITS download rates based on time of day, so that it downloads more aggressively while you're sleeping, for example.

  • The Old New Thing

    The strangest way of rounding down to the nearest quarter


    In a previous life, I wrote database software. A customer complained that one of their reports was taking an unacceptably long amount of time to generate, and I was asked to take a look at it even though it wasn't my account.

    The report was a vacation-days report, listing the number of vacation days taken and available for each employee. Vacation days accrued at a fixed rate but were granted only in quarter-day increments. For example, if you earned 15 vacation days per year and the year was 32% complete, then you had accrued 32% × 15 = 4.8 vacation days, of which 4.75 were available to use.

    The existing code to round the number of accrued days down to the nearest quarter-day went something like this:

    * assume that at this point, ACCRUED is the number
    * of accrued days.
    * STR(ACCRUED,6,2) converts ACCRUED to a 6-character
    * string: 3 integer digits, a decimal point, and two
    * fractional digits.  Excess fractional digits are rounded.
    STORE RIGHT(S,2) TO F        && extract digits after decimal
    IF F < "25"
     F = "00"                    && 00 to 24 becomes 00
     IF F < "50"
      F = "25"                   && 25 to 49 becomes 25
      IF F < "75"
       F = "50"                  && 50 to 74 becomes 50
       F = "75"                  && 75 to 99 becomes 75
    ROUNDED = VAL(LEFT(S,4) + F) && reconstruct value and convert

    In other words, the code converted the number to a string, extracted the digits after the decimal point, did string comparisons to figure out which quartile the fraction resided in, then created a new string with the replacement fraction and converted that string back to a number. And all this in an interpreted language.

    This code fragment was repeated each time rounding-down was needed because the language supported only 32 subroutines, and this procedure wasn't important enough to be worth kicking out one of the other existing subroutines.

    I replaced this seventeen-line monstrosity with the one-line equivalent each time it occurred, and the report ran much faster.

    (This is nowhere near the strangest way of implementing rounding. There are far worse examples.)

    Exercise: What is the one-line equivalent?

    Exercise: What is the double-rounding bug in the original code?

  • The Old New Thing

    Why do files and directories with no time/date mess up sorting in Explorer?


    If you have a file or directory that does not have a last-modified date, you may find that it causes Explorer to sort very strangely. (How do you get a file or directory with no last-modifiied date? It's hard to do; you need the help of an external file system.) Why is this?

    As we learned earlier, a sort comparison function must impose a total order in order to produce consistent results. The problem is that Explorer's comparison function mis-handles files and directories with no last-modified date.

    To get some more of the background behind this, you need to know about so-called "simple pidls". A simple pidl is an item ID list that refers to a file or directory that does not actually exist.

    The problem is that a valid file or directory with no last-modified date looks just like one of these simple pidls becauses Explorer uses the last-modified date to distinguish whether it is manipulating a real pidl or a simple one.

    The problems with sorting occur when it comes time to decide where in the list these "real pidls that are mistaken for simple pidls" go into the sorted list. Explorer tries to keep all folders together, but if it sees a "simple pidl" it can't tell whether that item is a folder or a file (after all, something that doesn't exist is neither a file nor a folder) and it ends up producing inconsistent comparison results.

    Moral of the story: Be careful with your sort functions. If you produce inconsistent results in your sort function, you will get inconsistent results in your "sorted" output.

Page 381 of 446 (4,453 items) «379380381382383»