• The Old New Thing

    If you set up roaming profiles, you are expected to set up each machine identically, for the most part


    A customer discovered the following behavior when they set up roaming user profiles on their domain. Consider two machines, 1 and 2. An application A is installed on machine 1, but not machine 2. A user with a roaming profile logs onto machine 1 and pins application A to the taskbar. That user then logs off of machine 1 and logs onto machine 2.

    Now things get interesting: The taskbar on machine 2 initially shows a white icon on the taskbar, representing the nonexistent application A. A short time later, that icon vanishes. When the user logs off of machine 2 and back onto machine 1, the pinned icon is missing on machine 1, too.

    The white icon is deleted automatically by the system because it sees that you pinned an application which is not installed, so it unpins it too. This general rule is to handle the case where you install an application and pin it, then somebody else unninstalls it. The taskbar removes the now-broken icon to reflect the fact that the application is no longer installed. There's no point having a shortcut to a nonexisting program, and it relieves application vendors the impossible task of cleaning up pinned icons upon uninstall. (It's impossible because some users who pinned the application may not have their profile locally present because it roamed to another machine. Or worse, the uninstaller tries to edit a profile that is not active and ends up corrupting the master copy when the two versions reconcile.)

    The user profiles team explained that one of the assumptions behind classic roaming user profiles is that the machines participating in roaming be semantically identical: They must be running the same operating system on the same processor architecture. They must have the same set of applications installed into the same locations. And they must have the same drive letter layout.

    But that's just classical roaming profiles. There are other roaming profile solutions, such as User Experience Virtualization, which may meet the customer's needs better. (I'm told that there are also third-party roaming solutions, though I don't know of any offhand, this not being my area of expertise.)

  • The Old New Thing

    When corporate policies meet precision scientific equipment


    One of my colleagues used to work as an IT consultant, and one of his clients was a tobacco company. Since they were a tobacco company, the company policy on smoking was "You can smoke anywhere, any time."

    "Anywhere" includes the labs. The labs with very expensive precision scientific equipment.

    My colleague told me that this policy meant that the company regularly replaced $50,000 pieces of equipment after only a few months, thanks to smoke damage. But the company couldn't change their smoking policy. Imagine the public relations disaster if a tobacco company had a no-smoking policy!

    Starting next year, cigarette maker Reynolds American will be a smoke-free workplace.

    Bonus chatter: One of the researchers showed my colleague one of those pieces of expensive scientific equipment. The way my colleague explained it, "On the graph was a spike. The spike is what makes the cigarette taste good. It also is what kills you. The trick was to tweak the product in order to move the spike far enough to the right that people prefer the product over the competition, but not so far that you end up killing your customer base." (Note that this was my colleague's interpretation of what the researcher said, not the researcher's actual words.)

  • The Old New Thing

    Notes on calculating constants in SSE registers


    There are a few ways to load constants into SSE registers.

    • Load them from memory.
    • Load them from general purpose registers via movd.
    • Insert selected bits from general purpose registers via pinsr[b|w|d|q].
    • Try to calculate them in clever ways.

    Loading constants from memory incurs memory access penalties. Loading or inserting them from general purpose registers incurs cross-domain penalties. So let's see what we can do with clever calculations.

    The most obvious clever calculations are the ones for setting a register to all zeroes or all ones.

        pxor    xmm0, xmm0 ; set all bits to zero
        pcmpeqd xmm0, xmm0 ; set all bits to one

    These two idioms are special-cased in the processor and execute faster than normal pxor and pcmpeqd instructions because the results are not dependent on the previous value in xmm0.

    There's not much more you can do to construct other values from zero, but a register with all bits set does create additional opportunities.

    If you need a value loaded into all lanes whose bit pattern is either a bunch of 0's followed by a bunch of 1's, or a bunch of 1's followed by a bunch of 0's, then you can shift in zeroes. For example, assuming you've set all bits in xmm0 to 1, here's how you can load some other constants:

        pcmpeqd xmm0, xmm0 ; set all bits to one
        pslld  xmm0, 30    ; all 32-bit lanes contain 0xC0000000
        psrld  xmm0, 29    ; all 32-bit lanes contain 0x00000007
        psrld  xmm0, 31    ; all 32-bit lanes contain 0x00000001

    Intel suggests loading 1 into all lanes with the sequence

        pxor    xmm0, xmm0 ; xmm0 = { 0, 0, 0, 0 }
        pcmpeqd xmm1, xmm1 ; xmm1 = { -1, -1, -1, -1 }
        psubd   xmm0, xmm1 ; xmm0 = { 1, 1, 1, 1 }

    but that not only takes more instructions but also consumes two registers, and registers are at a premium since there are only eight of them. The only thing I can think of is that psubd might be faster than psrld.

    In general, to load 2ⁿ−1 into all lanes, you do

        pcmpeqd xmm0, xmm0 ; set all bits to one
        psrlw  xmm0, 16-n  ; clear top 16-n bits of all 16-bit lanes
        psrld  xmm0, 32-n  ; clear top 32-n bits of all 32-bit lanes
        psrlq  xmm0, 64-n  ; clear top 64-n bits of all 64-bit lanes

    Conversely, if you want to load ~(2ⁿ−1) = -2ⁿ into all lanes, you shift the other way.

        pcmpeqd xmm0, xmm0 ; set all bits to one
        psllw  xmm0, n     ; clear bottom n bits of all 16-bit lanes = 2¹⁶ - 2ⁿ
        pslld  xmm0, n     ; clear bottom n bits of all 32-bit lanes = 2³² - 2ⁿ
        psllq  xmm0, n     ; clear bottom n bits of all 64-bit lanes = 2⁶⁴ - 2ⁿ

    And if the value you want has all its set bits in the middle, you can combine two shifts (and stick something in between the two shifts to ameliorate the stall):

        pcmpeqd xmm0, xmm0 ; set all bits to one
        psrlw  xmm0, 13    ; all lanes = 0x0007
        psllw  xmm0, 4     ; all lanes = 0x0070
        psrld  xmm0, 31    ; all lanes = 0x00000001
        pslld  xmm0, 3     ; all lanes = 0x00000008

    If you want to set high or low lanes to zero, you can use pslldq and psrldq.

        pcmpeqd xmm0, xmm0 ; set all bits to one
        pslldq xmm0, 2     ; clear bottom word, xmm0 = { -1, -1, -1, -1, -1, -1, -1, 0 }
        pslldq xmm0, 4     ; clear bottom dword, xmm0 = { -1, -1, -1, 0 }
        pslldq xmm0, 8     ; clear bottom qword, xmm0 = { -1, 0 }
        psrldq xmm0, 2     ; clear top word, xmm0 = { 0, -1, -1, -1, -1, -1, -1, -1 }
        psrldq xmm0, 4     ; clear top dword, xmm0 = { 0, -1, -1, -1 }
        psrldq xmm0, 8     ; clear top qword, xmm0 = { 0, -1 }

    No actual program today. Just some notes from my days writing SSE assembly language.

    Bonus chatter: There is an intrinsic for pxor xmmReg, xmmReg: _mm_setzero_si128. However, there is no corresponding intrinsic for pcmpeqd xmmReg, xmmReg, which would presumably be called _mm_setones_si128 or _mm_setmone_epiNN. In order to get all-ones, you need to get a throwaway register and compare it against itself. The cheapest throwaway register is one that is set to zero, since that is special-cased inside the processor.

    __m128i zero = _mm_setzero_si128();
    __m128i ones = _mm_cmpeq_epi32(zero, zero);
  • The Old New Thing

    Detecting whether a SID is well-known SID


    You might think that the Is­Well­Known­Sid function would tell you whether a SID is well-known, but it doesn't. Rather, it tells you whether a SID exactly matches the well-known SID you specified. For example, you can ask, "Is this the Authenticated Users SID?" or "Is this the Everyone SID?" But you can't ask, "Is this any type of well-known SID?"

    I guess you could enumerate through all the well-known SIDs, and check if your SID matches any of them, but that's getting kind of ugly.

    If what you're interested in is whether this is a machine-relative SID (or a domain-relative SID, which is the special case where the machine is the domain controller), as opposed to a universal SID, you can check whether the SID format is S-1-5-21-#-#-#-#. All machine-relative SIDs have that form.

    #define SECURITY_NT_NON_UNIQUE          (0x00000015L) // decimal 21

    If you want to exclude machine\Administrator and other predefined machine-relative SIDs, you can verify that the last number (the RID) is greater than or equal to 1000.

  • The Old New Thing

    What states are possible in a DRAWITEMSTRUCT structure?


    The DRAW­ITEM­STRUCT structure has an item­State member which contains a number of bits describing the state of the item being drawn. How do those states map to the underlying control?

    Most of the states are rather obvious. For a list box item to be selected, it means that the item is part of the selection. But what does selected mean for a button?

    Since people like tables, I'll put the answer in a table:

    Menu Listbox Combobox Button
    itemID menu item ID item index or −1 item index or −1
    ODS_SELECTED Selected Selected Selected Pushed
    ODS_GRAYED Grayed
    ODS_DISABLED Disabled Disabled Disabled Disabled
    ODS_CHECKED Checked
    ODS_FOCUS Focus Focus Focus
    ODS_DEFAULT Default menu item
    ODS_INACTIVE Inactive
    ODS_NOACCEL HideAccel HideAccel HideAccel HideAccel
    ODS_NOFOCUSRECT HideFocus HideFocus HideFocus
    ODS_COMBOBOXEDIT Is edit control
    Static Header Tab Listview Status
    itemID item index item index item index part index
    ODS_SELECTED Pushed Selected Selected
    ODS_CHECKED AutoChecked
    ODS_FOCUS Focus
    ODS_NOACCEL HideAccel

    Okay, now that it's all in a table, how do I read the table?

    A box is blank if the corresponding flag is not currently used by the control type. (No guarantees about the future.) For example, as of this writing, button controls do not set an itemID, nor do they ever ask for ODS_GRAYED.

    You may have noticed that the box for CtlType is blank for status controls. That's an oops. The status bar control forgot to set the CtlType when it sends the WM_DRAW­ITEM message, so the value is uninitialized garbage. The way to detect a status bar control is to check the window handle. (This works in general. You can always detect a control by checking the window handle.)

    For list boxes and combo boxes, the itemID can have the special value -1 to mean "I am drawing a list box/combo box where no item is selected." For list boxes, this happens when the list box is empty. For combo boxes, this happens when the user types text into the edit box that does not match any of the items in the list portion of the combo box.

    Most of the other box entries are self-explanatory. For the most part, the flag name matches the conditions under which the corresponding flag is set. For example, the ODS_FOCUS flag is set when the list box item being drawn is the selected item.

    Note that the ODS_SELECTED flag is used for button and header controls to indicate that the control should be drawn in the pushed state. For example, the user may have put focus on a button control and pressed the space bar and not yet released it, or the application may have manually set the BST_PUSHED state. Header controls can get into a pushed state if you enable the HDS_BUTTONS style.

    List view controls set the ODS_CHECKED flag if a check box should be drawn over the item. This happens if the LVS_EX_AUTO­CHECK­SELECT extended style is specified and the item is selected. (Normally, the check box is drawn to the side as a state image.)

    The ODS_COMBO­BOX­EDIT flag is used only by combo box controls. It is set if the item being drawn is the edit portion of a combo box control. If not set, then the item being drawn is in the list box portion of the combo box control.

    Finally, there is a box marked Oops.

    The static control is supposed to set ODS_DISABLED if the static control is disabled. And that's what happens if you are using the classic static control. However, there is a typo in the the fancy themed static control, and it sets the ODS_DISBALED flag incorrectly. If you are owner-drawing a themed static control, and you want to draw differently depending on whether the control is disabled, then you should ignore the ODS_DISABLED flag and instead draw the disabled state based on the result of calling Is­Window­Enabled function.

    The bug in the themed static control cannot be fixed for compatibility reasons. I can pretty much guarantee that there is some application which doesn't draw correctly if the ODS_DISABLED flag is not set.

  • The Old New Thing

    If you get a procedure address by ordinal, you had better be absolutely sure it's there, because the failure mode is usually indistinguishable from success


    A customer reported that the Get­Proc­Address function was behaving strangely.

    We have this code in one of our tests:

    typedef int (CALLBACK *T_FOO)(int);
    void TestFunctionFoo(HINSTANCE hDLL)
      // Function Foo is ordinal 1 in our DLL
      T_FOO pfnFoo = (T_FOO)GetProcAddress(hDLL, (PCSTR)1);
      if (pfnFoo) {
        ... run tests on pfnFoo ...

    Recently, this test started failing in bizarre ways. When we stepped through the code, we discovered that pfnFoo ends up calling Bar instead of Foo. The first time we try to test pfnFoo, we get stack corruption because Bar has a different function prototype from Foo, and of course on top of that the test fails horribly because it's calling the wrong function!

    When trying to narrow the problem, we found that the issue began when the test was run against a version of the DLL that was missing the Foo function entirely. The line

        Foo @1

    was removed from the DEF file. Why did the call to Get­Proc­Address succeed and return the wrong function? We expected it to fail.

    Let's first consider the case where a DLL exports no functions by ordinal.


    The linker builds a list of all the exported functions (in an unspecified order) and fills in two arrays based on that list. If you look in the DLL image, you'll see something like this:

    Exported Function Table
    00049180 address of Bar
    00049184 address of Foo
    0004918C address of Plugh
    Exported Names
    00049190 address of the string "Bar"
    00049194 address of the string "Foo"
    00049198 address of the string "Plugh"

    There are two parallel arrays, one with function addresses and one with function names. The string "Bar" is the first entry in the exported names table, and the function Bar is the first entry in the exported function table. In general, the string in the Nth entry in the exported names table corresponds to the function in the Nth entry of the exported function table.

    Since it is only the relative position that matters, let's replace the addresses with indices.

    Exported Function Table
    [1] address of Bar
    [2] address of Foo
    [3] address of Plugh
    Exported Names
    [1] address of the string "Bar"
    [2] address of the string "Foo"
    [3] address of the string "Plugh"

    Okay, now let's introduce functions exported by ordinal. When you do that, you're telling the linker, "Make sure this function goes into the NNth slot in the exported function table." Suppose your DEF file went like this:

        Foo @1

    This says "First thing we do is put Foo in slot 1. Once that's done, fill in the rest arbitrarily."

    The linker says, "Okay, I have a total of three functions, so let me build two tables with three entries each."

    Exported Function Table
    [1] address of ?
    [2] address of ?
    [3] address of ?
    Exported Names
    [1] address of ?
    [2] address of ?
    [3] address of ?

    "Now I place Foo in slot 1."

    Exported Function Table
    [1] address of Foo
    [2] address of ?
    [3] address of ?
    Exported Names
    [1] address of the string "Foo"
    [2] address of ?
    [3] address of ?

    "Now I fill in the rest arbitrarily."

    Exported Function Table
    [1] address of Foo
    [2] address of Bar
    [3] address of Plugh
    Exported Names
    [1] address of the string "Foo"
    [2] address of the string "Bar"
    [3] address of the string "Plugh"

    Since you explicitly placed Foo in slot 1, when you do a Get­Proc­Address(hDLL, 1), you will get Foo. On the other hand, if you do a Get­Proc­Address(hDLL, 2), you will get Bar, or at least you will with this build. With the next build, you may get something else, because the linker just fills in the slots arbitrarily, and next time, it may choose to fill them arbitrarily in some other order. Furthermore, if you do a Get­Proc­Address(hDLL, 6), you will get NULL because the table has only three functions in it.

    I hope you see where this is going.

    If you delete Foo from the EXPORTS section, this stops exporting Foo but says nothing about what goes into slot 1. As a result, the linker is free to put anything it wants into that slot.

    Exported Function Table
    [1] address of Bar
    [2] address of Plugh
    Exported Names
    [1] address of the string "Bar"
    [2] address of the string "Plugh"

    Now, when you do a Get­Proc­Address(hDLL, 1), you get Bar, since that's the function that happened to fall into slot 1 this time.

    The moral of the story is that if you try to obtain a function by ordinal, then it had better be there, because there is no reliable way of being sure that the function you got is one that was explicitly placed there, as opposed to some other function that happened to be assigned that slot arbitrarily.

    Related reading: How are DLL functions exported in 32-bit Windows?

  • The Old New Thing

    The psychology of confirmation, or something, I don't know what to call it


    There is probably a name for this phenomenon. I will illustrate it below.

    "Is there a way to configure the system to do X?"

    Go to the Y dialog and select Z.

    "It doesn't work."

    I just tried it. It works for me. I'm using ⟨configuration details⟩.

    "Thanks. It's working."

  • The Old New Thing

    Creating double-precision integer multiplication with a quad-precision result from single-precision multiplication with a double-precision result

    Suppose you want to multiply two double-word values producing a quad-word result, but your processor supports only single-word multiplication with a double-word result. For concreteness, let's say that your processor supports 32 × 32 → 64 multiplication and you want to implement 64 × 64 → 128 multiplication. (Sound like any processor you know?)

    Oh boy, let's do some high school algebra. Let's start with unsigned multiplication.

    Let x = A × 2³² + B and y = C × 2³² + D, where A, B, C, and D are all in the range 0 … 2³² − 1.

    x × y AC × 264 + (AD + BC) × 232 + BD
    AC × 264 + BD  +  (AD + BC) × 232
    provisional result cross-terms

    Each of the multiplications (not counting the power-of-two multiplications) is a 32 × 32 → 64 multiplication, so they are something we have as a building block. And the basic implementation is simply to perform the four multiplications and add the pieces together. But if you have SSE, you can perform two multiplies in a single instruction.

        // Prepare our source registers
        movq xmm0, x         // xmm0 = { 0, 0, A, B } = { *, *, A, B }
        movq xmm1, y         // xmm1 = { 0, 0, C, D } = { *, *, C, D }
        punpckldq xmm0, xmm0 // xmm0 = { A, A, B, B } = { *, A, *, B }
        punpckldq xmm1, xmm1 // xmm1 = { C, C, D, D } = { *, C, *, D }
        pshufd xmm2, xmm1, _MM_SHUFFLE(2, 0, 3, 1)
                             // xmm2 = { D, D, C, C } = { *, D, *, C }

    The PMULUDQ instruction multiplies 32-bit lanes 0 and 2 of its source and destination registers, producing 64-bit results. The values in lanes 1 and 3 do not participate in the multiplication, so it doesn't matter what we put there. It so happens that the PUNPCKLDQ instruction duplicates the value, but we really don't care. I used * to represent a don't-care value.

        pmuludq xmm1, xmm0 // xmm1 = { AC, BD } // provisional result
        pmuludq xmm2, xmm0 // xmm2 = { AD, BC } // cross-terms

    In two PMULUDQ instructions, we created the provisional result and the cross-terms. Now we just need to add the cross-terms to the provisional result. Unfortunately, SSE does not have a 128-bit addition (or at least SSE2 doesn't; who knows what they'll add in the future), so we need to do that the old-fashioned way.

        movdqa result, xmm1
        movdqa crossterms, xmm2
        mov    eax, crossterms[0]
        mov    edx, crossterms[4] // edx:eax = BC
        add    result[4], eax
        adc    result[8], edx
        adc    result[12], 0      // add the first cross-term
        mov    eax, crossterms[8]
        mov    edx, crossterms[12] // edx:eax = AD
        add    result[4], eax
        adc    result[8], edx
        adc    result[12], 0      // add the second cross-term

    There we go, a 64 × 64 → 128 multiply constructed from 32 × 32 → 64 multiplies.

    Exercise: Why didn't I use the rax register to perform the 64-bit addition? (This is sort of a trick question.)

    That calculates an unsigned multiplication, but how do we do a signed multiplication? Let's work modulo 2128 so that signed and unsigned multiplication are equivalent. This means that we need to expand x and y to 128-bit values X and Y.

    Let s = the sign bit of x expanded to a 64-bit value, and similarly t = the sign bit of y expanded to a 64-bit value. In other words, s is 0xFFFFFFFF`FFFFFFFF if x < 0 and zero if x ≥ 0.

    The 128-bit values being multiplied are

    X s × 264 + x
    Y t × 264 + y

    The product is therefore

    X × Y st × 2128  (sy + tx) × 264   +  xy
    zero adjustment unsigned product

    The first term is zero because it overflows the 128-bit result. That leaves the second term as the adjustment, which simplifies to "If x < 0 then subtract y from the high 64 bits; if y < 0 then subtract x from the high 64 bits."

        if (x < 0) result.m128i_u64[1] -= y;
        if (y < 0) result.m128i_u64[1] -= x;

    If we were still playing with SSE, we could compute this as follows:

        movdqa xmm0, result   // xmm0 = { high, low }
        movq   xmm1, x        // xmm1 = { 0, x }
        movq   xmm2, y        // xmm2 = { 0, y }
        pshufd xmm3, xmm1, _MM_SHUFFLE(1, 1, 3, 2) // xmm3 = { xhi, xhi, 0, 0 }
        pshufd xmm1, xmm1, _MM_SHUFFLE(1, 0, 3, 2) // xmm1 = { x, 0 }
        pshufd xmm4, xmm2, _MM_SHUFFLE(1, 1, 3, 2) // xmm4 = { yhi, yhi, 0, 0 }
        pshufd xmm2, xmm2, _MM_SHUFFLE(1, 0, 3, 2) // xmm2 = { y, 0 }
        psrad  xmm3, 31       // xmm3 = { s, s, 0, 0 } = { s, 0 }
        psrad  xmm4, 31       // xmm4 = { t, t, 0, 0 } = { t, 0 }
        pand   xmm3, xmm2     // xmm3 = { x < 0 ? y : 0, 0 }
        pand   xmm4, xmm1     // xmm4 = { y < 0 ? x : 0, 0 }
        psubq  xmm0, xmm3     // first adjustment
        psubq  xmm0, xmm4     // second adjustment
        movdqa result, xmm0   // update result

    The code is a bit strange because SSE2 doesn't have a full set of 64-bit integer opcodes. We would have liked to have used a psraq instruction to fill a 64-bit field with a sign bit. But there is no such instruction, so instead we duplicate the 64-bit sign bit into two 32-bit sign bits (one in lane 2 and one in lane 3) and then fill the lanes with that bit using psrad.

  • The Old New Thing

    Killing a window timer prevents the WM_TIMER message from being generated for that timer, but it doesn't retroactively remove ones that were already generated


    Calling Kill­Timer to cancel a window timer prevents WM_TIMER messages from being generated for that timer, even if one is overdue. In other words, give this sequence of operations:

    SetTimer(hwnd, IDT_MYTIMER, 1000, NULL);
    KillTimer(hwnd, IDT_MYTIMER);

    no WM_TIMER message is ever generated. Even though a timer became due during the Sleep, no timer message was generated during the sleep because timer messages are generated on demand, and nobody demanded one. Killing the timer then removes the ability to demand a timer message, and the result is that no message ever appears.

    In general, this means that once you kill a timer, you will not receive any WM_TIMER messages for that timer.

    Unless you demanded one while the timer was active and didn't process it.

    Let's try a variation:

    SetTimer(hwnd, IDT_MYTIMER, 1000, NULL);
    if (PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, 0)) {
    KillTimer(hwnd, IDT_MYTIMER);

    In this case, the Peek­Message function looks for a WM_TIMER message in the queue, and if none is found, it asks for one to be generated on the fly if a timer is due. It so happens that one is due (IDT_MY­TIMER), so the Peek­Message causes a WM_TIMER to be generated and placed in the queue. But it doesn't remain in this state for long, because the message is removed from the queue by the Peek­Message function.

    Okay, now let's make things weird:

    SetTimer(hwnd, IDT_MYTIMER, 1000, NULL);
    if (PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE)) {
     // oh hey there is an overdue timer, how about that
    KillTimer(hwnd, IDT_MYTIMER);

    This time, we passed the PM_NO­REMOVE flag. The window manager goes through the same process as before, first looking for a WM_TIMER message in the queue, and then failing to find one, generates one on the fly since the IDT_MY­TIMER timer is overdue. But the PM_NO­REMOVE flag makes things weird because it says, "Thanks for generating that message for me. But don't remove it from the queue. Leave it there. I'll deal with it later."

    You might do this if you want to stop processing if a timer elapses, but you don't want to handle the timer immediately because you are in some sensitive state at the point you realize that you need to stop processing. Instead, you want to return back out to the main message loop and let it deal with the timer.

    BOOL DoWorkUntilTheNextTimer()
     BOOL fFinished = FALSE;
     MSG msg;
     while (!PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE)) {
      if (AnyWorkLeft()) DoSomeWork(); 
      else { fFinished = TRUE; break; }
     return fFinished;

    And then you might call it like this:

    void DoWorkForUpToOneSecond()
     SetTimer(hwnd, IDT_MYTIMER, 1000, NULL);
     KillTimer(hwnd, IDT_MYTIMER);

    The Kill­Timer will prevent any new timer messages from being generated for IDT_MY­TIMER, but it does not go back in time and retroactively un-generate the timer message that was generated when Do­Work­Until­The­Next­Timer asked to see if there were any timer messages.

    You are now in the strange situation where a subsequent call to Peek­Message or Get­Message will retrieve a timer message for a timer that is no longer active!

    This is captured in the MSDN documentation with the simple sentence, "The Kill­Timer function does not remove WM_TIMER messages already posted to the message queue."

  • The Old New Thing

    If my WM_TIMER handler takes longer than the timer period, will my queue fill up with WM_TIMER messages?


    A customer was worried that they may have a problem with their message queue filling with WM_TIMER messages. "If my WM_TIMER handler takes longer than the timer period, will my queue fill up with WM_TIMER messages?"

    As we should know by now, timer messages are generated on demand:

    The WM_TIMER message is a low-priority message. The Get­Message and Peek­Message functions post this message only when no other higher-priority messages are in the thread's message queue.

    Here's the basic algorithm. (I'm ignoring filtering and I'm assuming that messages are removed.)

    • Look for a posted message. If one exists, then return it.
    • Was Post­Quit­Message called? If so, then generate and return a WM_QUIT message.
    • Look for an input message. If one exists, then return it.
    • Did the mouse move since the last call? If so, then generate and return a WM_MOUSE­MOVE message.
    • Does a window need to be repainted? If so, then generate and return a WM_PAINT message.
    • Is there a timer that has elapsed? If so, then generate and return a WM_TIMER message.

    Notice that the generated messages are generated on demand by message retrieval functions. If you never call a message retrieval function, then no messages are generated. And in the case where the messages are removed (i.e., you use Get­Message or you use Peek­Message with PM_REMOVE), the messages are removed immediately after being generated, so they don't hang around very long at all.

    In particular, if your WM_TIMER handler takes longer than the timer period, and it doesn't call a message retrieval function, then there is no opportunity for another WM_TIMER message to be generated. Only when you call a message retrieval function does there become a possibility for a WM_TIMER message to be generated.

Page 4 of 441 (4,404 items) «23456»