• The Old New Thing

    Spammers look stupid when they don't read the blog they spam on

    • 8 Comments

    Yesterday, I got a 419 scam via the contact form. This is a new low for spam stupidity. You'd think people who blog are more likely to be aware of Internet scams because they're reading the news, are clearly more comfortable with technology, and often make fun of news stories about 419 scammers.

    But if you're for real, I have this to say to you: Be careful! I'm sure you are being completely truthful when you say you are the only child of Deposed Corrupt Dictator XYZ, but be aware that there are many unscrupulous people claiming the same thing! So watch out for Vivian, Pierre, Lama, Austine, Martin, Kabilla, Jean Paul, Farek, David, Curimbada, Cynthia, and of course, Dictator Junior. It's very disappointing that there are so many impostors trying to get their hands on the fortune that you stole and that by rights belongs to you.

    Also watch out for XYZ's former Finance Minister, accountant, widow, brother, and hair stylist. They're after the same thing.

    Good luck.

    (I also find it impressive that you were able to embezzle $100 billion U.S. dollars, since your country raises less than $2 billion per year in tax revenues. How you managed to amass that much money so quickly is truly an amazing accomplishment of economics. If you returned even just half of that money back to the country in exchange for amnesty, that would fund your government for a generation. Think about it. Do it for the children.)

    P.S. I was joking about the hair stylist. Please don't hurt him.

  • The Old New Thing

    $501 for 510g of peanut butter

    • 8 Comments

    You can buy 510 grams of peanut butter for only $501.

    Of course, this is special peanut butter. It must be kept at -20°C. And it is not intended for human consumption.

    This is peanut butter from the National Institute of Standards and Technology intended for use by food analysis laboratories to calibrate their equipment. For example, standard peanut butter consists of 10.4% ± 0.2% saturated fat.

    (Link courtesy of Slate.)

  • The Old New Thing

    Unicode collation is hard

    • 8 Comments

    The principle of "garbage in, garbage out" applies to Unicode collation. If you hand it a meaningless string and ask to compare it to another meaningless string, you get meaningless results.

    I am not a Unicode expert; I just play one on the web. A real Unicode expert is Michael Kaplan, whose explanation of how comparing invalid Unicode strings result in nonsensical results I strongly recommend to those who attempt to generate random test strings in Unicode.

  • The Old New Thing

    A privacy policy that doesn't actively offend me

    • 8 Comments

    I've ranted before about privacy policies and how they don't actually protect your privacy. (All they're required to do is disclose the policy; there is no requirement that the policy must be any good.)

    Today I read MetLife's privacy policy and found to my surprise that it does not actively offend me. It's written in plain English, it's well-organized, and it actually explains and limits the scope of each exception.

    I have noticed how the word "terrorism" has turned into a "magic word" of late. Anything distasteful you want to do, just say you're doing it to combat "terrorism" and people will give you the green light.

  • The Old New Thing

    Exploding frying pans

    • 8 Comments

    Last year there was a recall of exploding frying pans. For some reason I found this funny.

    "The pans can explode or separate when preheated, used on high heat or used for frying," the safety commission said in a statement.

    It explodes when you fry something in it.

    Apparently "frying stuff" was not in the test plan for the frying pans.

  • The Old New Thing

    Why are the rules for GetWindowText so weird?

    • 8 Comments

    Joel Spolsky rightly points out that the rules for GetWindowText exhibit abstraction leakage. Why are the rules for GetWindowText so weird?

    Set the wayback machine to 1983. Your typical PC had an 8086 processor running at a whopping 4.7MHz, two 360K 5½-inch floppy drives (or if you were really loaded, one floppy drive and a 10MB hard drive), and 256KB of memory. [Original entry said 256MB - oops. Thanks to Joe Beda for pointing this out.]

    This was the world of Windows 1.0.

    Windows 1.0 was a coöperatively-multitasked system. No preëmptive multitasking here. When your program got control, it had control for as long as it wanted it. Only when you called a function like PeekMessage or GetMessage did you release control to other applications.

    This was important, because in the absence of a hardware memory manager, you really had to make sure that your memory didn't get ripped out from under you.

    One important consequence of coöperative multitasking is that if your program is running, not only do you know that no other program is running, but you also know that every window is responding to messages. Why? Because if they were hung, they wouldn't have released control to you!

    This means that it is always safe to send a message. You never had to worry about the possibility of sending a message to a hung window, since you knew that no windows were hung.

    In this simpler world, GetWindowText was a straightforward function:

    int WINAPI
    GetWindowText(HWND hwnd, LPSTR pchBuf, int cch)
    {
        // ah for the simpler days
        return SendMessage(hwnd, WM_GETTEXT, (WPARAM)cch, (LPARAM)pchBuf);
    }
    

    This worked for all windows, all the time. No special handling of windows in a different process.

    It was the transition to Win32 and preëemptive multitasking that forced the change in the rules, because for the first time, there was the possibility that (gasp) the window you were trying to communicate with was not responding to messages.

    Now you have the backwards compatibility problem. As I described in my original article, many parts of the system and many programs rely on the ability to retrieve window text without hanging. So how do you make it possible to retrieve window text without hanging, while still giving controls like the edit control the ability to do their own window text management?

    The Win32 rules on GetWindowText are the result of this attempt to reconcile conflicting goals.

    (This same story, with slight changes, also works as a discussion of why DDE works the way it does. But fewer people use DDE nowadays, so the effect is not as dramatic.)

  • The Old New Thing

    You too can dress like Raymond

    • 8 Comments

    Yes, I'm the Raymond that Kraig Brockschmidt is writing about when he discusses "Dress Like Raymond Day".

  • The Old New Thing

    Writing a sort comparison function

    • 8 Comments

    When you are writing a sort comparison function (say, to be passed to ListView_SortItems or *gasp* to be used as an IComparer), your comparison function needs to follow these rules:

    • Reflexivity: Compare(a, a) = 0.
    • Anti-Symmetry: Compare(a, b) has the opposite sign of Compare(b, a), where 0 is considered to be its own opposite.
    • Transitivity: If Compare(a, b) ≤ 0 and Compare(b, c) ≤ 0, then Compare(a, c) ≤ 0.

    Here are some logical consequences of these rules (all easily proved). The first two are obvious, but the third may be a surprise.

    • Transitivity of equality: If Compare(a, b) = 0 and Compare(b, c) = 0, then Compare(a, c) = 0.
    • Transitivity of inequality: If Compare(a, b) < 0 and Compare(b, c) < 0, then Compare(a, c) < 0.
    • Substitution: If Compare(a, b) = 0, then Compare(a, c) has the same sign as Compare(b, c).

    Of the original three rules, the first two are hard to get wrong, but the third rule is often hard to get right if you try to be clever in your comparison function.

    For one thing, these rules require that you implement a total order. If you merely have a partial order, you must extend your partial order to a total order in a consistent manner.

    I saw somebody get into trouble when they tried to implement their comparison function on a set of tasks, where some tasks have other tasks as prerequisites. The comparison function implemented the following algorithm:

    • If a is a prerequisite of b (possibly through a chain of intermediate tasks), then a < b.
    • If b is a prerequisite of a (again, possibly through a chain of intermediate tasks), then a > b.
    • Otherwise, a = b. "Neither task is a prerequisite of the other, so I don't care what order they are in."

    Sounds great. Then you can sort with this comparison function and you get the tasks listed in some order such that all tasks come after their prerequisites.

    Except that it doesn't work. Trying to sort with this comparison function results in all the tasks being jumbled together with apparently no regard for which tasks are prerequisites of which. What went wrong?

    Consider this dependency diagram:

       a ----> b
     
       c
    

    Task "a" is a prerequisite for "b", and task "c" is unrelated to both of them. If you used the above comparison function, it would declare that "a = c" and "b = c" (since "c" is unrelated to "a" or "b"), which in turn implies by transitivity that "a = b", which contradicts "a < b", since "a" is a prerequisite for "b". If your comparison function is inconsistent, you will get garbled results.

    Moral of the story: When you write a comparison function, you really have to know which items are less than which other items. Don't just declare two items "equal" because you don't know which order they should be in.

  • The Old New Thing

    Why is address space allocation granularity 64K?

    • 8 Comments

    You may have wondered why VirtualAlloc allocates memory at 64K boundaries even though page granularity is 4K.

    You have the Alpha AXP processor to thank for that.

    On the Alpha AXP, there is no "load 32-bit integer" instruction. To load a 32-bit integer, you actually load two 16-bit integers and combine them.

    So if allocation granularity were finer than 64K, a DLL that got relocated in memory would require two fixups per relocatable address: one to the upper 16 bits and one to the lower 16 bits. And things get worse if this changes a carry or borrow between the two halves. (For example, moving an address 4K from 0x1234F000 to 0x12350000, this forces both the low and high parts of the address to change. Even though the amount of motion was far less than 64K, it still had an impact on the high part due to the carry.)

    But wait, there's more.

    The Alpha AXP actually combines two signed 16-bit integers to form a 32-bit integer. For example, to load the value 0x1234ABCD, you would first use the LDAH instruction to load the value 0x1235 into the high word of the destination register. Then you would use the LDA instruction to add the signed value -0x5433. (Since 0x5433 = 0x10000 - 0xABCD.) The result is then the desired value of 0x1234ABCD.

    LDAH t1, 0x1235(zero) // t1 = 0x12350000
    LDA  t1, -0x5433(t1)  // t1 = t1 - 0x5433 = 0x1234ABCD
    

    So if a relocation caused an address to move between the "lower half" of a 64K block and the "upper half", additional fixing-up would have to be done to ensure that the arithmetic for the top half of the address was adjusted properly. Since compilers like to reorder instructions, that LDAH instruction could be far, far away, so the relocation record for the bottom half would have to have some way of finding the matching top half.

    What's more, the compiler is clever and if it needs to compute addresses for two variables that are in the same 64K region, it shares the LDAH instruction between them. If it were possible to relocate by a value that wasn't a multiple of 64K, then the compiler would no longer be able to do this optimization since it's possible that after the relocation, the two variables no longer belonged to the same 64K block.

    Forcing memory allocations at 64K granularity solves all these problems.

    If you have been paying really close attention, you'd have seen that this also explains why there is a 64K "no man's land" near the 2GB boundary. Consider the method for computing the value 0x7FFFABCD: Since the lower 16 bits are in the upper half of the 64K range, the value needs to be computed by subtraction rather than addition. The naïve solution would be to use

    LDAH t1, 0x8000(zero) // t1 = 0x80000000, right?
    LDA  t1, -0x5433(t1)  // t1 = t1 - 0x5433 = 0x7FFFABCD, right?
    

    Except that this doesn't work. The Alpha AXP is a 64-bit processor, and 0x8000 does not fit in a 16-bit signed integer, so you have to use -0x8000, a negative number. What actually happens is

    LDAH t1, -0x8000(zero) // t1 = 0xFFFFFFFF`80000000
    LDA  t1, -0x5433(t1)   // t1 = t1 - 0x5433 = 0xFFFFFFFF`7FFFABCD
    

    You need to add a third instruction to clear the high 32 bits. The clever trick for this is to add zero and tell the processor to treat the result as a 32-bit integer and sign-extend it to 64 bits.

    ADDL t1, zero, t1    // t1 = t1 + 0, with L suffix
    // L suffix means sign extend result from 32 bits to 64
                         // t1 = 0x00000000`7FFFABCD
    

    If addresses within 64K of the 2GB boundary were permitted, then every memory address computation would have to insert that third ADDL instruction just in case the address got relocated to the "danger zone" near the 2GB boundary.

    This was an awfully high price to pay to get access to that last 64K of address space (a 50% performance penalty for all address computations to protect against a case that in practice would never happen), so roping off that area as permanently invalid was a more prudent choice.
  • The Old New Thing

    Why do I get spurious WM_MOUSEMOVE messages?

    • 8 Comments

    In order to understand this properly, it helps to know where WM_MOUSEMOVE messages come from.

    When the hardware mouse reports an interrupt, indicating that the physical mouse has moved, Windows determines which thread should receive the mouse move message and sets a flag on that thread's input queue that says, "The mouse moved, in case anybody cares." (Other stuff happens, too, which we will ignore here for now. In particular, if a mouse button event arrives, a lot of bookkeeping happens to preserve the virtual input state.)

    When that thread calls a message retrieval function like GetMessage, and the "The mouse moved" flag is set, Windows inspects the mouse position and does the work that is commonly considered to be part of mouse movement: Determining the window that should receive the message, changing the cursor, and determining what type of message to generate (usually WM_MOUSEMOVE or perhaps WM_NCMOUSEMOVE).

    If you understand this, then you already see the answer to the question, "Why does my program not receive all mouse messages if the mouse is moving too fast?"

    If your program is slow to call GetMessage, then multiple mouse interrupts may arrive before your program calls GetMessage to pick them up. Since all that happens when the mouse interrupt occurs is that a flag is set, if two interrupts happen in succession without a message retrieval function being called, then the second interrupt will merely set a flag that is already set, which has no effect. The net effect is that the first interrupt acts as if it has been "lost" since nobody bothered to pick it up.

    You should also see the answer to the question, "How fast does Windows deliver mouse movement messages?"

    The answer is, "As fast as you want." If you call GetMessage frequently, then you get mouse messages frequently; if you call GetMessage rarely, then you get mouse messages rarely.

    Okay, so back to the original question, "Why do I get spurious WM_MOUSEMOVE messages?"

    Notice that the delivery of a mouse message includes lots of work that is typically thought of as being part of mouse movement. Often, Windows wants to do that follow-on work even though the mouse hasn't actually moved. The most obvious example is when a window is shown, hidden or moved. When that happens, the mouse cursor may be over a window different from the window it was over previously (or in the case of a move, it may be over a different part of the same window). Windows needs to recalculate the mouse cursor (for example, the old window may have wanted an arrow but the new window wants a pointy finger), so it artificially sets the "The mouse moved, in case anybody cares" flag. This causes all the follow-on work to happen, a side-effect of which is the generation of a spurious WM_MOUSEMOVE message.

    So if your program wants to detect whether the mouse has moved, you need to add a check in your WM_MOUSEMOVE that the mouse position is different from the position reported by the previous WM_MOUSEMOVE message.
Page 378 of 448 (4,479 items) «376377378379380»