December, 2010

  • The Old New Thing

    How do I limit the size of the preview window used by Aero Snap?

    • 7 Comments

    A customer reported that the translucent preview shows by Aero Snap showed the wrong dimensions for their application window. "As you can see in the screen shot, the preview is too wide. Our application window has a maximum width, but the preview is fully half the width of the screen. How can we disable the Aero Snap feature?"

    Whoa there, giving up so easily? Sounds like you're throwing the baby out with the bathwater.

    To control the size of the preview window used by Aero Snap, you respond to the same message you've already been responding to in order to tell Windows the valid range of sizes for your window: WM_GET­MIN­MAX­INFO.

    Start with our scratch program and make the following changes:

    void OnGetMinMaxInfo(HWND hwnd, LPMINMAXINFO pmmi)
    {
      pmmi->ptMaxTrackSize.x = 400;
    }
    
    // add to WndProc
        HANDLE_MSG(hwnd, WM_GETMINMAXINFO, OnGetMinMaxInfo);
    

    We specify in the On­Get­Min­Max­Info function that the maximum width for the window is 400 pixels. (In real life, of course, you wouldn't hard-code the width, but this is just a proof of concept program.) Since we don't touch ptMaxTrackSize.y, we impose no additional constraints on the window height beyond what comes with Windows by default.

    Run this program, and use Aero Snap to shove it against the edges of the screen. Observe that the Aero Snap preview respects our maximum window width.

    I never heard back from the customer, so I assume this simple solution worked for them. The fact that they had to ask this question tells me that they hadn't been handling the WM_GET­MIN­MAX­INFO message at all; instead, they were enforcing their window size procedurally after the window manager already decided on the wrong size. Either they didn't seem to mind that the maximize and restore animations showed the window animating to the wrong size, or they couldn't figure out how to fix that problem either.

  • The Old New Thing

    What appears superficially to be a line is actually just a one-dimensional mob

    • 35 Comments

    In China, queueing is honored more in the breach than in the observance. If you see a line for something, you must understand that what you are seeing is not really a line. It is a one-dimensional mob. You must be prepared to defend your position in line fiercely, because any sign of weakness will be pounced upon, and the next thing you know, five people just cut in front of you.

    I first became aware of this characteristic of "Chinese queueing theory" while still at the airport. When the gate agents announced that the flight to Beijing had begun boarding, a one-dimensional mob quickly formed, and I naïvely joined the end. It wasn't long before my lack of attentiveness to the minuscule open space in front of me resulted in another person cutting in front. At that point, I realized that the Chinese implementation of queueing theory was already in effect even before we left the United States.

    As another example: After the plane pulls up to the gate after landing, the aisles quickly fill with people anxious to get off the plane. In the United States, you can rely upon the kindness of strangers to let you into the aisle so you can fetch your bags and join the queue. But in China, you must force your way into the aisle. Nobody is going to let you in.

    Colleagues of mine who have spent time in both China and the United States tell me that it's an adjustment they have to make whenever they travel between the two countries. For example, in the United States, it is understood that when you are waiting in line for the ATM, you allow the person at the ATM a few feet of "privacy space." On the other hand, in China, you cannot leave such an allowance, because that is a sign of weakness in the one-dimensional mob. You have to stand right behind the person to protect your place in line.

    Bonus airport observation: How ironic it is that your last meal in your home country often comes from a crappy airport crfeteria.

  • The Old New Thing

    We've traced the call and it's coming from inside the house: Grid lines in list view report mode

    • 18 Comments

    A customer wanted to know how to remove the grid lines from a list view control in report mode. The customer was kind enough to include the source code for the relevant part of the program and drew our attention to the line in the resource file that he believed to be the source of the problem:

    CONTROL "",IDC_LIST,WC_LISTVIEW,LVS_REPORT|LVS_SHOWSELALWAYS|
           LVS_SINGLESEL|WS_HSCROLL|WS_TABSTOP|WS_VSCROLL,
           0,40,225,95,WS_EX_CLIENTEDGE
    

    The customer didn't know it, but that line in the resource file was of no help at all in diagnosing the problem. Fortunately, we found the root cause in the source code provided:

    void CListPage::OnInitDialog()
    {
      ...
      ListView_SetExtendedListViewStyleEx( hCtrl, 0,
         LVS_EX_FULLROWSELECT |
         LVS_EX_GRIDLINES |
         LVS_EX_LABELTIP |
         LVS_EX_HEADERINALLVIEWS);
    

    The grid lines are there because you explicitly asked for them!

    The customer accepted this answer without response. One of my colleagues suspected how this situation came about:

    If I may guess, the initial developer of the code did what the spec required at the time, which was to have grid lines. The initial developer then left the company, and a new developer inherited the code.

    Now the design team changed their mind and asked for the grid lines to be removed, but the new developer doesn't know the history of the code and therefore doesn't know where in the code the grid lines are turned on or even know that the default state for grid lines is off and that you have to turn them on explicitly.

    Chances are within the next two months, the design team will change their mind again and the developer will have to put the grid lines back, but at least this time he knows how to do it.

  • The Old New Thing

    Kindergarten writing exercise from my niece

    • 21 Comments

    When my niece was in kindergarten, a regular classroom assignment was for students to write a few sentences about something happening in their lives. For one of the assignments, my niece wrote the following:

    My auntie has a baby in her tumy. If it is a boy I will call him Kevin. If it is a grl I will call her Alula.

    We have no idea where the name "Alula" came from.

    The baby turned out to be a girl, but her aunt chose a different name.

    My niece did not hold up her end of the bargain and call her new cousin "Alula".

    Update: Upon further reflection, I think the proposed boy's name was "Derik", not Kevin. Not important to the story, but included for completeness.

  • The Old New Thing

    It rather involved being on the other side of this airtight hatchway: Invalid parameters from one security level crashing code at the same security level

    • 40 Comments

    In the category of dubious security vulnerability, I submit the following (paraphrased) report:

    I have discovered that if you call the XYZ function (whose first parameter is supposed to be a pointer to a IUnknown), and instead of passing a valid COM object pointer, you pass a pointer to a random hunk of data, you can trigger an access violation in the XYZ function which is exploitable by putting specially-crafted data in that memory blob. An attacker can exploit the XYZ function for remote execution and compromise the system, provided an application uses the XYZ function and passes a pointer to untrusted data as the first parameter instead of a valid IUnknown pointer. Although we have not found an application which uses the XYZ in this way, the function neverless contains the potential for exploit, and the bug should be fixed as soon as possible.
    The person included a sample program which went something like this (except more complicated):
    // We can control the behavior by tweaking the value
    // of the Exploit array.
    unsigned char Exploit[] = "\x01\x02\x03...";
    
    void main()
    {
       XYZ((IUnknown*)Exploit);
    }
    

    Well, yeah, but you're already on the other side of the airtight hatchway. Instead of building up a complicated blob of memory with exactly the right format, just write your bad IUnknown:

    void Pwnz0r()
    {
      ... whatever you want ...
    }
    
    class Exploit : public IUnknown
    {
    public:
      STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
      { Pwnz0r(); return E_NOINTERFACE; }
      STDMETHODIMP_(ULONG) AddRef() { Pwnz0r(); return 2; }
      STDMETHODIMP_(ULONG) Release() { Pwnz0r(); return 1; }
    };
    
    void main()
    {
       XYZ(&Exploit);
    }
    

    Wow, this new "exploit" is even portable to other architectures!

    Actually, now that you're on the other side of the airtight hatchway, you may as well take XYZ out of the picture since it's just slowing you down:

    void main()
    {
       Pwnz0r();
    }
    

    You're already running code. It's not surprising that you can run code.

    There's nothing subtle going on here. There is no elevation of privilege because the rogue activity happens in user-mode code, based on rogue code provided by an executable with trusted code execution privileges, at the same security level as the original executable.

    The people reporting the alleged vulnerability do say that they haven't yet found any program that calls the XYZ function with untrusted data, but even if they did, that would be a data handling bug in the application itself: Data crossed a trust boundary without proper validation. It's like saying "There is a security vulnerability in the DeleteFile function because it is possible for an application to pass an untrusted file name and thereby result in an attacker deleting any file of his choosing." Even if such a vulnerability existed, the flaw is in the application for not validating its input, not in DeleteFile for, um, deleting the file it was told to delete.

    The sad thing is that it took the security team five days to resolve this issue, because even though it looks like a slam dunk, the issue resolution process must be followed, just to be sure. Who knows, maybe there really is a bug in the XYZ function's use of the first parameter that would result in elevation of privilege. All supported versions of Windows need to be examined for the slim possibility that there's something behind this confused vulnerability report.

    But there isn't. It's just another dubious security vulnerability report.

    Exercise: Apply what you learned to this security vulnerability report. This is also paraphrased from an actual security report:

    There is a serious denial-of-service vulnerability in the function XYZ. This function takes a pointer to a buffer and a length. If the function is passed malformed parameters, it may encounter an access violation when it tries to read from an invalid buffer. Any application which calls this function with bad parameters will crash. Here is a sample program that illustrates the vulnerability:

    int main(int argc, char **argv)
    {
     // crash inside XYZ attempting to read past end of buffer
     XYZ("x", 9999999);
     return 0;
    }
    

    Credit for discovering this vulnerability goes to ABC Security Research Company. Copyright© 20xx ABC Security Research Company. All Rights Reserved.

  • The Old New Thing

    Creative naming in pursuit of subverting the no-fun zone

    • 16 Comments

    For a time, the Information Technology department at Microsoft cracked down on what it believed to be frivolous mailing lists. All mailing lists inside the company had to have a valid business purpose.

    The nascent wireless networking team found a way to circumvent this rule: They created a mailing list for discussion of non-business topics but officially said that it was for discussing Wireless Networking Interference.

  • The Old New Thing

    TrackMouseEvent tracks mouse events in your window, but only if the events belong to your window

    • 15 Comments

    Greg Williams wonders why Track­Mouse­Event fails to detect mouse hover events when responding to Do­Drag­Drop callbacks. "My suspicion is that Do­Drag­Drop monopolizes the window so that a WM_MOUSEHOVER message is never posted, so it won't end up being useful."

    That's basically it, for the appropriate sense of the word "monopolize."

    The Track­Mouse­Event monitors mouse events that take place in your window and notifies your window when events of interest occur. But this requires that the events actually take place in your window!

    The Do­Drag­Drop function calls Set­Capture so that it can carry out the task of following the mouse anywhere on the screen. Recall that mouse events normally are delivered to the window beneath the mouse, but Set­Capture lets you say, "No, I want all mouse events to go to me for as long as the mouse button is held down. Mine! All mine!" This function is typically called when you are performing some sort of mouse drag operation so that the window can respond to mouse events even after the use has dragged the mouse out of the window. (Which, in many cases, is the only interesting case.)

    That's why Track­Mouse­Event has no effect when you try to use it to detect mouse hovering during a drag/drop operation: The Track­Mouse­Event function is not seeing any mouse events! They're all being stolen by Do­Drag­Drop.

    The unfortunate consequence of this is that if you want to have a special behavior during drag/drop hover, you'll have to detect the hover manually by remembering the mouse position and timestamp and waiting for the appropriate amount of time to elapse without a significant mouse motion.

    Exercise: But wait, since I don't get drag/drop events when the mouse is not inside my window, how can I simulate WM_MOUSELEAVE?

  • The Old New Thing

    ZOMG! This program is using 100% CPU!1! Think of the puppies!!11!!1!1!eleven

    • 98 Comments

    For some reason, people treat a program consuming 100% CPU as if it were unrepentantly running around kicking defenseless (and cute) puppies. Calm down already. I get the impression that people view the CPU usage column in Task Manager not as a diagnostic tool but as a way of counting how many puppies a program kicks per second.

    While a program that consumes 100% CPU continuously (even when putatively idle) might legitimately be viewed as an unrepentant puppy-kicker, a program that consumes 100% CPU in pursuit of actually accomplishing something is hardly scorn-worthy; indeed it should be commended for efficiency!

    Think of it this way: Imagine if your CPU usage never exceed 50%. You just overpaid for your computer; you're only using half of it. A task which could have been done in five minutes now takes ten. Your media player drops some frames out of your DVD playback, but that's okay, because your precious CPU meter never went all the way to the top. (Notice that the CPU meter does not turn red when CPU usage exceeds 80%. There is no "danger zone" here.)

    Consider this comment where somebody described that they want their program to use less CPU but get the job done reasonably quickly. Why do you want it to use less CPU? The statement makes the implicit assumption that using less CPU is more important than getting work done as fast as possible.

    You have a crowd of people at the bank and only ten tellers. If you let all the people into the lobby at once, well, then all the tellers will be busy—you will have 100% teller utilization. These people seem to think it would be better to keep all the customers waiting outside the bank and only let them into the lobby five at a time in order to keep teller utilization at 50%.

    If it were done when 'tis done, then 'twere well / It were done quickly.

    Rip off the band-aid.

    Piss or get off the pot.

    Just do it.

    If you're going to go to the trouble of taking the CPU out of a low-power state, you may as well make full use of it. Otherwise, you're the person who buys a bottle of water, drinks half of it, then throws away the other half "because I'm thinking of the environment and reducing my water consumption." You're making the battery drain for double the usual length of time, halving the laptop's run time because you're trying to "conserve CPU."

    If the task you are interested in is a low priority one, then set your thread priority to below-normal so it only consumes CPU time when there are no foreground tasks demanding CPU.

    If you want your task to complete even when there are other foreground tasks active, then leave your task's priority at the normal level. Yes, this means that it will compete with other foreground tasks for CPU, but you just said that's what you want. If you want it to compete "but not too hard", you can sprinkle some Sleep(0) calls into your code to release your time slice before it naturally expires. If there are other foreground tasks, then you will let them run; if there aren't, then the Sleep will return immediately and your task will continue to run at full speed.

    And cheerfully watch that CPU usage go all the way to 100% while your task is running. Just make sure it drops back to zero when your task is complete. You don't want to be a task which consumes 100% CPU even when there's nothing going on. That'd just be kicking puppies.

    [Raymond is currently away; this message was pre-recorded.]

    Clarification: Many people appear to be missing the point. So let's put it more simply: Suppose you have an algorithm that takes 5 CPU-seconds to complete. Should you use 100% CPU for 5 seconds or 50% CPU for 10 seconds? (Obviously, if you can refine your algorithm so it requires only 2 CPU-seconds, that's even better, but that's unrelated to the issue here.)

  • The Old New Thing

    The alignment declaration specifier is in bytes, not bits

    • 18 Comments

    Explicit object alignment is not something most people worry about when writing code, which means that when you decide to worry about it, you may be a bit rusty on how the declarations work. (After all, if it's something you worried about all the time, then you wouldn't have trouble remembering how to do it!)

    I was looking at some customer code, and there was a class who had a data member with an explicit alignment declaration.

    class Whatever {
        ...
        __declspec(align(32)) LONG m_lSomething; // Must be DWORD-aligned to make writes atomic
        ...
    };
    

    I pointed out that the comment didn't match the code. The comment says that the variable needs to be DWORD-aligned (which in Windows-speak means aligned on a 32-bit boundary), but the code aligns it on a 32-byte boundary, eight times as generous as required. On the other hand, maybe they really did want the member aligned on a 32-byte boundary (say to put it on its own cache line).

    Turns out that in this case, the comment was correct and the code was wrong. To force a variable to align on a DWORD boundary, you want to say __declspec(align(4)). Save yourself a bunch of unnecessary padding bytes.

    But in fact, in this case, the customer was simply trying too hard. The code was compiled with default alignment, which aligns integer types on their natural boundaries anyway. The compiler was going to align the variable even if you didn't specify anything.

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    I will be speaking at TechEd China 2010 today

    • 17 Comments

    As I've mentioned a few times by now, the way to get me to show up at your event is to invite me. The easiest (i.e. cheapest) way is to hold your event in the Seattle area so that my travel expenses are effectively zero; I just use my bus pass. The folks at TechEd China 2010, on the other hand, had to fly me all the way out to Beijing, which is where I've been this week.

    Preparing these talks is a lot of work, because each one is different. It's not like I have a "stock presentation" that I give over and over. So even if you invite me, I may decline because I simply don't have the time to write up a new presentation for your event, and if your event is outside the Seattle area, I may decline because the travel would take me away for too long. (The TechEd China folks actually invited me last year, but I already had accepted an out-of-town invitation and couldn't really afford two out-of-town presentations in one year.)

    Anyway, if you can read Chinese, then you can check out the TechEd China 2010 Web site — nobody told me that crossed arms was the official speaker pose of TechEd China 2010 — my speaker biography and the talk description. I can't read Chinese, so I'm trusting the translators to have done a good job.

    I've found that a good way to salvage content that I've deleted from the queue (because the topic draws out flame comments) is to use them in talks and presentations instead. It turns out that the people who actually show up for a talk are much more polite than those who just post snarky comments from their mother's basement. I think the fact that you have to be snarky in person removes the veil of anonymity and makes you more accountable for what you say.

    And since I know you're going to ask:

    • I'm giving the talk in English.
    • I don't know whether the talk is going to be recorded.
    • The talk is being given on December 2, even though this entry is dated December 1. That's because it will already be December 2 in China.

    [Raymond is currently away, but he wrote this entry just now.]

Page 3 of 4 (31 items) 1234