• The Old New Thing

    A discovered quirk is just few steps away from becoming a feature

    • 24 Comments

    Commenter Cherry wonders who invented all those strange syntaxes, like set " to show all environment variables, including the hidden ones.

    An interesting historical note is the origin of the convention in unix that files whose names begin with a dot are hidden by default (here's the relevant portion). That article highlights how a discovered quirk is just a few steps away from becoming a feature.

    As Master Yoda might put it: Discovery leads to dissemination. Dissemination leads to adoption. Adoption leads to entrenchment. Entrenchment creates a compatibility constraint.

    As I've noted many times, the batch language was not designed. It simply evolved out of the old CP/M program SUBMIT, which was an even more rudimentary batch processor. (The original SUBMIT.COM didn't have conditional branches. It merely ran every line in your batch file one after another.)

    One of the consequences of something that old is that any quirk, once discovered, can turn into a feature, and from there it becomes a support burden and compatibility constraint. We've seen this many times before: Counting the number of lines in a file by exploiting a buffer underflow bug in FIND.COM. Update the last-modified time of a file by using a magic sequence of punctuation marks. Echoing a blank line by typing ECHO.. All of these were accidental discovered behaviors (just like unix dot files) which became entrenched. Even when the underlying program was completely rewritten, these special quirks had to be specifically detected and painstakingly reproduced because so many programs (i.e., batch files) relied on them.

    For set ", it's a case of taking advantage of two quirks in the implementation: The first quirk is that a missing close-quotation mark is forgiven. That means that set " is logically equivalent to set "".

    You are therefore asking for a filtered list of environment variables, but passing the logical equivalent of no filter. Specifically, you're asking for all environment variables which begin with the empty string, and it so happens that every string begins with the empty string. The second quirk is that when an explicit filter is applied, the set command disables its default filter of "Hide environment variables whose names begin with an equals sign."

    In other words, the code goes like this:

    foreach (var entry in Environment.GetEnvironmentVariables()) {
     if (prefixFilter != null ?
         entry.Key.StartsWith(prefixFilter) :
         !entry.Key.StartsWith("=")) {
      Console.WriteLine("{0}={1}", entry.Key, entry.Value);
     }
    }
    

    Perhaps this is a bug, and it should have been written like this:

    foreach (var entry in Environment.GetEnvironmentVariables()) {
     if (!entry.Key.StartsWith("=") &&
         (prefixFilter == null || entry.Key.StartsWith(prefixFilter))) {
      Console.WriteLine("{0}={0}", entry.Key, entry.Value);
     }
    }
    

    But it's too late to fix it now. People have discovered the quote trick, so it's now a feature and therefore a compatibility constraint.

  • The Old New Thing

    I thought you could use SWP_FRAMECHANGED to force a control to recalculate its properties after a change in styles

    • 7 Comments

    Simon Cooke dug back into his memory and asked, "Doesn't calling Set­Window­Pos with SWP_FRAME­CHANGED cause a recreate and re-apply of the styles?"

    The SWP_FRAME­CHANGED flag does not recreate anything, but it does reapply the styles, as far as it knows.

    Recall that the bits in the window style break into two parts. There are the styles managed by the window manager, which are in the upper 16 bits, and there are the styles that are specific to each control, which are in the lower 16 bits.

    The window manager knows about the styles that it manages, but it has no clue about the styles that are specific to each control. It has no idea that the MCIWNDF_NO­PLAY­BAR style controls the toolbar in an MCI window, or that the ES_RIGHT style controls the alignment of text in an edit control.

    The SWP_FRAME­CHANGED flag tells the window manager, "Hey, I changed some styles that affect the non-client area of the window (the window frame). Could you go and re-read those styles and apply them to the window? Thanks." That's sort of implied in the name: "Frame changed."

    If you want a control to re-inspect the window styles and adjust its behavior in response, you need to do something control-specific. The control might have a custom message you can send it to say, "Hey, I changed some styles that afect the client area of the window. Could you go and re-read those styles and apply them to the window? Thanks." Or there may be special messages specifically for changing styles, such as EM_SET­READ­ONLY. The fancier windows may do it automatically on receipt of the WM_STYLE­CHANGED messages.

  • The Old New Thing

    How do I programmatically create folders like My Pictures if they were manually deleted?

    • 10 Comments

    A corporate customer had a problem with their employees accidentally deleting folders like Videos and Pictures and are looking for a way to restore them, short of blowing away the entire user profile and starting over. They found some techniques on the Internet but they don't always work consistently or completely. What is the recommended way of recreating these missing folders?

    It turns out that the customer was asking a question that I answered many years ago, but looking at it from the other side.

    To recreate a folder, call SHGet­Folder­Path with the flag CSIDL_FLAG_CREATE, or call SHGet­Special­Folder­Path and pass fCreate = TRUE.

    If you are targeting Windows Vista or higher, the known-folder equivalent is calling SHGet­Known­Folder­Path, SHGet­Known­Folder­ID­List, or SHGet­Known­Folder­Item with the KF_FLAG_CREATE flag.

    (There is a CSIDL-to-KF conversion table in MSDN.)

  • The Old New Thing

    Le Chatelier's Principle in action: Administrative overrides

    • 43 Comments

    Today we have another example of Le Chatelier's Principle as interpreted by John Gall: Every system resists its proper functioning.

    There was a video card manufacturer which was using the AppInit_DLLs key so that they could inject their DLL into every process. I have no idea why. Perhaps to get a nice bonus.

    In Windows Vista, the AppInit_DLLs registry key was deactivated for both engineering and security reasons. Oh no! Undeterred, the video card manufacturer issued an update to their driver so that in addition to adding themselves to AppInit_DLLs, they also set the administrative override switch that re-enabled the feature. Boom, they probably got a second bonus for that.

    Another lesson from this story is that if you provide an administrative override to restore earlier behavior, then you never really removed the earlier behavior. Since installers run with administrator privileges, they can go ahead and flip the setting that is intended to be set only by system administrators.

  • The Old New Thing

    How can I get information about the items in the Recycle Bin from script?

    • 7 Comments

    Today we'll do a scripting version of an old C++ program: Printing information about the items in the Recycle Bin. (How you wish to act on the information is up to you.)

    This is a pattern we've seen a lot. Bind to a folder, enumerate its contents, extract properties.

    var shell = new ActiveXObject("Shell.Application");
    var recycleBin = shell.NameSpace(10); // CSIDL_BITBUCKET
    var items = recycleBin.Items();
    for (var i = 0; i < items.Count; i++) {
      var item = items.Item(i);
      WScript.StdOut.WriteLine(item.Name);
      WScript.StdOut.WriteLine(item.ExtendedProperty(
                                    "System.Recycle.DeletedFrom"));
      WScript.StdOut.WriteLine(item.ExtendedProperty(
                                    "System.Recycle.DateDeleted"));
      WScript.StdOut.WriteLine(item.Size);
    }
    

    Wow, that was way easier than doing it in C++!

    Just for fun, I'll do it in C#, first as a straight port:

    // add a reference to shell32.dll
    class Program {
      public static void Main()
      {
        var shell = new Shell32.Shell();
        var recycleBin = shell.NameSpace(10); // CSIDL_BITBUCKET
        var items = recycleBin.Items();
        for (var i = 0; i < items.Count; i++) {
          var item = (Shell32.FolderItem2)items.Item(i);
          System.Console.WriteLine(item.Name);
          System.Console.WriteLine(item.ExtendedProperty(
                                        "System.Recycle.DeletedFrom"));
          System.Console.WriteLine(item.ExtendedProperty(
                                        "System.Recycle.DateDeleted"));
          System.Console.WriteLine(item.Size);
        }
      }
    }
    

    We have to cast to Shell32.Folder­Item2 because the default interface for the Item() method is Shell32.Folder­Item, but Extended­Property is a method on Shell32.Folder­Item2. We didn't have to do this explicit cast in JavaScript because JavaScript is a dynamically-typed language.

    So let's use the dynamic keyword to mimic that in C#. Note, however, that if you use dynamic, then you miss out on a lot of IntelliSense features.

    class Program {
      public static void Main()
      {
        var shell = new Shell32.Shell();
        var recycleBin = shell.NameSpace(10); // CSIDL_BITBUCKET
        var items = recycleBin.Items();
        foreach (dynamic item in items) {
          System.Console.WriteLine(item.Name);
          System.Console.WriteLine(item.ExtendedProperty(
                                        "System.Recycle.DeletedFrom"));
          System.Console.WriteLine(item.ExtendedProperty(
                                        "System.Recycle.DateDeleted"));
          System.Console.WriteLine(item.Size);
        }
      }
    }
    

    Now you can do things like list all the files deleted today

    class Program {
      public static void Main()
      {
        var today = DateTime.Today;
        var shell = new Shell32.Shell();
        var recycleBin = shell.NameSpace(10); // CSIDL_BITBUCKET
        var items = recycleBin.Items();
        foreach (dynamic item in items) {
          if (item.ExtendedProperty("System.Recycle.DateDeleted").Date
                                                           == today) {
            System.Console.WriteLine(item.name);
          }
        }
      }
    }
    
  • The Old New Thing

    Raymond's house rules for Easter Egg Hunts

    • 19 Comments

    One of my colleagues frustrates his family by hiding the eggs for the annual Egg Hunt way too well. "Apparently, drawers and freezers are out of bounds in the traditional egg hunt."

    Here are my house rules for Easter Egg Hunts:

    • All eggs must be hidden within the implied egg-hiding area. No sneaky outliers.
    • All eggs must be at least partially observable by egg-hunters without disturbing anything. No hiding in drawers or under flowerpots, or putting them on top of a tall piece of furniture that a shorter egg-hunter cannot see.
    • However, you may still have to work to see them. They might be behind a sofa or placed above eye level. For example, you might find an egg tucked between the slats of horizontal blinds.

    Personally, I like to hide eggs in plain sight. It's surprising how long it can take somebody to find a yellow egg resting brazenly on the lap of a yellow teddy bear.

  • The Old New Thing

    How do I set a breakpoint on a function whose name contains spaces or other special characters?

    • 3 Comments

    If you use one of the command line debuggers based on the Debugger Engine, you can set a breakpoint on a function whose name contains spaces or other special characters by quoting the symbol name. The trick here is that you do not quote the entire string; you quote only the symbol name.

    0:001> bu @!"CSimpleArray<wchar_t *>::CSimpleArray<wchar_t *>"
    

    Note that the quotation marks do not go around the @! part. They go only around the symbol. (Otherwise, the debugger thinks you are setting a breakpoint action.)

    Another trick for setting breakpoints is using tab autocompletion for symbols. If you type bp contoso!*Widget* and then hit Tab repeatedly, you will cycle through all the matches. (It takes a few seconds to build the list of matches, so be patient the first time you hit Tab.)

    Personally, I use the x command to print out all the matches, and then cherry-pick the one I want.

    0:001> x contoso!*Widget*
    00af114c contoso!CreateWidget
    009fe863 contoso!DestroyWidget
    00a2e161 contoso!MakeWidgetReadOnly
    00a93168 ...
    
    0:001> bp 00a2e161     set breakpoint on MakeWidgetReadOnly
    
  • The Old New Thing

    How can I get the Windows 8 touch keyboard to display autocomplete suggestions like the Bing app?

    • 25 Comments

    A customer observed that if you use the Windows 8 Bing app with the touch keyboard, the top of the touch keyboard includes autocomplete suggestions for quick access. They wanted to know how to enable this in their own application. In the illustration below, it's the two boxes immediately above the keyboard with the words "aol" and "amazon".

    |
    a|
    SUGGESTIONS
    aol
    amazon
    att.net
    autotrader
    ask.com
    american airlines
    aol   amazon
    q
    w
    e
    r
    t
    y
    u
    i
    o
    p
    a
    s
    d
    f
    g
    h
    j
    k
    l
    '
    Search
    z
    x
    c
    v
    b
    n
    m
    ,
    .
    ?
    &123
    Ctrl
     
    <
    >

    The answer is that it's all done with mirrors.

    The thing with the completion suggestions is not actually a part of the keyboard. The Bing app is simply drawing a black box with buttons at the very bottom edge of their window, so that it exactly abuts the touch keyboard. Touch events on those buttons go straight to the Bing app, and Bing programmatically inserts the appropriate word into the text box.

    In other words: It's a fake!

  • The Old New Thing

    There is no complete list of all notifications balloon tips in Windows

    • 33 Comments

    A customer wanted a complete list of all notifications balloon tips in Windows.

    There is no such list. Each component is responsible for its own balloon tips and is not required to submit their list of balloon tips to any central authority for cataloging. In order to create such a list, somebody would have to go contact every component team and ask them for a list of all their balloon tips, and that component team would probably undertake a search of their code base looking for balloon tips. And figuring out the text of each balloon tip can be tricky since the text may be built dynamically. (And the customer didn't ask for an explanation of the conditions under which each balloon tip may appear, but that's probably going to be their follow-up question.)

    It's like publishing instructions on how to display a message on the message board, and then somebody asking the message board manufacturer, "Please send me a list of all messages that might appear on the message board." The message board manufacturer doesn't know how its customers are using the message board. They would have to go survey their customers and ask each one to build an inventory of every message that could potentially be shown.

    In other words, this customer was asking for a research project to be spun up to answer their question.

    I suspected that this was a case of the for-if antipattern being applied to custom support questions, and I asked what the customer intended to do with this information.

    It turns out that they didn't even want this list of balloon tips. They just had a couple of balloon tips they were interested in, and they wanted to know if there were settings to disable them.

    But even though that was their stated goal, they still reiterated their request for a list of balloon tips. The customer liaison asked, "Is there a possibility is getting a list of balloon tips generated by Windows itself? Even a partial list would be helpful. Can we help with this?"

    What the customer liaison can do is contact each Windows component team and ask them, "Can you give me a list of the balloon tips your component generates? Even a partial list would be helpful." I wished him luck.

    The customer liaison replied, "I passed this information to the customer and will follow up if they have any further questions." No follow-up ever appeared.

  • The Old New Thing

    The gradual erosion of the car trip experience, part 2

    • 27 Comments

    When I learned that my nieces were heading out on a road trip, I asked, "Are you going to sing songs?"

    My eldest niece looked at me as if I were from Mars, then replied, "No, we bring electronics."

Page 7 of 425 (4,249 items) «56789»