• The Old New Thing

    Microspeak: Stretch goal

    • 10 Comments

    Recall that Microspeak is not merely for jargon exclusive to Microsoft, but also for jargon that you need to know.

    A project will set some goals, which are things it intends to accomplish. It may also set stretch goals, which are things it hopes to acomplish. If you fail to achieve a stretch goal, your project is still a success, but if you make it, your project is even more awesome. A stretch goal could be unrelated to an existing goal.

    Goals for this release

    • Support dynamic widget recolorization.

    Stretch goals for this release

    • Improve throughput by 10% compared to previous version.

    Non-goals for this release

    • Support offline mode.

    But more often, it takes the form of a higher level of achievement for an existing goal:

    Goal: 40% of the programs will land actual spend within 10% of estimate. Stretch goal: 60%.

    Setting a stretch goal is tricky. You want to set it just at the edge of achievability. If you make it unrealistic, then nobody will take it seriously.

    You can think of a stretch goal as an "extra credit" assignment. You won't be penalized for missing it, but making it will earn you kudos.

  • The Old New Thing

    Parsing a string as a 64-bit integer, somehow

    • 17 Comments

    Today's Little Program takes a string and tries to parse it as a 64-bit integer in formats that a programmer would likely encounter.

    Here's a first stab:

    using System;
    using System.Globalization;
    
    class Program
    {
     static long ParseLongSomehow(string s)
     {
      if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) {
       return long.Parse(s.Substring(2), NumberStyles.HexNumber);
      } else {
       return long.Parse(s);
      }
     }
    
     public static void Main(string[] args)
     {
      long value = ParseLongSomehow(args[0]);
      Console.WriteLine(value);
      Console.WriteLine("0x{0:X}", value);
     }
    }
    

    If the string begins with 0x, then we treat the rest of the argument as a hex value; otherwise, we treat it as a decimal value.

    Unfortunately, this doesn't work if the input is 9223372036854775808, which is the value of 1 << 63, a value that is representable as a 64-bit unsigned value but not a 64-bit signed value.

    Our problem statement was pretty vague, so let's write a functional specification. It helps to know what problem you're solving before you start to solve it. Otherwise, you're just flailing around writing code before you have a plan. When I tried to solve this problem, I flailed around a bit until I realized that I didn't have a spec.

    What formats would a programmer be likely to encounter as the string representation of a 64-bit integer?

    • 0x1234: 64-bit number in hex format, case-insensitive. The value can range from 0 to UInt64.MaxValue.
    • 12345: 64-bit unsigned number in decimal format. The value can range from 0 to UInt64.MaxValue.
    • -12345: 64-bit signed number in decimal format. The value can range from Int64.MinValue to Int64.MaxValue.
    • Other formats may be permitted, but you need to support at least the above.

    Writing down exactly what I was doing and what I wasn't doing was the part that solved my flailing. I had been worrying about things like -0x12345 and -9223372036854775809 and 9999999999999999999, even though those numbers would not be something a programmer would be likely to encounter.

    From the specification we can develop our algorithm.

    • If the string begins with 0x, then parse what's left as an unsigned 64-bit hexadecimal number.
    • If the string begins with a minus sign, then parse it as a 64-bit signed number in decimal format.
    • If the string does not begin with a minus sign, then parse it as a 64-bit unsigned number in decimal format.

    And that is pretty easy to implement.

     static long ParseLongSomehow(string s)
     {
      if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) {
       return long.Parse(s.Substring(2), NumberStyles.HexNumber);
      } else if (s[0] == '-') {
       return long.Parse(s);
      } else {
       return (long)ulong.Parse(s);
      }
     }
    

    Note that we are a little sloppy with our treatment of whitespace. We accept leading and trailing spaces on decimal values, and allow trailing spaces on hex values (and even allow spaces between the 0x and the first hex digit). That's okay, because the spec allows us to accept formats beyond the ones listed.

    Now, for bonus points, let's revise the functional specification a little bit, specifically by adding another case:

    • 0x12`3456789A: 64-bit number in hex format, case-insensitive, with backtick separating the upper 32 bits from the lower 32 bits.

    This is the format used by the Windows debugger engine.

     static long ParseLongSomehow(string s)
     {
      if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) {
       return long.Parse(s.Substring(2).Replace("`", ""), NumberStyles.HexNumber);
      } else if (s[0] == '-') {
       return long.Parse(s);
      } else {
       return (long)ulong.Parse(s);
      }
     }
    

    We'll leave it here for now. Next time, we'll start putting some blocks together.

  • The Old New Thing

    Windows 95 and Windows NT manage the TEB on x86 differently

    • 22 Comments

    Today, a historical note of no consequence. Just a story.

    The Win32 x86 ABI specifies that the FS register holds a selector which is based at the current thread's TEB. In other words, fs:[n] is the nth byte of the TEB.

    It so happens that the two operating systems chose to manage the FS register differently.

    Windows 95 gave each TEB in the system its own selector.

    Windows NT allocated a single selector to represent the TEB, and each time the processor changed threads, the selector base was updated to match the TEB for the new thread. With this model, every thread has the same value for FS, but the selector's descriptor kept changing.

    It's as if you had a car-rental service, and one of the features of the service is that the radio remembers your presets. The instructions for setting the radio are as follows:

    • Enter the four-digit customer preferences ID printed on your receipt.
    • Your radio is now set to your preferences.

    There are two ways you could set up this system.

    Windows 95 assigns each customer a unique customer preferences ID and prints it on the receipt. When the customer enters the ID, the radio looks up the ID in a database and applies the user's radio preferences.

    Windows NT prints the same preferences ID on every receipt: 1234. Before the customer picks up the car from the rental service, the service agent sets the radio to the customer's preferences. When the customer enters the ID, the radio does nothing (aside from printing an error message if you enter anything other than 1234).

    Even though the Windows NT way creates more work for the service agent, it does solve an important problem: It lets your service scale to more than 10,000 customers, for once you have 10,001 customers, you cannot assign each of them a unique four-digit ID any more.

    Car Windows
    car processor
    customer thread
    radio preferences TEB
    customer ID selector

    By assigning a unique selector to each TEB, Windows 95 limited itself to at most 8192 threads. (In practice, the limit was much lower because selectors were used for other things, too.) This was not an issue in practice because Windows 95 would run into other problems long before you ran into the 8192-thread limit.

    But Windows NT was designed to be scalable to very large workloads, and they couldn't artificially limit themselves to a maximum of 8192 threads.

    The consequences of changing the meaning of the FS register at every thread switch means that some tricks that happened to work in Windows 95 didn't work in Windows NT. For example, in Windows 95, if you captured the value of the FS register in one thread, you could use it (in violation of the ABI) on another thread in the same process and still access the originating thread's TEB. If you tried this trick on Windows NT, you would just see your own TEB.

    In the analogy, it's as if you copied the customer preferences ID from another customer's receipt and tried to use it in your car. In a Windows NT car, you would just get your own preferences again, because every receipt has the same customer preferences ID printed on it.

    According to the Win32 ABI for x86, the only thing you can do with the FS register is dereference it to access your TEB. If you try to fiddle with its value or try to copy its value somewhere, you are off in unsupported territory, and the resulting behavior is undefined.

  • The Old New Thing

    Where is the full version of the music that plays when you start Windows 98 for the first time?

    • 23 Comments

    A customer was presumably exercising the unlimited support part of their support contract when they asked, "Where is the full song for the music that plays when you start Windows 98 for the first time? The Welcome application plays only the first 30 seconds. Can you send us the rest of the song?"

    I guess the IT department really likes that music and wants the extended dance remix for their late-night raves. (If you stick it out, there's a special appearance in the linked video of the screen I wrote.)

    The music was commissioned to be 30 seconds long. There is no extended version of the song, or at least not one that Microsoft acquired the rights to. The original composer could have written a five-minute version and sent Microsoft only the first 30 seconds, or maybe the piece is only 30 seconds long to begin with. All we know is that we have a 30-second music clip.

  • The Old New Thing

    A trick for finding the correct internal mailing list

    • 12 Comments

    Suppose you have a problem and you want to report it to an internal mailing list, but you don't know the correct mailing list to use. Here's a trick that apparently is not that well-known, though perhaps that's a reflection on people's lack of creativity than any inherent difficulty in execution.

    For example, suppose you have a problem with the file system. One thing you can try is to go to the company Address Book and type "file system" and see what turns up.

    Of course, you have to be judicious about this. Some people use this trick but aren't very careful about their search terms, and they end up sending questions to mailing lists like "Windows Team All" which are intended for announcements, not Q&A.

    You want to look for a feature-specific discussion group or Q&A group or bug reporting group, not an entire product group.

    Use your common sense.

    In many cases, you have an idea of a person who might be able to help you. You may know the name of somebody from the file system team because you see their name in status report mail, or you find their name in the change logs, or because they helped you out once before. But, of course, you don't want to go to that person directly, because that would be presumptuous, and they might be on vacation, or they might be busy, or they might not be the right person after all.

    But what you can do is look up their entry in the company Address Book, then click on Membership to see what mailing lists they belong to. Browse the list looking for something named "File system bugs" or "File system talk" or "File system automated testing failures" or "File system code reviews", depending on what sort of help you need. If you know more than one person who might help, you can do some set theory to narrow down on a good candidate mailing list to contact.

    (Within Microsoft, a common convention is for mailing lists that deal with general Q&A to have "talk" in their name, whereas those that deal with urgent issues have "hot" in their name. And then there is "casual" which is where team members hang out and discuss the latest episode of Breaking Bad Mad Men Game of Thrones.)

    Bonus chatter: There is a Microsoft internal Web site called Polyarchy which visualizes organizational data. One of the many things you can do is give it a list of people, and it will show you the mailing lists that they all belong to, thereby doing the set theory magic I alluded to earlier.

  • The Old New Thing

    Sorry for the interruption, but it doesn't happen often

    • 22 Comments

    Many years ago, my feature manager and I were called into the project leader's office for some reason or other, I forget exactly what.

    We were about five minutes into the meeting when the project leader's mobile phone rang. This was back in the days when mobile phones were not commonplace, and having one was a way of showing off your status.

    The project leader answered the call, but instead of saying, "I'm in a meeting, can I call you back?" he proceeded to carry on a conversation with the caller as if we weren't there, while we sat there and waited.

    I got up and left his office.

    I went downstairs to the lobby and read whatever newspaper happened to be sitting there for visitors to read while they waited. I think it was The Wall Street Journal, but it could have been The New York Times.

    After finishing a section of the paper, I came back upstairs to the project leader's office. By that time, the project leader had finished his call, whatever it was.

    As I sat down, the project leader said, "Sorry, I don't get calls on this phone often."

    I immediately replied, "That's okay, I don't read The Wall Street Journal often either."

  • The Old New Thing

    Reinterpreting the bits of a 64-bit integer as if they were a double-precision floating point number (and vice versa)

    • 22 Comments

    Today's Little Program takes a 64-bit integer and reinterprets its physical representation as a double-precision floating point number.

    using System;
    
    class Program
    {
     static double ReinterpretAsDouble(long longValue)
     {
      return BitConverter.ToDouble(BitConverter.GetBytes(longValue), 0);
     }
    
     static long ReinterpretAsLong(double doubleValue)
     {
      return BitConverter.ToInt64(BitConverter.GetBytes(doubleValue), 0);
     }
    
     static void Main()
     {
      Console.WriteLine(ReinterpretAsDouble(0x4000000000000000));
      Console.WriteLine("{0:X}", ReinterpretAsLong(2.0));
     }
    }
    

    Our first attempt uses the Bit­Converter class to convert the 64-bit integer to an array of bytes, and then parses a double-precision floating point number from that byte array.

    Maybe you're not happy that this creates a short-lived byte[] array that will need to be GC'd. So here's another version that is a little sneakier.

    using System;
    using System.Runtime.InteropServices;
    
    class Program
    {
     [StructLayout(LayoutKind.Explicit)]
     struct LongAndDouble
     {
      [FieldOffset(0)] public long longValue;
      [FieldOffset(0)] public double doubleValue;
     }
    
     static double ReinterpretAsDouble(long longValue)
     {
      LongAndDouble both;
      both.doubleValue = 0.0;
      both.longValue = longValue;
      return both.doubleValue;
     }
    
     static long ReinterpretAsLong(double doubleValue)
     {
      LongAndDouble both;
      both.longValue = 0;
      both.doubleValue = doubleValue;
      return both.longValue;
     }
     ...
    }
    

    This version creates a structure with an unusual layout: The two members occupy the same physical storage. The conversion is done by storing the 64-bit integer into that storage location, then reading the double-precision floating point value out.

    There's a third method that involves writing the 64-bit integer to a memory stream via Binary­Writer then reading it back with Binary­Reader, but this is clearly inferior to the Bit­Converter so I didn't bother writing it up.

    Update: Damien points out that this functionality already exists in the BCL: Bit­Converter.Double­To­Int64­Bits and Bit­Converter.Int64­Bits­To­Double. But there doesn't appear to be a Bit­Converter.Float­To­Int32­Bits method, so the techniques discussed above are not completely useless.

    Exercise: Why did I have to initialize the doubleValue before writing to longValue, and vice versa? What are the implications of the answer to the above question? (Yes, I could have written LongAndDouble both = new LongAndDouble();, which automatically zero-initializes everything, but then I wouldn't have had an interesting exercise!)

  • The Old New Thing

    Random links on taking better pictures

    • 28 Comments

    I'm always interested in finding simple things you can do to take better pictures. Here are some links I've collected.

    One thing I discovered as a tourist is that if you ask a random person to take your picture (because you're traveling alone or because it's a group picture), they will usually gladly oblige, but they will also do a really bad job of framing the photo.

    Here's what I want: A head-and-shoulders shot of me with the object of interest.


    Δ

    Usually I'll get this: The photographer has zoomed out because they want to get my whole body (and my sneakers and my backpack that I put on the ground because I don't want it in the picture):


    Δ
    ||
    ¯ ¯


    At least some cropping and zooming can undo that.

    Worse is when the photographer tries to fix the problem by taking a few steps back, thereby changing the relative sizes of the object and me.


    Δ
    ||
    ¯ ¯


    When you take a few steps back, I get smaller but the object of interest remains the same size (since it is further away).

    I've tried a few things, like taking a sample photo to show what I want. ("Just do this again, but I'll be standing there.") It usually doesn't help. The volunteer photographer will take the picture they want.

    I can't really complain, because they were doing me a favor. But I've also learned to set my expectations appropriately and assume that any picture not taken by me will not be framed in a manner I like.

    This upcoming Sunday is National Selfie Day, according to some DJ in Texas.

  • The Old New Thing

    Why does the class name for Explorer change depending on whether you open it with /e?

    • 21 Comments

    I noted some time ago that Explorer's original name was Cabinet, and that the name lingers in the programmatic class name: Cabinet­WClass. A commenter with a rude name points out that Explorer uses the class name Explorer­WClass if you open it with the /e command line switch, adding, "This is rather strange since you can toggle the folder pane on/off in the UI either way."

    In Windows 95, the window class names for Explorer were Cabinet­WClass for plain Explorer windows and Explorer­WClass for windows opened in Explore mode with the folder tree view thingie on the left hand side. This was not strange at the time because there were two different types of Explorer windows, and there was no way to change between them. The UI to toggle the folder pane on/off did not exist.

    Internally, the two types of Explorer windows were handled by different frame window classes, and naturally the two different classes got different names. The plain Explorer window frame hosted a view window, an address bar, and a status bar, whereas the fancy Explorer window frame hosted those components plus a folder tree. It wasn't until some time later that the ability to toggle the folder pane on and off was added. To do this, the two window classes were merged into a single implementation that dynamically added in or removed the folder tree.

    Great, we can get rid of Explorer­WClass and just use Cabinet­WClass for everything.

    And then the application compatibility bug reports came in.

    Because even though it wasn't documented, application relied on the implementation detail that plain Explorer windows could be found by doing a Find­Window for Cabinet­WClass, and that fancy Explorer windows could be found by doing a Find­Window for Explorer­WClass. They would do things like launch explorer.exe /e C:\some\folder, wait a few seconds, and then do a Find­Window("Explorer­WClass", ...) and expect to find a window. (Just do a Web search for Cabinet­WClass and Explorer­WClass if you don't believe me.)

    For compatibility, therefore, Explorer windows still use the old class names from Windows 95. If you open the window with the folder pane hidden, the class name is Cabinet­WClass, and if you open it with the folder pane visible, the class name is Explorer­WClass. The two classes are functionally identical, but people who rely on undocumented behavior expect to see the same names from 1995.

  • The Old New Thing

    How come the technique for launching an unelevated process from an elevated process doesn't work?

    • 40 Comments

    A customer was following the Execute in Explorer sample to launch an unelevated process from an elevated process. (A sample which I rehashed some time ago.) The customer reported that the resulting process was still elevated.

    Upon closer inspection, the customer had disabled User Account Control (UAC).

    If UAC is disabled, then the ability for an administrative user to launch an unelevated process no longer exists.

    Since people like tables, here are some tables.

    In the classical world without UAC, administrators are administrators, and standard users are standard users. In other words, processes run by administrators are always elevated, and processes run by standard users are always non-elevated.

    UAC disabled
    User type Process type
    Elevated Non-elevated
    Administrator
    Standard

    UAC added a new option to the table: The administrator who voluntarily relinquishes administrative privilege and runs a process non-elevated.

    UAC enabled
    User type Process type
    Elevated Non-elevated
    Administrator
    Standard

    In words: In the classic non-UAC world, an administrative user can run processes elevated, and a standard user can run processes un-elevated. If UAC is enabled, then a new combination becomes available: An administrative user can run a process non-elevated.

    If you disable UAC, then you are back in the classic world, where there is no such thing as an administrative user running a non-elevated process. It's therefore no surprise that when you try to run the process unelevated, it still runs elevated.

    You can look at this issue another way: If UAC is disabled, then Explorer runs elevated. And therefore, if you ask Explorer to run a process, that process runs elevated too.

    It turns out that the customer turned off UAC because they didn't want to see any UAC prompts; they wanted their program to elevate silently, yet launch child processes unelevated. From a security-theoretical point of view, this is not an interesting configuration: If you allow silent elevation, then those child processes can just silently elevate themselves, and your attempt to run them unelevated accomplished nothing.

    If you disable UAC, then the only way to get both elevated processes and unelevated processes is to run the elevated processes as one user (an administrator) and the unelevated processes as another user (a standard user).

Page 3 of 455 (4,542 items) 12345»