December, 2007

  • The Old New Thing

    What was the role of MS-DOS in Windows 95?


    Welcome, Slashdot readers. Remember, this Web site is for entertainment purposes only.

    Sean wants to know what the role of MS-DOS was in Windows 95. I may regret answering this question since it's clear Slashdot bait. (Even if Sean didn't intend it that way, that's what it's going to turn into.)

    Here goes. Remember, what I write here may not be 100% true, but it is "true enough." (In other words, it gets the point across without getting bogged down in nitpicky details.)

    MS-DOS served two purposes in Windows 95.

    • It served as the boot loader.
    • It acted as the 16-bit legacy device driver layer.

    When Windows 95 started up, a customized version of MS-DOS was loaded, and it's that customized version that processed your CONFIG.SYS file, launched COMMAND.COM, which ran your AUTOEXEC.BAT and which eventually ran WIN.COM, which began the process of booting up the VMM, or the 32-bit virtual machine manager.

    The customized version of MS-DOS was fully functional as far as the phrase "fully functional" can be applied to MS-DOS in the first place. It had to be, since it was all that was running when you ran Windows 95 in "single MS-DOS application mode."

    The WIN.COM program started booting what most people think of as "Windows" proper. It used the copy of MS-DOS to load the virtual machine manager, read the SYSTEM.INI file, load the virtual device drivers, and then it turned off any running copy of EMM386 and switched into protected mode. It's protected mode that is what most people think of as "the real Windows."

    Once in protected mode, the virtual device drivers did their magic. Among other things those drivers did was "suck the brains out of MS-DOS," transfer all that state to the 32-bit file system manager, and then shut off MS-DOS. All future file system operations would get routed to the 32-bit file system manager. If a program issued an int 21h, the 32-bit file system manager would be responsible for handling it.

    And that's where the second role of MS-DOS comes into play. For you see, MS-DOS programs and device drivers loved to mess with the operating system itself. They would replace the int 21h service vector, they would patch the operating system, they would patch the low-level disk I/O services int 25h and int 26h. They would also do crazy things to the BIOS interrupts such as int 13h, the low-level disk I/O interrupt.

    When a program issued an int 21h call to access MS-DOS, the call would go first to the 32-bit file system manager, who would do some preliminary munging and then, if it detected that somebody had hooked the int 21h vector, it would jump back into the 16-bit code to let the hook run. Replacing the int 21h service vector is logically analogous to subclassing a window. You get the old vector and set your new vector. When your replacement handler is called, you do some stuff, and then call the original vector to do "whatever would normally happen." After the original vector returned, you might do some more work before returning to the original caller.

    One of the 16-bit drivers loaded by CONFIG.SYS was called IFSMGR.SYS. The job of this 16-bit driver was to hook MS-DOS first before the other drivers and programs got a chance! This driver was in cahoots with the 32-bit file system manager, for its job was to jump from 16-bit code back into 32-bit code to let the 32-bit file system manager continue its work.

    In other words, MS-DOS was just an extremely elaborate decoy. Any 16-bit drivers and programs would patch or hook what they thought was the real MS-DOS, but which was in reality just a decoy. If the 32-bit file system manager detected that somebody bought the decoy, it told the decoy to quack.

    Let's start with a system that didn't contain any "evil" drivers or programs that patched or hooked MS-DOS.

    Program calls int 21h

    32-bit file system manager
    checks that nobody has patched or hooked MS-DOS
    performs the requested operation
    updates the state variables inside MS-DOS
    returns to caller
    Program gets result

    This was paradise. The 32-bit file system manager was able to do all the work without having to deal with pesky drivers that did bizarro things. Note the extra step of updating the state variables inside MS-DOS. Even though we extracted the state variables from MS-DOS during the boot process, we keep those state variables in sync because drivers and programs frequently "knew" how those state variables worked and bypassed the operating system and accessed them directly. Therefore, the file system manager had to maintain the charade that MS-DOS was running the show (even though it wasn't) so that those drivers and programs saw what they wanted.

    Note also that those state variables were per-VM. (I.e., each MS-DOS "box" you opened got its own copy of those state variables.) After all, each MS-DOS box had its idea of what the current directory was, what was in the file tables, that sort of thing. This was all an act, however, because the real list of open files was kept in by the 32-bit file system manager. It had to be, because disk caches had to be kept coherent, and file sharing need to be enforced globally. If one MS-DOS box opened a file for exclusive access, then an attempt by a program running in another MS-DOS box to open the file should fail with a sharing violation.

    Okay, that was the easy case. The hard case is if you had a driver that hooked int 21h. I don't know what the driver does, let's say that it's a network driver that intercepts I/O to network drives and handles them in some special way. Let's suppose also that there's some TSR running in the MS-DOS box which has hooked int 21h so it can print a 1 to the screen when the int 21h is active and a 2 when the int 21h completes. Let's follow a call to a local device (not a network device, so the network driver doesn't do anything):

    Program calls int 21h

    32-bit file system manager
    notices that somebody has patched or hooked MS-DOS
    jumps to the hook (which is the 16-bit TSR)

    16-bit TSR (front end)
    prints a 1 to the screen
    calls previous handler (which is the 16-bit network driver)

    16-bit network driver (front end)
    decides that this isn't a network I/O request calls previous handler (which is the 16-bit IFSMGR hook)

    16-bit IFSMGR hook
    tells 32-bit file system manager
      that it's time to make the donuts

    32-bit file system manager
    regains control
    performs the requested operation
    updates the state variables inside MS-DOS
    returns to caller

    16-bit network driver (back end)
    returns to caller

    16-bit TSR (back end)
    prints a 2 to the screen
    returns to caller
    Program gets result

    Notice that all the work is still being done by the 32-bit file system manager. It's just that the call gets routed through all the 16-bit stuff to maintain the charade that 16-bit MS-DOS is still running the show. The only 16-bit code that actually ran (in red) is the stuff that the TSR and network driver installed, plus a tiny bit of glue in the 16-bit IFSMGR hook. Notice that no 16-bit MS-DOS code ran. The 32-bit file manager took over for MS-DOS.

    A similar sort of "take over but let the crazy stuff happen if somebody is doing crazy stuff" dance took place when the I/O subsystem took over control of your hard drive from 16-bit device drivers. If it recognized the drivers, it would "suck their brains out" and take over all the operations, in the same way that the 32-bit file system manager took over operations from 16-bit MS-DOS. On the other hand, if the driver wasn't one that the I/O subsystem recognized, it let the driver be the one in charge of the drive. If this happened, it was said that you were going through the "real-mode mapper" since "real mode" was name for the CPU mode when protected mode was not running; in other words, the mapper was letting the 16-bit drivers do the work.

    Now, if you were unlucky enough to be using the real-mode mapper, you probably noticed that system performance to that drive was pretty awful. That's because you were using the old clunky single-threaded 16-bit drivers instead of the faster, multithread-enabled 32-bit drivers. (When a 16-bit driver was running, no other I/O could happen because 16-bit drivers were not designed for multi-threading.)

    This awfulness of the real-mode mapper actually came in handy in a backwards way, because it was an early indication that your computer got infected with an MS-DOS virus. After all, MS-DOS viruses did what TSRs and drivers did: They hooked interrupt vectors and took over control of your hard drive. From the I/O subsystem's point of view, they looked just like a 16-bit hard disk device driver! When people complained, "Windows suddenly started running really slow," we asked them to look at the system performance page in the control panel and see if it says that "Some drives are using MS-DOS compatiblity." If so, then it meant that the real-mode mapper was in charge, and if you didn't change hardware, it probably means a virus.

    Now, there are parts of MS-DOS that are unrelated to file I/O. For example, there are functions for allocating memory, parsing a string containing potential wildcards into FCB format, that sort of thing. Those functions were still handled by MS-DOS since they were just "helper library" type functions and there was no benefit to reimplementing them in 32-bit code aside from just being able to say that you did it. The old 16-bit code worked just fine, and if you let it do the work, you preserved compatibility with programs that patched MS-DOS in order to alter the behavior of those functions.

  • The Old New Thing

    Why aren't console windows themed on Windows XP?


    Commenter Andrej Budja asks why cmd.exe is not themed in Windows XP. (This question was repeated by Serge Wautier, proving that nobody checks whether their suggestion has already been submitted before adding their own. It was also asked by a commenter who goes by the name "S", and then repeated again just a few hours later, which proves again that nobody reads the comments either.) Knowledge Base article 306509 explains that this behavior exists because the command prompt window (like all console windows) is run under the ClientServer Runtime System (CSRSS), and CSRSS cannot be themed.

    But why can't CSRSS be themed?

    CSRSS runs as a system service, so any code that runs as part of CSRSS creates potential for mass havoc. The slightest mis-step could crash CSRSS, and with it the entire system. The CSRSS team decided that they didn't want to take the risk of allowing the theme code to run in their process, so they disabled theming for console windows. (There's also an architectural reason why CSRSS cannot use the theming services: CSRSS runs as a subsystem, and the user interface theming services assume that they're running as part of a Win32 program.)

    In Windows Vista, the window frame is drawn by the desktop window manager, which means that your console windows on Vista get the glassy frame just like other windows. But if you take a closer look, you will see that CSRSS itself doesn't use themed windows: Notice that the scroll bars retain the classic look.

    The window manager giveth and the window manager taketh away, for at the same time console windows gained the glassy frame, they also lost drag and drop. You used to be able to drag a file out of Explorer and drop it onto a command prompt, but if you try that in Windows Vista, nothing happens. This is a consequence of tighter security around the delivery of messages from a process running at lower integrity to one running at a higher integrity level (see UIPI). Since CSRSS is a system process, it runs at very high security level and won't let any random program (like Explorer) send it messages, such as the ones used to mediate OLE drag and drop. You'll see the same thing if you log on as a restricted administrator and then kick off an elevated copy of Notepad. You won't be able to drag a file out of Explorer and drop it onto Notepad, for the same reason.

  • The Old New Thing

    How do 16-bit programs start up?


    Back in 16-bit Windows, MS-DOS cast a long and dark shadow. The really ugly low-level munging was very much in the MS-DOS spirit. You opened files by setting up registers and issuing an int 21h, just like in MS-DOS. Although the interrupt went to Windows instead, Windows maintained the MS-DOS calling convention. Process startup followed the same "real men write in assembly language" philosophy.

    All the parameters to a 16-bit program were passed in registers. The entry point to a 16-bit process received the following parameters on Windows 3.1:

    AXzero (used to contain even geekier information in Windows 2)
    BXstack size
    CXheap size
    DXunused (reserved)
    SIprevious instance handle
    DIinstance handle
    BPzero (for stack walking)
    DSapplication data segment
    ESselector of program segment prefix
    SSapplication data segment (SS=DS)
    SPtop of stack

    Hey, nobody said that 16-bit Windows was designed for portability.

    The first thing a 16-bit program did was call the InitTask function. This function receives its parameters in registers, precisely in the format that they are received by the program entry point. The InitTask function initializes the stack, the data segment, the heap, retrieves and prepares the command line, recovers the nCmdShow parameter that was passed to WinExec, all the normal startup stuff. It even edits the stack of the caller so that real-mode stack walking works (critical for memory management in real-mode). When InitTask is all finished, it returns with the registers set for the next phase:

    AXselector of program segment prefix (or 0 on error)
    BXoffset of command line
    CXstack limit
    SIprevious instance handle
    DIinstance handle
    BPtop of stack (for stack walking)
    DSapplication data segment
    ESselector of command line
    SSapplication data segment (SS=DS)
    SPedited top of stack

    Once InitTask returns, the stack, heap, and data segment are "ready to run," and if you have no other preparations to do, you can head right for the application's WinMain function. Minimal startup code therefore would go like this:

        call    far InitTask
        test    ax, ax
        jz      exit
        push    di      ; hInstance
        push    si      ; hPrevInstance
        push    es      ; lpszCmdLine selector
        push    bx      ; lpszCmdLine offset
        push    dx      ; nCmdShow
        ... some lines of code that aren't important to the discussion ...
        call    far WinMain ; call the application's WinMain function
        ; return value from WinMain is in the AL register,
        ; conveniently positioned for the exit process coming up next
        mov     ah, 4Ch ; exit process function code
        int     21h     ; do it

    Why wasn't the application entry point called main? Well, for one thing, the name main was already taken, and Windows didn't have the authority to reserve an alternate definition. There was no C language standardization committee back then; C was what Dennis said it was, and it was hardly guaranteed that Dennis would take any special steps to preserve Windows source code compatibility in any future version of the C language. Since K&R didn't specify that implementations could extend the acceptable forms of the main function, it was entirely possible that there was a legal C compiler that rejected programs that declared main incorrectly. The current C language standard explicitly permits implementation-specific alternate definitions for main, but requiring all compilers to support this new Windows-specific version in order to compile Windows programs would gratuitously restrict the set of compilers you could use for writing Windows programs.

    If you managed to overcome that obstacle, you'd have the problem that the Windows version of main would have to be something like this:

    int main(int argc, char *argv[], HINSTANCE hinst,
             HINSTANCE hinstPrev, int nCmdShow);

    Due to the way C linkage was performed, all variations of a function had to agree on the parameters they had in common. This means that the Windows version would have to add its parameters onto the end of the longest existing version of main, and then you'd have to cross your fingers and hope that the C language never added another alternate version of main. If you went this route, your crossed fingers failed you, because it turns out that a third parameter was added to main some time later, and it conflicted with your Windows-friendly version.

    Suppose you managed to convince Dennis not to allow that three-parameter version of main. You still have to come up with those first two parameters, which means that every program's startup code needs to contain a command line parser. Back in the 16-bit days, people scrimped to save every byte. Telling them, "Oh, and all your programs are going to be 2KB bigger" probably wouldn't make you a lot of friends. I mean, that's four sectors of I/O off a floppy disk!

    But probably the reason why the Windows entry point was given a different name is to emphasize that it's a different execution environment. If it were called main, people would take C programs designed for a console environment, throw them into their Windows compiler, and then run them, with disastrous results.

  • The Old New Thing

    How long does it take to create a 16TB file?


    Although the theoretical maximum file size on NTFS is 264−1 clusters, the current implementation of the NTFS driver supports files up to "only" 16TB minus 64KB. (In other words, the disk format supports files up to 264−1 clusters, but the current drivers won't go above 16TB−64KB.)

    Back in 2002, in order to verify that the drivers did indeed support files as big as their design maximum, the NTFS test team sat down, created a volume slightly bigger than 16TB, and then created a file of the maximum supported size, filling it with a known pattern. After the file was successfully created, they then ran another program that read the entire file back into memory and verified that the contents were correct. (They ran other tests, too, of course, but those are the ones that are important to this story.)

    How long did it take to create this nearly-16TB file?

    Around three days.

    Verifying that the data was written correctly took about four days.

    (Yes, it's strange that reading was slower than writing. I don't know why, but I can guess and so can you. Maybe the read test did a bunch of extra verification. Maybe the read test used random access as well as sequential access. Or maybe there was just rounding error in the reporting of the duration. I wasn't there, so I don't know for sure.)

  • The Old New Thing

    Management-speak: Norming around mechanisms


    This is the entire text of an actual piece of email I received from a high-level manager in response to some feedback I sent.


    There is a lot of norming around any of these mechanisms as well as a certain amount of ability to hold ones ground in these interactions in addition to the admin of the rule being good enough as discussed.

    Bob (not the manager's actual name)

    I've heard rumors that this message was written in English, but I'm not convinced.

  • The Old New Thing

    How did wildcards work in MS-DOS?


    The rules were simple but led to complicated results.

    MS-DOS files were eleven characters long with an implicit dot between characters eight and nine. Theoretically, spaces were permitted anywhere, but in practice they could appear only at the end of the file name or immediately before the implicit dot.

    Wildcard matching was actually very simple. The program passed an eleven-character pattern; each position in the pattern consisted either of a file name character (which had to match exactly) or consisted of a question mark (which matched anything). Consider the file "ABCD····TXT", where I've used · to represent a space. This file name would more traditionally be written as ABCD.TXT, but I've written it out in its raw 11-character format to make the matching more obvious. Let's look at some patterns and whether they would match.

    ABCD····TXT Match exact
    ??????????? Match all positions are a wildcard so of course they match
    ABCD????··· No match space (position 9) does not match T
    A?CD····??? match perfect match at A, C, D, and the spaces; wildcard match at the question marks

    The tricky part is converting the traditional notation with dots and asterisks into the eleven-character pattern. The algorithm used by MS-DOS was the same one used by CP/M, since MS-DOS worked hard at being backwards compatible with CP/M. (You may find some people who call this the FCB matching algorithm, because file names were passed to and from the operating system in a structure called a File Control Block.)

    1. Start with eleven spaces and the cursor at position 1.
    2. Read a character from the input. If the end of the input is reached, then stop.
    3. If the next character in the input is a dot, then set positions 9, 10, and 11 to spaces, move the cursor to position 9, and go back to step 2.
    4. If the next character in the input is an asterisk, then fill the rest of the pattern with question marks, move the cursor to position 12, and go back to step 2. (Yes, this is past the end of the pattern.)
    5. If the cursor is not at position 12, copy the input character to the cursor position and advance the cursor.
    6. Go to step 2.

    Let's parse a few patterns using this algorithm, since the results can be surprising. In the diagrams, I'll underline the cursor position.

    First, let's look at the traditional "ABCD.TXT".

    ··········· Initial conditions
    A A·········· Copy to cursor and advance the cursor
    B AB········· Copy to cursor and advance the cursor
    C ABC········ Copy to cursor and advance the cursor
    D ABCD······· Copy to cursor and advance the cursor
    . ABCD······· Blank out positions 9, 10, and 11 and move cursor to position 9
    T ABCD····T·· Copy to cursor and advance the cursor
    X ABCD····TX· Copy to cursor and advance the cursor
    T ABCD····TXT  Copy to cursor and advance the cursor

    The final result is what we expected: ABCD····TXT.

    Let's look at a weird case: the pattern is ABCDEFGHIJKL.

    ··········· Initial conditions
    A A·········· Copy to cursor and advance the cursor
    B AB········· Copy to cursor and advance the cursor
    C ABC········ Copy to cursor and advance the cursor
    D ABCD······· Copy to cursor and advance the cursor
    E ABCDE······ Copy to cursor and advance the cursor
    F ABCDEF····· Copy to cursor and advance the cursor
    G ABCDEFG···· Copy to cursor and advance the cursor
    H ABCDEFGH··· Copy to cursor and advance the cursor
    I ABCDEFGHI·· Copy to cursor and advance the cursor
    J ABCDEFGHIJ· Copy to cursor and advance the cursor
    K ABCDEFGHIJK  Copy to cursor and advance the cursor

    Sure, this was extremely boring to watch, but look at the result: What you got was equivalent to ABCDEFGH.IJK. The dot is optional if it comes after exactly eight characters!

    Next, let's look at the troublesome A*B.TXT.

    ··········· Initial conditions
    A A·········· Copy to cursor and advance the cursor
    * A??????????  Fill rest of pattern with question marks and move to position 12
    B A??????????  Do nothing since cursor is at position 12
    . A???????··· Blank out positions 9, 10, and 11 and move cursor to position 9
    T A???????T·· Copy to cursor and advance the cursor
    X A???????TX· Copy to cursor and advance the cursor
    T A???????TXT  Copy to cursor and advance the cursor

    Notice that the result is the same as you would have gotten from the pattern A*.TXT. Any characters other than a dot that come after an asterisk have no effect, since the asterisk moves the cursor to position 12, at which point nothing changes the parse state except for a dot, which clears the last three positions and moves the cursor.

    I won't work it out here, but if you stare at it for a while, you'll also discover that *.* is the same as * by itself.

    In addition to the rules above, the MS-DOS command prompt had some quirks in its parsing. If you typed DIR .TXT, the command prompt acted as if you had typed DIR *.TXT; it silently inserted an asterisk if the first character of the pattern was a dot. This behavior was probably by accident, not intentional, but it was an accident that some people came to rely upon. When we fixed the bug in Windows 95, more than one person complained that their DIR .TXT command wasn't working.

    The FCB matching algorithm was abandoned during the transition to Win32 since it didn't work with long file names. Long file names can contain multiple dots, and of course files can be longer than eleven characters, and there can be more than eight characters before the dot. But some quirks of the FCB matching algorithm persist into Win32 because they have become idiom.

    For example, if your pattern ends in .*, the .* is ignored. Without this rule, the pattern *.* would match only files that contained a dot, which would break probably 90% of all the batch files on the planet, as well as everybody's muscle memory, since everybody running Windows NT 3.1 grew up in a world where *.* meant all files.

    As another example, a pattern that ends in a dot doesn't actually match files which end in a dot; it matches files with no extension. And a question mark can match zero characters if it comes immediately before a dot.

    There may be other weird Win32 pattern matching quirks, but those are the two that come to mind right away, and they both exist to maintain batch file compatibility with the old 8.3 file pattern matching algorithm.

  • The Old New Thing

    You mean, you have computers in Taiwan?


    The wife of one of my relatives grew up in Taiwan and attended college in the United States. When she went home to Taiwan for the holidays, she would tell her classmates, "If you need to get in touch while I'm away, you can send me email."

    One of them asked her, "You mean, you have computers in Taiwan?"

    (Hint: It's the home of the world's third largest PC manufacturer behind Dell and HP.)

    One of her friends grew up in Japan. When she moved to the United States at around age twelve and was introduced to her new school, her classmates were excited to hear about what it's like living in Japan. One of them asked her, "Do you have television in Japan?"

    When I told this story, one of my colleagues added his own version: "When I came to the USA from England, an 11-year-old asked me if we had electricity there."

  • The Old New Thing

    The magical healing properties of safe mode - bonus content


    Okay, so you already read The healing properties of safe mode in TechNet Magazine. Here's the bonus content that was cut for space.

    First, the original title was "The Magical Healing Powers of Safe Mode," but it got trimmed for space reasons. (Ich bin mit der deutschen Übersetzung des ersten Satzes ein bisschen enttäuscht. Die eingeklammerte Phrase bittet um einen von den berühmten nur auf Deutsch gesehenen unverständlich langen adjektivischen Ausdrücken. Anstatt dessen hat der Übersetzer aufgegeben und die Phrase einfach weggelassen. Anderseits benutzt die deutsche Version den ursprünglichen Titel, so vielleicht ist es ja nicht so schlecht.)

    Useless Windows history: The feature now known as safe mode went through many other names before the final name was settled upon.

    • Fail-safe boot
    • FailSafe boot
    • Fail-safe mode
    • Safe mode
  • The Old New Thing

    When selecting system colors, match but don't mix


    Here's a question that came in from a customer:

    Is there a way to view all the Windows color schemes at once? We want to display text in the COLOR_BTNTEXT color against a background of COLOR_INACTIVECAPTION, and we want to check that this looks good in all of the themes.

    A mistake I see from some programs is mixing system colors that are not meant to be mixed. The colors I'm talking about are the ones obtained from the GetSysColor function. Here are the text and background color pairs, with a sample of what those colors are on a default install of Windows XP.

    Text Background Sample

    If you're going to combine colors, and you need them to contrast against each other (for example, because you're going to draw text with them as the foreground and background colors), choose a pair from one of the rows above. Do not choose colors from different rows because there is no guarantee that they will be readable against each other.

    For example, I like to use black on #71FFFF as my color scheme for highlighted text. I've seen programs which break the above rule and draw text in the COLOR_HIGHLIGHT color against a background of COLOR_WINDOW, on the assumption that the highlight color contrasts against the window color. (They get away with this in the default Windows XP color scheme because the window color is white and the highlight color is medium blue.) Unfortunately, on my machine, this results in text that is extremely painful on the eyes.

    Remember: When it comes to system colors, match. Don't mix.

  • The Old New Thing

    Microthink: If you can't measure it, then it doesn't exist


    At Microsoft, there is an obsession with measurement. If you can't measure it, then it doesn't exist. As a result, we set up data collection mechanisms, and try to interpret that data, even if the data isn't what we're really interested in, but we act as if it is. Because it's what we know how to do. (If all you have is a hammer...)

    A classic example of this is trying to gauge the impact of blogging. Microsoft employees who are considering taking up the practice ask questions about measurement.

    I want to measure the impact of my blog. I'd like to put a survey at the bottom of my blog that asks people "Did this blog posting prevent a call to Microsoft product support?" or "Was this blog posting helpful?" or "Rate this blog posting on a scale of 1 to 10." Then I can generate reports based on what people think so I can see how effective I am. Somebody in sales might ask "Did this blog posting convince you to buy a Microsoft product?" A developer might ask "Did this blog posting help you integrate your third-party product with Microsoft Windows?"

    This smells like "I must make this quantitative and measurable so I can make it a review goal to increase my blog's 'impact' by 25%." In my opinion, blogging isn't like that. Blogging is more about creating an atmosphere. Sure, individual entries may solve specific problems, but the cumulative effect is the goal. Using a survey to measure the impact of a blog entry is like having somebody fill out a survey after you give them a ride home because you want to determine the impact that one action had on how nice a person they think you are.

    Questions about measuring the impact of blogs will never go away because Microsoft is all about measurement. Many people believe that if you can't measure it, then you can't claim on your annual performance review.

Page 1 of 4 (37 items) 1234