• The Old New Thing

    That's not a duck

    • 19 Comments

    One of the audio features added to Windows 7 goes by the formal name stream attenuation, but it is more commonly known to people in the audio world as ducking. Ducking is the process of lowering the volume of background sounds in order to draw more attention to the foreground sound. For example, when you're watching a big battle scene in a summer action movie, your ears are assaulted with the sounds of weapons fire, objects exploding left and right, but when the hero turns to his girlfriend-of-the-moment, the sound level of all the death and destruction drops a bit so you can hear him say something tender, or maybe inspiring, or maybe inspiringly tender. Something like that. And then when the moment is over, the sound level returns to normal and once again the sound of things blowing up overwhelms your eardrums.

    Sorry, I got a bit carried away. Where was I? Oh right, ducking.

    Ducking is the process of temporarily reducing the volume of background sounds in order to make foreground sounds easier to hear. Here's a presentation from the 2008 PDC by Larry Osterman which covers the technical part of ducking.

    When the feature was added to Windows 7, the icon on the Communications tab of the Sound control panel was a yellow rubber duck. Those audio folks think they're so cute.

    Sadly (or perhaps fortunately), the icon was changed to a telephone handset.

    The subject line of this article is an inside joke: When visiting one of my friends, I would sometimes speak to his three-year-old daughter in German, because they say that exposure to multiple languages is a good thing. At one time, I asked her a question, I forget what it was, and she responded, in English, "That's not a duck!"

    None of us could figure out what she was talking about, but it has been a catch phrase among us for over a decade.

  • The Old New Thing

    The Importance of Being Snooki

    • 2 Comments

    What if Jersey Shore were actually a play written by Oscar Wilde? Algernon Moncrieff and Jack Worthing from Broadway's current production of The Importance of Being Earnest explore that premise in a five-part series titled "Jersey Shore" Gone Wilde, drawing its dialogue from things actually said on "Jersey Shore".

    The dialogue and delivery are funny enough. But what really sells it is the acting: The eye rolls, the nervous glances, the blank stares...

    Warning: NSFW for crude language, so I didn't embed the videos. You'll have to click through.

  • The Old New Thing

    Hidden compatibility constraints of redirecting program execution via a stub

    • 20 Comments

    One of the "obvious" solutions to the issue of how much work you're willing to do to save 68KB of disk space was to replace one of the copies with a stub that launches the other copy.

    If you try this obvious solution, you may run into some compatibility issues.

    First of all, there are programs which launch Notepad and then wait on the process handle so they can wait until the user closes Notepad. Your stub program cannot just do a Create­Process on the target, because programs which perform a wait will find the wait satisfied when your stub program exits.

    Okay, so your stub program has to wait for the real copy of Notepad to exit before it can exit itself.

    Once you fix that, you'll find another problem: Programs call Get­Exit­Code­Process to see how Notepad exited. Your stub program therefore cannot just perform an Exit­Process; it has to do a Get­Exit­Code­Process on the real Notepad and pass that exit code to your own Exit­Process.

    Once you fix that, you'll find another problem: There are programs which execute a process and then look for windows owned by that process. (Yes, there can be more than one, but Notepad is a simple program that creates only one top-level unowned window.) Those programs will get the process ID of your stub program and be unable to find the Notepad window (since it belongs to the real Notepad program, which has a different process ID). I'm not sure how to fix that one.

    Yes, you can write a stub that launches another program, but that solves the "save disk space" problem by introducing other problems.

    Remember, even though people are supposed to stick to documented behavior (since that is all that is contractual), in practice any implementation detail becomes a compatibility constraint.

  • The Old New Thing

    Not quite understanding why you wash your hands before playing the piano

    • 15 Comments

    My niece wanted to play my piano, and I asked her to wash her hands. She said, "I don't need that, I have Magic Soap," and she produced a bottle of hand sanitizer.

    Um, the purpose of washing your hands isn't so the piano doesn't get sick.

    My piano-instructor cousin-in-law tells me that her young students often say, "My hands are not dirty. They are just a little sticky because I just ate a chocolate muffin on my way here. I don't think I need to wash my hands. Why do you always ask me to wash my hands before the piano lesson?"

  • The Old New Thing

    Why is there a RestoreLastError function that does the same thing as SetLastError?

    • 18 Comments

    Matt Pietrek noticed that Set­Last­Error and Restore­Last­Error do exactly the same thing and wondered why there's a separate function for it.

    It's to assist in debugging and diagnostics.

    Say you're debugging a problem and when you call Get­Last­Error you get ERROR_ACCESS_DENIED. It would really help a lot if you could figure out who set the error code to ERROR_ACCESS_DENIED. If you set a breakpoint on Set­Last­Error, you find that people call Set­Last­Error for two different reasons:

    1. To report an error.
    2. To restore the error code to what it was before they did something that might change the last error code.

    That second one needs a little explanation. You might have a logging function that goes like this:

    // Remember, code in italics is wrong
    void LogSomething(blah blah)
    {
     DWORD dwError = GetLastError();
     ... do logging stuff ...
     SetLastError(dwError);
    }
    
    // or if you prefer RAII
    
    class PreserveLastError
    {
    public:
        PreserveLastError() : m_dwLastError(GetLastError()) {} 
        ~PreserveLastError() { SetLastError(m_dwLastError); }
    private:
        DWORD m_dwLastError;
    };
    
    void LogSomething(blah blah)
    {
     PreserveLastError preserve;
    
     ... do logging stuff ...
    }
    

    It's important that functions which perform logging, assertion checking, and other diagnostic operations are nonintrusive. You don't want a bug to go away when you turn on logging because the logging code somehow perturbed the system. Therefore, your logging function saves the value of Get­Last­Error() and sets that back as the error code when it's done, so that any errors that took place during logging do not escape and inadvertently affect the rest of the program.

    Now let's go back to the code that's trying to figure out who set the error code to ERROR_ACCESS_DENIED. You set up your debugging diagnostic tool and tell it to record everybody who calls Set­Last­Error() and pay particular attention to everybody who sets the error to ERROR_ACCESS_DENIED. You then run your scenario, your program encounters the failure you're trying to debug, and you ask the diagnostic tool, "Tell me who set the error code to ERROR_ACCESS_DENIED." The diagnostic tool says, "Ah, I have that in my history. The function that set the error code to ERROR_ACCESS_DENIED is... Log­Something!"

    Of course, Log­Something wasn't really the originator of the ERROR_ACCESS_DENIED; it was just restoring things to how it found them. The real ERROR_ACCESS_DENIED came from somebody else, and the log function was just being careful not to disturb it.

    ...
      if (!FunctionX()) {
        LogSomething("Function X failed");
      } else {
        LogSomething("Function X succeeded");
       FunctionY(); // also does some logging
      }
      FunctionZ(); // also does some logging
      Assert(EverythingOkay()); // assertion fires
      // GetLastError() returns ERROR_ACCESS_DENIED
    ...
    

    All those calls to logging functions in between called Get­Last­Error() and got ERROR_ACCESS_DENIED back, then when the logging was complete, they called Set­Last­Error(ERROR_ACCESS_DENIED) to put things back. Your diagnostic error-tracing tool gleefully points the finger at your logging function: "Look! Look! This guy set the error code to ERROR_ACCESS_DENIED!"

    Enter Restore­Last­Error. This function does the same thing as Set­Last­Error, but its use is a message to diagnostic tools that "Sure, you may see me set an error code, but it wasn't my idea. I'm just trying to put things back the way I found them. Keep looking backwards in your history."

    (The message also works forward in time: If you want to catch ERROR_ACCESS_DENIED in the act, you might set a breakpoint on Set­Last­Error, and then get frustrated that the breakpoint keeps getting hit by your logging function. Switching the logging function to Restore­Last­Error keeps the breakpoint on Set­Last­Error from firing spuriously.)

    The corrected version of the Log­Something function is therefore something like this:

    void LogSomething(blah blah)
    {
     DWORD dwError = GetLastError();
     ... do logging stuff ...
     RestoreLastError(dwError);
    }
    
    // or if you prefer RAII
    
    class PreserveLastError
    {
    public:
        PreserveLastError() : m_dwLastError(GetLastError()) {} 
        ~PreserveLastError() { RestoreLastError(m_dwLastError); }
    private:
        DWORD m_dwLastError;
    };
    
  • The Old New Thing

    Microspeak: Hipo

    • 23 Comments

    A friend of mind was asked out of the blue, "What does hypo mean?"

    She started to flash back to high school English class and Greek word roots.

    "I've started to hear it everywhere. Like Everyone in that meeting is a hypo or We need to reach out to hypos."

    My friend realized that she had mis-heard the question. It was not about the Greek root hypo but rather the bizarro Microspeak word hipo, shorthand for high-potential employee.

    As I researched this term (which I had never encountered before), I found that it fell into that special category of Microspeak known as if you have to ask, I'm not going to tell you. Identifying and developing high-potential employees is one of the charter activities of the ExPo project. And if you look through the ExPo Web site, you'll find that nowhere do they tell you what HiPo and ExPo stand for. "If you have to ask, I'm not going to tell you."

    My friend suggested that "ExPos are people who have a lot of potential and enjoy showing it off to others."

  • The Old New Thing

    How do I pin a program directly to the Start menu rather than a shortcut?

    • 34 Comments

    Anonymous bemoans the fact that pinning programs to the Start menu (in Windows XP) actually pins the shortcut rather than the program itself. This means that if you right-click on a shortcut to pin it, then delete the shortcut, the pinned item stops working. How do you pin the program directly?

    You pin the program directly by right-clicking on the program directly. If you right-click on a shortcut and select Pin to Start menu, then you're pinning the shortcut. This is sort of obvious, because after all, that's what you right-clicked on. If you want to pin the raw program, then dive into the Program Files folder and right-click the EXE and select Pin to Start menu. Now you pinned the program executable rather than a shortcut.

    The Windows XP Start menu behaves this way instead of tunnelling through the shortcut to the executable because the shortcut itself contains valuable information such as name, command line parameters, hotkey, working directory, and icon. If you have a shortcut called "Development command prompt" that has custom console colors and runs CMD.EXE with a working directory set to your development tree and with a special startup batch file (via the /K command line switch) to set up your environment variables, and you right-click on that shortcut and selected Pin to Start menu, then you would probably be upset if the thing that actually got pinned to your Start menu was a boring CMD.EXE command prompt with no options and no customization.

    "It would be nice if Windows were a bit more intuitive about this."

    Anonymous didn't provide any specific suggestion as to what would be more intuitive. Pinning the executable and throwing away the shortcut properties is definitely not intuitive.

    The Windows 7 folks thought about this and came up with something that hopefully meets the a bit more intuitive criterion: When you pin a shortcut to the taskbar or the Start menu, they make a copy of the shortcut and pin the copy.

  • The Old New Thing

    No, you can't ask whether your notification icon is visible or not

    • 43 Comments

    A customer asked what started out as a simple question, but by now you know that this sort of simple question only raises new questions:

    Is there a way that we can detect that the user has hidden our notification icon?

    No, there is no way to ask whether your notification icon is hidden or visible. Users decide which icons they want to see, and you don't get to snoop in on the users' decisions.

    Questions like this scare me, because it sounds like this customer's program is going to respond to the user's choice to hide their icon by doing something even more annoying. This is another demonstration of Le Chatelier's Principle as interpreted by John Gall: Every system resists its proper functioning. In this case, a design to make programs less annoying and intrusive merely drives those programs to even more annoying and intrusive behavior.

    The customer's response confirmed this suspicion.

    We have a long-running process with multiple stages. It starts with a dialog box and a progress bar, and after the dialog finishes, a long operation takes place in the background. In some cases during this background operation, we need to ask the user to take an action or make a decision. Our previous version of the program displayed a dialog box under these circumstances, but we switched to a notification balloon to make the experience less annoying. But if the user has hidden our notification, then we want to go back to showing the dialog. (Ideally, we would make the long background operation part of the initial dialog box, but we do not control the initial dialog box.)

    If you use a notification icon, then you have to accept that the user can hide notification icons. If you still want a notification icon, you can start by displaying a dialog box with a progress bar, and give it a button that says "Hide this dialog and display a notification when attention is required." With this design, converting the dialog box to a notification icon is an active step the user must take, and if the user hides the notification icon, it's their own fault.

    Or abandon the notification icon entirely. According to the notification area design guidance, you shouldn't use notification icons if immediate action is required.

    Is the status critical? Is immediate action required? If so, display the information in a way that demands attention and cannot be easily ignored, such as a dialog box.

    Windows 7 lets you communicate the progress of the background operation unobtrusively by displaying the progress bar as part of the taskbar icon.

  • The Old New Thing

    There's only so much you can do to stop running code from simulating UI actions

    • 33 Comments

    Commenter KiwiBlue asks whether Captcha-style tests were considered to prevent unsigned drivers from programmatically clicking the 'Install anyway' button.

    I'm sure somebody considered it, but Captcha has its own problems.

    "Type the (distorted) letters below"-type Captcha cannot be used by people with visual impairments, people who are dyslexic, or people who simply are not familiar with the Latin alphabet. (Believe it or not, the vast majority of people on the planet have a native language which does not use the Latin alphabet.) Using an audio captcha runs into the problem of different accents, letters whose readings vary (zee/zed anyone?), and computers without a sound card (like most servers).

    And yes, there are other types of Captchas (dog/cat, for example), but the strongest argument against Captcha is probably that it's just adding more locks to the front door while leaving the service entrance wide open. Once you make it computationally infeasible to programmatically solve the Captcha, unscrupulous driver vendors would simply inject a DLL into the "Install this unsigned driver?" process and patch the call to Did­User­Answer­Captcha­Correctly so it always returns TRUE.

    Or even easier, just programmatically set the Driver Signing Options to Install the software anyway.

    If somebody is running code with administrative privileges, then they already own your machine. Any roadblocks you put up they can find a way to drive over. The goal is not so much putting up stronger and stronger roadblocks (because eventually people will simply drive around them) but rather making it clear to the developer that what they're doing is driving around a roadblock.

  • The Old New Thing

    Even if you have a lock, you can borrow some lock-free techniques

    • 8 Comments

    Even if you prefer to use a lock (after all, they are much easier to program), you can borrow some lock-free techniques. For example, consider this:

    CRITICAL_SECTION g_cs;
    GORILLADATA g_data;
    
    void PokeGorilla(double intensity)
    {
      EnterCriticalSection(&g_cs);
      DeformGorilla(intensity, &g_data);
      Reticulate(&g_data.spline);
      int stress = CalculateTension(&g_data.spline);
      if (stress < 25)      g_data.mood = RELAXED;
      else if (stress < 50) g_data.mood = ANNOYED;
      else                  g_data.mood = ANGRY;
      DeleteObject(g_data.hbmGorilla);
      g_data.hbmGorilla = RenderGorilla(&g_data);
      LeaveCriticalSection(&g_cs);
    }
    

    There are some concerns here. First of all, there's the lock hierarchy issue: If reticulating a spline takes the geometry lock, that may violate our lock hierarchy.

    If the lock g_cs is a hot lock, you may be concerned that all this gorilla stuff will hold the lock for too long. Maybe rendering a gorilla is a slow and complicated operation because it's hard to get the fur just right.

    These issues become less onerous if you switch to a lock-free algorithm, but that's an awful lot of work, and it's hard to get right. But maybe you can do just 20% of the work to get 80% of the benefit.

    void PokeGorilla(double intensity)
    {
      // Capture
      EnterCriticalSection(&g_cs);
      GORILLADATA data = g_data; // typo fixed
      LeaveCriticalSection(&g_cs);
    
      // Recalculate based on captured data
      DeformGorilla(intensity, &data);
      Reticulate(&data.spline);
      int stress = CalculateTension(&data.spline);
      if (stress < 25)      data.mood = RELAXED;
      else if (stress < 50) data.mood = ANNOYED;
      else                  data.mood = ANGRY;
      data.hbmGorilla = RenderGorilla(&data);
    
      // Commit
      EnterCriticalSection(&g_cs);
      HBITMAP hbmToDelete = g_data.hbmGorilla;
      g_data = data;
      LeaveCriticalSection(&g_cs);
      DeleteObject(hbmToDelete);
    }
    

    Here, we use the capture/try/commit model. We capture the state of the gorilla into a local variable, then perform our update based on that captured state. The spline reticulation takes place without any locks held, which avoids introducing a lock hierarchy violation. And rendering the gorilla is done without any locks held, which avoids introducing a choke point on the lock. After the calculations are done, we then re-enter the lock and commit the changes.

    This pattern uses a last-writer-wins model. If another thread pokes the gorilla while we are still calculating the previous gorilla state, we will overwrite that gorilla state when we complete. For some scenarios, that's acceptable. But maybe the gorilla's emotional state needs to be an accumulation of all the times he's ben poked. We want to detect that somebody has poked the gorilla while we were busy calculating so that we can incorporate that new information into the final result.

    To do that, we introduce a change counter.

    LONG g_lCounter;
    
    void PokeGorilla(double intensity)
    {
      BOOL fSuccess;
    
      do {
        // Capture
        EnterCriticalSection(&g_cs);
        GORILLADATA data = g_data; // typo fixed
        LONG lCounter = g_lCounter;
        LeaveCriticalSection(&g_cs);
    
        // Recalculate based on captured data
        DeformGorilla(intensity, &data);
        Reticulate(&data.spline);
        int stress = CalculateTension(&data.spline);
        if (stress < 25)      data.mood = RELAXED;
        else if (stress < 50) data.mood = ANNOYED;
        else                  data.mood = ANGRY;
        data.hbmGorilla = RenderGorilla(&data);
    
        // Commit
        EnterCriticalSection(&g_cs);
        HBITMAP hbmToDelete;
        if (lCounter == g_lCounter)
        {
          hbmToDelete = g_data.hbmGorilla;
          g_data = data;
          g_lCounter++;
          fSuccess = TRUE;
        } else {
          hbmToDelete = data.hbmGorilla;
          fSuccess = FALSE;
        }
        LeaveCriticalSection(&g_cs);
        DeleteObject(hbmToDelete);
      } while (!fSuccess);
    }
    

    In addition to the regular gorilla data, we also associate a change counter that is incremented each time somebody pokes the gorilla. In real life, you might want to make the change counter part of the GORILLA­DATA structure. (Actually, in real life, you probably shouldn't poke a gorilla.) In a lock-free algorithm, we would Interlocked­Compare­Exchange­Release the lock counter to see if the lock counter changed (and if not, to update it with the new lock counter). But since a GORILLA­DATA structure cannot be updated atomically, we have to use our critical section to perform the comparison-and-update.

    Even though we used a lock, we still follow the lock-free pattern. If the gorilla has been poked while we were busy processing our own poke, then we throw away the results of our computations and start over, so that our poke can be accumulated with the previous pokes.

    Exercise: What constraints must be applied to the GORILLADATA structure for this technique to work?

Page 125 of 447 (4,467 items) «123124125126127»