July, 2010

  • The Old New Thing

    Suggestion Box 4


    The topic backlog from Suggestion Box 3 has nearly cleared out, and I've actually been enjoying not having to write up a reply every Monday for the past several months, but all good things must come to an end, and so, without much fanfare, we now have Suggestion Box 4.

    Remember, the suggestion box is for suggestions for future topics. It isn't for developer support, bug reports, or ranting. Topics I'm inclined to cover:

    • Windows history (particularly the Windows 95 era).
    • Windows user interface programming in Win32, and shell programming in particular.
    • General programming topics (selectively).
    • Issues of general interest.
    • My personal hobbies.

    Topics I am not inclined to cover:

    • The blog software itself. You can visit the Community Server home page and cruise their support forums.
    • Internet Explorer. You can try the IE folks.
    • Visual Studio. You can try one of the Visual Studio blogs.
    • Managed code. This is not a .NET blog. I do not work on .NET technologies. As far as .NET is concerned, I'm just another programmer like you. Occasionally I touch a .NET-related topic (including the annual "CLR Week"), but I do not bring any inside expertise to the subject.
    • Non-software Microsoft topics, such as product support policies, marketing tactics, jobs and careers, legal issues.
    • Microsoft software that isn't Windows. (Exchange, Office, ...)
    • Windows topics outside user interface programming. (Plug and Play, Terminal Services, Windows Messenger, Outlook Express, SQL, IIS, remoting, SOA...)
    • User interface programming in anything other than Win32. (Because I know nothing about it.)
    • Debugging a specific problem. (Not of general interest.)
    • Predictions for the future. (What's the title of this blog again?)
    • Participation in Internet memes.

    Selected products at Microsoft participate in the Connect program, and many more have official blogs.

    Suggestions should be between two and four sentences in length. Think of it as an elevator pitch: You have three seconds to get your point across. Please also search the Web site first because your suggestion may have already been covered. (Possibly as a suggestion that was submitted to an earlier Suggestion Box that was not accepted.) And remember, questions aren't suggestions.

    The Suggestion Box will be open for only two weeks, and I will be much more selective about which one I choose to accept than in previous go-rounds. I'll answer one every Monday of 2012 (minus holidays and special events such as CLR Week), and once the end of the year is reached, that's the end of Suggestion Box 4.

  • The Old New Thing

    Why didn't Windows XP auto-elevate programs beyond those named setup.exe?


    Commenter J-F has a friend who wonders why Windows XP didn't auto-elevate all installers but rather only the ones named setup.exe. (Perhaps that friend's name is Josh, who repeated the question twelve days later.)

    Remember what the starting point was. In Windows 2000, nothing was auto-elevated.

    Before adding a feature, you have to know what problem the feature is trying to solve. The problem is improving the experience for non-administrators who want to install software. When they try to install a program and forget to use the Run as feature, then instead of proceeding halfway through the installer and then getting an Access denied error, do the Run as for them automatically.

    Knowing whether the user is running an installer that requires elevation requires a degree of semantic analysis beyond what you want to add to the Create­Process code path. Hey, here's a program called PRONT4.EXE. Is it an installer? Turns out that it is. And then there are the programs that might be installers, depending on what other command line switches you provide.

    Given that you're reduced to a heuristic, you have to decide what the acceptable rates of false positives and false negatives will be. If you guess wrong and think a program requires administrator privileges when it doesn't, then you've screwed over all the non-administrators who want to use the program. "I used to be able to run this program, but now when I try, I'm asked for the administrator password, which I do not know. Windows broke my program." The effect of a false positive is My program stops working.

    On the other hand, if you fail to detect a program that requires being run with administrator privileges, the behavior is the same as before: The user gets an Access denied error. The effect of a false negative is No change.

    Given that the cost of a false positive is huge and the cost of a false negative is zero, you can see that the math says to use a conservative heuristic. The heuristic is that a program named setup.exe will be treated as an installation program, and nothing else.

    Windows was under no obligation to auto-detect installation programs. Indeed, according to the strict interpretation of operating system design, it shouldn't do this. If the user says to run this program at the current privilege level, then you darned well better run the program with the current privilege level. The treatment of programs named setup.exe is really just a compatibility hack, a courtesy to make your life a little bit easier.

    It's a case of giving somebody five dollars and being asked why you didn't give them ten.

    Starting in Windows Vista, applications can specify via a manifest whether they want to run at the privilege level the user requested (requested­Execution­Level level="as­Invoker") or always to elevate to administrator (requestedExecution­Level level="require­Administrator"). Hopefully, all new applications will specify their elevation requirements explicitly, and the heuristic will be necessary only for old programs.

  • The Old New Thing

    No, you can't lock a gadget to the top of the sidebar


    In another installment of I bet somebody got a really nice bonus for that feature, I offer you this customer:

    My customer has created a Windows Vista sidebar gadget and wants to know if there's a way to force this gadget to appear at the top of the sidebar and prevent the user from moving or removing it.

    I applaud this company for having written the most awesome sidebar gadget in the history of the universe. It's so compelling that it should override the user's preferences and force itself into the upper right corner of their screen in all perpetuity.

    Unfortunately, Windows was not prepared for a program as awesome as this, and there is no supported way to force a gadget into a particular position and prevent the user from moving or removing it.

  • The Old New Thing

    Tips for planning your ship party


    Not saying how I know these things. Just making a little list for reference.

    • If you plan on staying dry, do not hold the party near a fountain. (Note that fountain avoidance is a necessary but not sufficient criterion.)
    • Corollary: If your team members have an armory of super soaker water cannons, and you plan on staying dry, then absolutely do not hold the party near a fountain.
    • Unrolled spools of bubble wrap are a poor choice of equipment when trying to climb from the lobby to the upper floor balcony.
    • After discovering that bubble wrap is unsuitable for climbing, do not upend a glass table in an attempt to gain a higher starting point.
    • A contest to see who can run and break through a plaster wall is not a recommended choice of impromptu amusement.
    • Do not throw a couch from the upper floor balcony. Not even if it's on fire.
    • Costco underwear does not count as swim trunks.
    • Do not have a tug of war over a vat of Jell-O.
    • The expensive sculpture outside your building is not a water storage tank in need of refilling.
    • Exercise caution when driving your motorcycle through the halls. The carpet damage from your burnout can be repaired, but the patches never really look the same.
    • On the day of the ship party, do not wear a nice suit. You might be thrown into the back of a pick-up truck and buried in ice.

    Bonus: Other lessons learned.

  • The Old New Thing

    Hardware backward compatibility: The finicky floppy drive


    I think the behavior is more petulant than finicky, but finicky is alliterative.

    Back in the days of Windows 95, I was talking with the person responsible for, among other things, the floppy disk driver, and I learned about a particular driver hack that was needed to work around a flaw in a very common motherboard chipset.

    Apparently the floppy disk controller in this chipset was very picky about how you talked to it. If the very first command it receives after power-on is a read request, and there is no disk in the drive, the controller chip hangs unrecoverably. Issuing a reset to the chip has no effect. It's gone. You have to power-cycle the machine and try again.

  • The Old New Thing

    Hardware backward compatibility: The firmware that missed one tiny detail


    The person responsible for the floppy disk driver in Windows 95 also was responsible for the low-level CD-ROM drivers. (Not to be confused with the CDFS file system, which was handled by the file system team, not the hardware driver folks.) And I remember a story about one particularly strange CD-ROM drive.

    This drive was produced by a name-brand manufacturer. The box that the drive comes in proudly announces that it is an IDE ATAPI drive. And they did a fantastic job. They implemented all the ATAPI commands that were defined at the time, with one tiny exception.

    They forgot to implement the "Are you an ATAPI drive?" command.

  • The Old New Thing

    To enable and disable a window, use the EnableWindow function


    Commenter Chris 'Xenon' Hanson points out that fiddling with the WS_DISABLED style directly via Set­Window­Long leads to strange behavior. However it isn't the case that "most widget classes work fine." Reaching in and fiddling the style bit directly is like reaching into a program's internal variables and just changing the values: All the other work that is associated with changing the value simply doesn't happen.

    It's like taking a book you checked out of the library, re-shelving it, and then going into the library computer and marking it as "returned". The bookkeeping will say that the book has been returned, but all the other processes associated with a book return has not taken place: People who had placed a hold on the book aren't notified. The "number of books checked out" counter isn't updated. (Which gets interesting when you come to the end of your senior year and the system won't let you graduate because its records say that you still have 1 book outstanding, yet when you say "Show me all the books I have checked out" it returns no records.)

    In the case of windows, merely setting the WS_DISABLED style does not generate WM_ENABLE messages, it doesn't generate accessibility notifications, it doesn't do focus bookkeeping, all it does is set the flag and goes home. Eventually, some code will stop working because something "impossible" happened (in this case, a window transitioning from enabled to disabled without ever receiving a WM_ENABLE message).

    Similarly, the way to change a window's visible state is to use the Show­Window function and not to manipulate the WS_VISIBLE style directly.

    "I think I filed a suggestion on MSDN2.microsoft.com's suggestion box to advise people not to fiddle with the WS_DISABLED flag at runtime via Set­Window­Long() since it seems like a viable route if you don't know otherwise."

    Actually, the advice already exists right at the top of the Window Styles page where it says "After the control has been created, these styles cannot be modified, except as noted." And for WS_DISABLED, it says "To change this after a window has been created, use Enable­Window."

  • The Old New Thing

    The frustration of people who have already decided on the solution and won't let you derail them with your annoying questions


    I illustrate this frustration with an actual mail thread (suitably redacted) which I was an observer to. It's a long thread because that's part of the frustration.

    From: Adam

    I am looking for some expert advice here on finding a better solution to our performance problem with Product P. Here are the details.

    [Here follow the details on a problem and three proposed solutions. Feature F is mentioned briefly and rejected because "it will be a problem because of Condition C."]

    From: Bob

    This approach is prone to a lot of trouble.

    Please be more specific about what was wrong with Feature F.

    From: Adam

    We were concerned about Scenario S when we are in Condition C.

    From: Bob

    But Condition C is exactly why Feature F was developed.

    From: Charles

    How big is Scenario S? I'm part of a team that is even worse than Condition C, and we haven't noticed any problems. But maybe your Scenario S is a lot bigger than ours.

    From: David

    Stepping back a bit and emphasizing Charles's point: You rejected Feature F because "it will be a problem." Have you actually tried it? Set up a testbed with Condition C, turn on Feature F, and try it out. Do you actually see problems you predict? As with any performance problem, the first thing you have to do is measure. Information like "A job of size X takes M minutes to run to completion" gives us something to work from.

    From: Adam

    Actually, I know what Charles's situation is, and our Condition C is worse than Charles's. And we found that when we ran Scenario S on a default configuration, it was really painful.

    From: Edward

    Can you be more specific about what the pain was?

    From: Adam

    Please focus on the topic we originally asked and let the experts help us solve our performance problem.

    From: Edward

    I am the Product P expert who designed and implemented Feature F. I would like to help you. The measurement suggested by David would help everyone understand your situation. Feature F was specifically designed to improve performance in Condition C. If you assume that it will not work without even trying it, then the first thing you need to do is turn it on and try it and measure it.

    By the way, your second proposed solution is missing some important steps. If you want to continue along that plan, then you should refer to the Scenario Z section of this whitepaper I wrote (part of the Product P Resource Kit) and use that as a template.

    From: Adam

    Thanks for your great whitepaper. It's really useful! I will test it in our lab next week.

    That was the last we heard from Adam.

    It's sort of the whitepaper version of somebody who is just looking for the magic thing to type without trying to understand why it works or what its advantages, disadvantages, and limitations are.

    My guess is that Adam is just going to plunge ahead with his proposed solution #2 even though it's probably not the best solution. He already decided what he was going to do and just wanted somebody to sign off on his solution. He wasn't interested in learning about other options, as demonstrated by his blind rejection of Feature F (which was in fact specifically designed to address his situation).

    It was also strange hearing Adam demand that we avoid annoying tangents (like trying to understand what exactly the problem is) and let the experts solve his problem. Even though everybody involved in the discussion knew far more about Product P than he does, and that he singled out the most qualified expert for his scolding.

  • The Old New Thing

    If I'm not supposed to call IsBadXxxPtr, how can I check if a pointer is bad?


    Some time ago, I opined that Is­Bad­Xxx­Ptr should really be called Crash­Program­Randomly and you really should just let the program crash if somebody passes you a bad pointer. It is common to put pointer validation code at the start of functions for debugging purposes (as long as you don't make logic decisions based on whether the pointer is valid). But if you can't use Is­Bad­Xxx­Ptr, how can you validate the pointer?

    Well, to validate a write pointer, write to it. To validate a read pointer, read from it. If the pointer is invalid, you'll crash, and at a predictable location, before the function has gotten halfway through its processing (making post-mortem debugging more difficult). Here are the functions I used:

    // Make sure to disable compiler optimizations in these functions
    // so the code won't be removed by the optimizer.
    void DebugValidateWritePtr(void *p, size_t cb)
     memcpy(p, p, cb);
    void DebugValidateReadPtr(void *p, size_t cb)
     memcmp(p, p, cb);

    To verify that a buffer can be written to, we write to it by copying it to itself. Similarly, to verify that a buffer can be read, we read from it by comparing it to itself. The result of the operation is not important; we are interested in the side-effect of the memory access itself.

    Note that the Debug­Validate­Write­Ptr function is not thread-safe: If another thread modifies the buffer while we are copying it to itself, the write may be lost. But code that does this violates one of the ground rules for programming (specifically the parameter stability requirements). Of course, if your function has specific behavior requirements beyond the ground rules, then that helper function may not work for you. I'm just putting it out there as a courtesy.

  • The Old New Thing

    How do I configure a Remote Desktop Connection shortcut to open on a specific monitor?


    A customer wanted to know how to configure a Remote Desktop Connection shortcut so that the session appears on the monitor of choice. "I have two RDP shortcuts, and each one displays on a different monitor, but I want them all to display on my 20-inch monitor. How do I tell the bad shortcut, 'Hey, use that monitor over there please'?"

    Normal shell shortcuts (LNK files) do not encode monitor information. It is up to the application to decide where to display its windows. Many applications save the window position when you exit and restore it when you restart. If you have one of these types of programs, then the solution is simple: Move the window to where you want its position to be remembered, and then close it.

    In the case of RDP files, the window position is stored in the RDP file settings. The Terminal Services Team Blog tells you how.

    One thing I found interesting about that blog entry is that none of the comments is on topic. Well, okay maybe two of them.

Page 1 of 3 (28 items) 123