January, 2013

  • The Old New Thing

    Microspeak: pivot

    • 12 Comments

    A great word to use at Microsoft to make it sound like you're one of the cool insiders is pivot. Mostly because the meaning of the word varies from place to place, so you can use it to mean whatever you like while still sounding hip and jargony.

    In Windows Phone, the term pivot is a technical term which refers to a type of control that lets users switch easily from page to page. The term is used metonymically to mean the pages themselves.

    In the Calendar, on the To-Do pivot, you can press and hold on a to-do item and select postpone a day.

    In Excel, the term pivot refers to a type of table or chart that summarizes data.

    In Windows Live Search (as it was known back then), the term pivot referred to the category selectors at the top of the page (Images, Videos, News, etc.)

    But once you go beyond specific technical definitions, things get vague quickly.

    The most general sense of the term is just that a pivot is way of visualizing data. No summarization required.

    Old and busted New hotness
    Calendar view Pivot by time
    Group by type Pivot by type

    Use the new hotness to gain instant credibility.

    Here are some other citations which seem to be even more vague.

    We will focus v-teams along client/server pivots across workloads.
    Is there a way to write a program that does Q whenever Z happens? Another pivot on this question would be to add this as a feature in the XYZ product.
    Over time, we will adjust this diagram to pivot by process rather than organizational unit, but for the moment, the organizational unit serves as a rough proxy for process.
    The new model will be a significant change to the organization, and it will take focused effort to reorient the organization to this alignment. Initially in this new pivot, there may be challenges in learning new roles, accountabilities and responsibilities from role to role in the overall project structure.

    That last one is today's winner for muddled management-speak.

    When in doubt, toss in the word pivot. Nobody will know what you mean, but that's okay, because you don't know what it means either. It just sounds cool.

  • The Old New Thing

    The 2013/2014 Seattle Symphony subscription season at a glance

    • 10 Comments

    For many years, I've put together a little pocket guide to the Seattle Symphony subscription season for my symphony friends to help them decide which ticket package they want. In the years that have passed since I started doing this, nearly all of the members of the group have started families, and we tried things like splitting tickets, but even that became difficult to maintain, and last year, we ended up not ordering any tickets at all. (Well, no tickets for adults. We did buy tickets to kids concerts.)

    I compiled the at-a-glance season guide for the 2013/2014 season anyway, but I didn't bother adding my comments since we know we're not ordering any tickets. Moreover, the Seattle Symphony created online playlists where you can listen to the pieces in the upcoming season, so you can decide for yourself whether you like them or not!

    Here is the official brochure for those who want to read the details, and you can see what The Seattle Times thinks of it. To access the playlists, go to www.naxos­music­library.com and log on with userid SymphonySubs with password 20132014. Once you've logged in, click Playlists in the navigation bar and choose the concerts you want to hear.

    Week Program 21 13 7A
    7B
    7C
    7D
    7E
    7F
    7G 4A HS SU WG
    09/19
    2013
    Ravel: Alborada del gracioso
    Ravel: Concerto for Left Hand in D
    Ravel: Rapsodie espagnole
    Ravel: Pavane pour une infante défunte
    Ravel: Piano Concerto in G
    Ravel: Boléro
                   
     

     
     

     
     
    10/03
    2013
    Beethoven: Triple Concerto
    Schubert: Symphony #9 in C The Great
                       
    10/10
    2013
    Purcell: Suite
    Mozart: Piano Concerto #23 in A
    Vaughan Williams: Symphony #5
                       
    10/18
    2013
    Mozart: Divertimento for String in D
    Dittersdorf: Sinf. concertante for Double Bass & Viola
    Mozart: Exsultate jubilate K.65
    Mozart: Symphony #29
                       
    10/24
    2013
    Elgar: Serenade
    Haydn: Cello Concerto in C
    Mozart: Symphony #37
    Tchaikovsky: Serenade for Strings
                       
    11/07
    2013
    Boulez: Notation
    Mahler: Symphony #6
                       
    11/13
    2013
    Sibelius: Tapiola
    Pascal Dusapin: Violin Concerto
    Beethoven: Symphony #6 Pastoral
                       
    11/21 Verdi: Requiem                    
    12/13 Vivaldi: The Four Seasons                    
    12/20 Handel: Messiah                    
    12/23 Christmas carols (Northwest Sinfonia)                    
    12/31
    2013
    Gershwin: Rhapsody in Blue
    Tchaikovsky/Ellington: Nutcracker excerpts
    Williams: Star Wars excerpts
    other amusements
                       
    01/02
    2014
    Brahms: Variations on a Theme by Haydn
    Beethoven: Symphony #9 Choral
                   
     
     
    01/23
    2014
    Prokofiev: Symphony #1 Classical
    Haydn: Piano Concerto in D
    Prokofiev: Piano Concerto #4
    Mozart: Symphony #35 Haffner
                       
    01/30
    2014
    John Adams: The Chairman Dances
    Shostakovich: Piano Concerto #2
    Liszt (arr John Adams): The Black Gondola
    Shostakovich: Symphony #9
                   
     

     
     
    02/13
    2014
    Schumann: Cello Concerto
    Berlioz: Symphonie fantastique
                       
    03/06
    2014
    R. Strauss: Don Juan
    R. Strauss: Burleske
    R. Strauss: Divertimento
    R. Strauss: Suite from Der Rosenkavalier
                       
    03/13
    2014
    Dvořák: The Noonday Witch
    Bartók: Violin Concerto #2
    Mozart: Symphony #38 Prague
                       
    03/20
    2014
    Rimsky-Korsakov: Suite from The Snow Maiden
    Alexander Raskatov: Piano Concerto
    Tchaikovsky: Symphony #6 Pathetique
                       
    03/27
    2014
    Dvořák: Cello Concerto
    Varèse: Déserts
    Debussy: La Mer
                       
    04/03
    2014
    Haydn: Symphony #100 Military
    Orff: Carmina burana
                       
    04/17
    2014
    James MacMillan: The Death of Oscar
    Beethoven: Piano Concerto #3
    Rachmaninoff: Symphony #2
                       
    04/24
    2014
    Martinů: Memorial to Lidice
    Brahms: Violin Concerto
    Dvořák: Symphony #7
                       
    06/05
    2014
    Dutilleux: Symphony #2 Le double
    Ravel: Daphnis et Chloé complete ballet
                       
    06/12
    2014
    J. Strauss: Emperor Waltzes
    Schoenberg: Piano Concerto
    Brahms: Symphony #2
                       
    06/19
    2014
    Stravinsky: The Firebird
    Stravinsky: Petrushka
    Stravinsky: The Rite of Spring
                   
     


     
    Week Program 21 13 7A
    7B
    7C
    7D
    7E
    7F
    7G 4A HS SU WG

    Legend:

    21Masterworks 21-concert series (Choice of Thursdays or Saturdays)
    13Masterworks 13-concert series (Choice of Thursdays or Saturdays)
    7AMasterworks 7-concert series A (Thursdays)
    7BMasterworks 7-concert series B (Saturdays)
    7CMasterworks 7-concert series C (Thursdays)
    7DMasterworks 7-concert series D (Saturdays)
    7EMasterworks 7-concert series E (Thursdays)
    7FMasterworks 7-concert series F (Saturdays)
    7GMasterworks 7-concert series G (Sunday afternoons)
    4AMasterworks 4-concert series A (Friday afternoons)
    HSHolidays at the Symphony
    SUSymphony Untuxed (Fridays, reduced program)
    WGWolfGang (Various evenings), see notes below

    For those not familiar with the Seattle Symphony ticket package line-ups: Most of the ticket packages are named Masterworks nX where n is the number is the number of concerts in the package, and the letter indicates which variation. Ticket packages have been combined if they are identical save for the day of the week. For example, 7C and 7D are the same concerts; the only difference is that 7C is for Thursday nights, while 7D is for Saturday nights.

    The WolfGang series is available only to members of the WolfGang club. It also includes two concerts not listed above (Opening Night and Sonic Evolution).

    This chart doesn't include "one-off" concert series such as the Mainly Mozart or Distinguished Artists series. A "one-off" series is a concert series which shares no concerts with any other series. This year, the Beyond the Score multimedia concerts fell into that category.

    According to the Seattle Times article, the new music director has "always dreamed of spending a whole evening with a composer," and he went big this year, with three concerts consist of multiple works by a single composer.

    Symphony trivia: The audience for the Sunday afternoon concerts (series 7G) leans toward what we in the United States euphemistically call senior citizens. I find it interesting to see what those sweet old ladies get put through every year. For example, this year, they get a performance of Carmina burana, which will probably offend half of the audience and delight the other half.

  • The Old New Thing

    The changing name of the Microsoft event held in conjunction with Martin Luther King, Jr. Day

    • 9 Comments

    Today is Martin Luther King, Jr. Day, a federal holiday in the United States honoring the civil rights leader and formally serving as a day to reflect on the principles of racial equality and nonviolent social change and more generally to honor Dr. King's legacy through service.

    At Microsoft, the day has been recognized with an event whose name is, um, well, the name keeps changing. Here are the names from recent years:

    • 2006: Martin Luther King, Jr. Day Event
    • 2007: Martin Luther King, Jr. Celebration
    • 2008: Dr. Martin Luther King, Jr. Day Event
    • 2009: MLK Day Event
    • 2010: Celebration to Honor Dr. Martin Luther King, Jr.
    • 2011: Martin Luther King Day of Celebration
    • 2012: Martin Luther King, Jr. Day of Celebration
    • 2013: Dr. Martin Luther King, Jr. Day of Celebration Event

    I like to think this is done intentionally just to keep people on their toes.

    Ironically, although the event "celebrates diversity and inclusion," the email announcing the event inadvertently excludes people with visual impairments because it consists of a giant JPG with no ALT text.

    (No dream report from me today. That would be disrespectful to the man himself.)

  • The Old New Thing

    What is this rogue version 1.0 of the HTML clipboard format?

    • 34 Comments

    At least as of the time this article was originally written, the HTML clipboard format is officially at version 0.9. A customer observed that sometimes they received HTML clipboard data that marked itself as version 1.0 and wanted to know where they could find documentation on that version.

    As far as I can tell, there is no official version 1.0 of the HTML clipboard format.

    I hunted around, and the source of the rogue version 1.0 format appears to be the WPF Toolkit. Version 1.0 has been the version used by ClipboardHelper.cs since its initial commit.

    If you read the code, it appears that they are not generating HTML clipboard data that uses any features beyond version 0.9, so the initial impression is that it's just somebody who jumped the gun and set their version number higher than they should have. The preliminary analysis says that you can treat version 1.0 the same as version 0.9.

    But that's merely the preliminary analysis.

    A closer look at the Get­Clipboard­Content­For­Html function shows that it generated the HTML content incorrectly. The code treats the fragment start and end offsets as character offsets, not byte offsets. But the offsets are explicitly documented as in bytes.

    StartFragment Byte count from the beginning of the clipboard to the start of the fragment.
    EndFragment Byte count from the beginning of the clipboard to the end of the fragment.
    My guess is that the author of that helper function made two mistakes that partially offset each other.

    1. The author failed to take into account that C# operates in Unicode, whereas the HTML clipboard format operates in UTF-8. The byte offset specified in the HTML format header is the byte count in the UTF-8 encoding, not the byte count in the UTF-16 encoding used by C#.
    2. The author did all their testing with ASCII strings. UTF-8 has the property that ASCII encodes to itself, so one byte equals one character. If they had tested with a non-ASCII character, they would have seen the importance of the byte count. (Or maybe they simply would have gotten more confused.)

    Now, WPF knows that the Data­Formats.HTML clipboard format is encoded in UTF-8, so when you pass a C# string to be placed on the clipboard as HTML, it knows to convert the string to UTF-8 before putting it on the clipboard. But it doesn't know to convert the offsets you provided in the HTML fragment itself. As a result, the values encoded in the offsets end up too small if the text contains non-ASCII characters. (You can see this by copying text containing non-ASCII characters from the DataGrid control, then pasting into Word. Result: Truncated text, possibly truncated to nothing depending on the nature of the text.)

    There are two other errors in the Get­Clipboard­Content­For­Html function: Although the code attempts to follow the recommendation of the specification by placing a <!--EndFragment--> marker after the fragment, they erroneously insert a \r\n in between. Furthermore, the EndHTML value is off by two. (It should be DATA­GRID­VIEW_html­End­Fragment.Length, which is 38, not 36.)

    Okay, now that we see the full situation, it becomes clear that at least five things need to happen.

    The immediate concern is what an application should do when it sees a rogue version 1.0. One approach is to exactly undo the errors in the WPF Toolkit: Treat the offsets as character offsets (after converting from UTF-8 to UTF-16) rather than byte offsets. This would address the direct problem of the WPF Toolkit, but it is also far too aggressive, because there may be another application which accidentally marked its HTML clipboard data as version 1.0 but which does not contain the exact same bug as the WPF Toolkit. Instead, applications which see a version number of 1.0 should treat the EndHTML, EndFragment, and EndSelection offsets as untrustworthy. The application should verify that the EndFragment lines up with the <!--EndFragment--> marker. If it does not, then ignore the specified value for EndFragment and infer the correct offset to the fragment end by searching for the last occurrence of the <!--EndFragment--> marker in the clipboard data, but trim off the spurious \r\n that the WPF Toolkit erroneously inserted, if present. Similarly, EndHTML should line up with the end of the </HTML> tag; if not, the specified offset should be ignored and the correct value inferred. Fortunately, the WPF Toolkit does not use EndSelection, so there is no need to attempt to repair that value, and it does not use multiple fragments, so only one fragment repair is necessary.

    Welcome to the world of application compatibility, where you have to accommodate the mistakes of others.

    Some readers of this Web site would suggest that the correct course of action for your application is to detect version 1.0 and put up an error message saying, "The HTML on the clipboard was placed there by a buggy application. Contact the vendor of that application and tell them to fix their bug. Until then, I will refuse to paste the data you copied. Don't blame me! I did nothing wrong!" Good luck with that.

    Second, the authors of the WPF Toolkit should fix their bug so that they encode the offsets correctly in their HTML clipboard format.

    Third, at the same time they fix their bug, they should switch their reported version number back to 0.9, so as to say, "Okay, everybody, this is the not-buggy version. No workaround needed any more." If they leave it as 1.0, then applications which took the more aggressive workaround will end up double-correcting.

    Fourth, the maintainers of the HTML clipboard format may want to document the rogue version 1.0 clipboard format and provide recommendations to applications (like I just did) as to what they should do when they encounter it.

    Fifth, the maintainers of the HTML clipboard format must not use version 1.0 as the version number for any future revision of the HTML clipboard format. If they make another version, they need to call it 0.99 or 1.01 or something different from 1.0. Version 1.0 is now tainted. It's the version number that proclaims, "I am buggy!"

    At first, we thought that all we found was a typo in an open-source helper library, but digging deeper and deeper revealed that it was actually a symptom of a much deeper problem that has now turned into an industry-wide five-pronged plan for remediation.

  • The Old New Thing

    A brief history of the GetEnvironmentStrings functions

    • 24 Comments

    The Get­Environment­Strings function has a long and troubled history.

    The first bit of confusion is that the day it was introduced in Windows NT 3.1, it was exported funny. The UNICODE version was exported under the name Get­Environment­StringsW, but the ANSI version was exported under the name Get­Environment­Strings without the usual A suffix.

    A mistake we have been living with for over two decades.

    This is why the winbase.h header file contains these confusing lines:

    WINBASEAPI
    LPCH
    WINAPI
    GetEnvironmentStrings(
        VOID
        );
    
    WINBASEAPI
    LPWCH
    WINAPI
    GetEnvironmentStringsW(
        VOID
        );
    
    #ifdef UNICODE
    #define GetEnvironmentStrings  GetEnvironmentStringsW
    #else
    #define GetEnvironmentStringsA  GetEnvironmentStrings
    #endif // !UNICODE
    

    It's trying to clean up a mess that was created long ago, and it only partly succeeds. This is why your IDE may get confused when you try to call the Get­Environment­Strings function and send you to the wrong definition. It's having trouble untangling the macros whose job is to try to untangle the original mistake.

    The kernel folks tried to clean this up as quickly as they could, by exporting new functions with the names Get­Environment­StringsW and Get­Environment­StringsA, like they should have been in the first place, but for compatibility purposes, they still have to export the weird unsuffixed Get­Environment­Strings function. And then to avoid all the "gotcha!"s from people looking for proof of nefarious intent, they kept the mistake in the public header files to make their actions visible to all.

    Though personally, I would have tidied things up differently:

    WINBASEAPI
    LPCH
    WINAPI
    GetEnvironmentStrings(
        VOID
        );
    
    WINBASEAPI
    LPCH
    WINAPI
    GetEnvironmentStringsA(
        VOID
        );
    
    WINBASEAPI
    LPWCH
    WINAPI
    GetEnvironmentStringsW(
        VOID
        );
    
    #ifdef UNICODE
    #define GetEnvironmentStrings  GetEnvironmentStringsW
    #else
    #define GetEnvironmentStrings  GetEnvironmentStringsA
    #endif // !UNICODE
    

    I would have left the declaration of the mistaken Get­Environment­Strings function in the header file, but redirected the symbolic name to the preferred suffixed version.

    But then again, maybe my version would have confused IDEs even more than the current mechanism does.

    The other unfortunate note in the history of the Get­Environment­Strings function is the odd way it handled the Unicode environment. Back in the old days, the Get­Environment­Strings function returned a raw pointer to the environment block. The result was that if some other code modified the environment, your pointer became invalid, and there was nothing you could do about it. As I noted, the function was subsequently changed so that both the ANSI and Unicode versions return snapshots of the environment strings, so that the environment strings you received wouldn't get spontaneously corrupted by another thread.

  • The Old New Thing

    How do I create a TaskDialog with a progress bar but no cancel button?

    • 35 Comments

    A developer from another group within Microsoft wanted to create a Task­Dialog with a progress bar, but they couldn't figure out how to get rid of the Cancel button.

    Reticulating Splines
    Contacting Spline Reticulation Server

    "Is there a way to remove all the buttons from a Task Dialog?"

    Um, users hate it when you give them a window that cannot be closed or cancelled. What should the user do if the reticulation server stops responding? Shut down the computer? (Hey, at least shutting down the computer will actually work.)

    "The process usually takes around two seconds, and we time out after ten. In the case of timeout, we replace the progress dialog with a failure dialog with the options Close and Retry. But for this dialog, we just want to show the progress bar so they know that we are doing something. We have not yet finalized the design. One design is to have a Cancel button on the progress dialog; another is to remove the option to Cancel. We're just investigating the possibility of the second option. We haven't committed to it yet."

    You should leave the Cancel button enabled, and if the user clicks it, then go straight to the "timed out" dialog. Removing the Cancel button leaves the user trapped in a dialog box with no escape route.

    Bonus chatter: By an astonishing coincidence, a few weeks after this email exchange concluded, I happened to encounter the Reticulating Splines dialog, and it got stuck, and there was no Cancel button. The frustrated user who got trapped with a window that could not be closed or cancelled turned out to be me.

  • The Old New Thing

    If there were some sort of award for alternative commuting, we would've been eligible

    • 17 Comments

    A few projects ago, I worked on a team whose members came to work by a wide variety of modes. If there were some sort of award for alternative commuting, we would've been eligible.

    • Two bicyclists.
    • One runner.
    • One motorcyclist.
    • Two full-time telecommuters.
    • One part-time telecommuter.
    • One traditional car commuter. (Freak.)

    One of the full-time telecommuters was based in Spain, which was handy because he was available to deal with issues that cropped up while everybody in Redmond was sleeping.

    Perhaps to compensate for all the gasoline being saved by his employees, our manager drove an oversized pick-up truck to work.

  • The Old New Thing

    State law requires you to watch this video of a singing hippo

    • 15 Comments

    I did it briefly a few years ago, but I'm going to make it a regular Monday feature, at least for this year: Blogging my dreams.

    I dreamed that I was back in my high school English class, and due to some new state law, everybody was required to watch this video of a singing hippo. The hard part was walking four miles to the nearest viewing location, seeing as we didn't even have a TCP/IP standard yet, much less YouTube.

    To think that this is the culmination of the technological advances of the last three decades: Being able to watch a singing hippo on demand.

  • The Old New Thing

    How can I write a script that finds my top-rated photos?

    • 25 Comments

    I'm not sure if I'll be able to keep it up, but I'm going to see if I can make Monday "Little Programs" day, where I solve simple problems with little programs.

    Today's little program is a script that goes through your Pictures folder and picks out your top-rated photos.

    The key step here is extracting the rating, which goes by the name System.Rating in the shell property system. The method which does the extraction is Shell­Folder­Item.Extended­Property.

    var shell = new ActiveXObject("Shell.Application");
    var picturesFolder = shell.Namespace(39); // CSIDL_MYPICTURES
    var items = picturesFolder.Items();
    var SHCONTF_NONFOLDERS = 64;
    items.Filter(SHCONTF_NONFOLDERS, "*.jpg");
    for (var i = 0; i < items.Count; i++) {
      var item = items.Item(i);
      if (item.ExtendedProperty("System.Rating") >= 80) {
        WScript.StdOut.WriteLine(item.Path);
      }
    }
    

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

    That program searches one folder, but let's say we want to do a full recursive search. No problem. Take the code we wrote and shove it into a helper function process­Files­In­Folder, then call it as part of a recursive directory search.

    function processFilesInFolder(folder) {
      var items = folder.Items();
      var SHCONTF_NONFOLDERS = 64;
      items.Filter(SHCONTF_NONFOLDERS, "*.jpg");
      for (var i = 0; i < items.Count; i++) {
        var item = items.Item(i);
        if (item.ExtendedProperty("System.Rating") >= 80) {
          WScript.StdOut.WriteLine(item.Path);
        }
      }
    }
    
    function recursiveProcessFolder(folder) {
      processFilesInFolder(folder);
      var items = folder.Items();
      var SHCONTF_FOLDERS = 32;
      items.Filter(SHCONTF_FOLDERS, "*");
      for (var i = 0; i < items.Count; i++) {
        recursiveProcessFolder(items.Item(i).GetFolder);
      }
    }
    
    var shell = new ActiveXObject("Shell.Application");
    var picturesFolder = shell.Namespace(39);
    recursiveProcessFolder(picturesFolder);
    

    You can use this as a jumping-off point for whatever you want to do with your top-rated pictures, like copy them to your digital photo frame.

  • The Old New Thing

    Understanding errors in classical linking: The delay-load catch-22

    • 25 Comments

    Wrapping up our week of understanding the classical model for linking, we'll put together all the little pieces we've learned this week to puzzle out a linker problem: The delay-load catch-22.

    You do some code cleanup, then rebuild your project, and you get

    LNK4199: /DELAYLOAD:SHLWAPI ignored; no imports found from SHLWAPI
    

    What does this error mean?

    It means that you passed a DLL via the /DELAYLOAD command line switch which your program doesn't actually use, so the linker is saying, "Um, you said to treat this DLL special, but I don't see that DLL."

    "Oh, right," you say to yourself. "I got rid of a call to Hash­String, and that was probably the last remaining function with a dependency on SHLWAPI.DLL. The linker is complaining that I asked to delay-load a DLL that I wasn't even loading!"

    You fix the problem by deleting SHLWAPI.DLL from the /DELAYLOAD list, and removing SHLWAPI.LIB from the list of import libararies. And then you rebuild, and now you get

    LNK2019: unresolved external '__imp__HashData' referenced in function 'HashString'
    

    "Wait a second, I stopped calling that function. What's going on!"

    What's going on is that the Hash­String function got taken along for the ride by another function. The order of operations in the linker is

    • Perform classical linking
    • Perform nonclassical post-processing
      • Remove unused functions (if requested)
      • Apply DELAYLOAD (if requested)

    The linker doesn't have a crystal ball and say, "I see that in the future, the 'remove unused functions' step is going to delete this function, so I can throw it away right now during the classical linking phase."

    You have a few solutions available to you.

    If you can modify the library, you can split the Hash­String function out so that it doesn't come along for the ride.

    If you cannot modify the library, then you'll have to use the /IGNORE flag to explicitly ignore the warning.

    Exercise: Another option is to leave SHLWAPI.LIB in the list of import libraries, but remove it from the DELAYLOAD list. Why is this a dangerous option? What can you do to make it less dangerous?

Page 2 of 3 (29 items) 123