• The Old New Thing

    Thanks for reminding me what to do when the elevator is out of order


    Every few years, the building maintenance people have to perform tests on the elevators to ensure they meet safety regulations. And the real estate department sends out the usual notice informing the building occupants that the elevators in the building will be taken out of service at various times during the day. They were kind enough to include the following advice:

    If an elevator is non-responsive and/or has out of order signage posted, please use another available elevator.

    One of my colleagues sarcastically remarked, "Wow, thank goodness they sent that email. I'd have no idea what to do had I seen a non-responsive elevator with an 'out of order' sign posted on it."

  • The Old New Thing

    Answers to exercises


    What is the significance of the (int) cast in the computation of dLines?

    It isn't. I got the question wrong. The real question should have been "What is the significance of the (int) cast in the computation of g_iWheelCarryover?"

    The answer is to ensure that the computation is performed with signed integers throughout. If the cast were missing, then the computation would have been unsigned (since mixing signed and unsigned yields unsigned). dLines is a signed integer, let's say it's -1. Multiply by WHEEL_DELTA yields -120. Now convert it to an unsigned integer and you get a number in excess of four billion. Divided by uScroll (typically 3) yields a number greater than one billion, which is incorrect.

    Assuming you don't have a high-resolution wheel mouse, how would you test that your sub-detent mouse wheel handling was working properly?

    This is an easy one. Insert temporary code at the top of the OnMouseWheel function that says zDelta /= 4. Ta-da, your mouse now has quadruple its original resolution.

  • The Old New Thing

    What's the story behind the WM_SYNCPAINT message?


    Danail wants to know the story behind the WM_SYNC­PAINT message.

    The documentation pretty much tells the story. When a window has been hidden, shown, moved or sized, the system may determine that it needs to send a WM_SYNC­PAINT message to the windows of other threads. This message must be passed to Def­Window­Proc, which will send the WM_NCPAINT and WM_ERASE­BKGND messages to the window as necessary.

    When you call the Set­Window­Pos function, the window manager updates the window size, position, whatever, and then it goes around repainting the windows that were affected by the operation. By default, the Set­Window­Pos function does a quick-repaint of the windows before returning. After the function returns, the normal WM_PAINT message does the real work of painting the window. The quick-repaint is done so that there is immediate feedback that the window did change its size, position, whatever.

    This quick-repaint is done by sending a WM_NCPAINT and WM_ERASE­BKGND message to the windows that were affected by the Set­Window­Pos operation. This normally happens without incident, but if one of the windows affected by the Set­Window­Pos operation belongs to another thread, the window manager needs to get into the context of that other thread to finish the job. That's where WM_SYNC­PAINT comes in. The WM_SYNC­PAINT message means, "Hey, I was going around quick-painting a bunch of windows, but I couldn't quick-paint you (or any other windows on your thread) because I was on the wrong thread. Could you finish quick-painting yourself (and all the other windows that need quick-painting)? Thanks."

    Another way of looking at this is that it is a way for the window manager to teleport itself into another thread so it can finish its work. "Lah di dah, quick-painting all the windows, oh crap, I can't quick-paint that window because it's on the wrong thread. Let me inject myself into that other process [trivial, since I'm the window manager, I'M IN YR PROCESS REEDING YR MSGS], and now I can send a message to myself [WM_SYNCPAINT], and when that other copy of me receives it, he'll finish where I left off."

    If you don't like any of this teleportation or multiple-copies-of-yourself imagery, you can say that the WM_SYNC­PAINT message means, "Quick-paint this window as part of a quick-paint operation begun on another thread."

    If you don't want this quick-paint to take place, you can follow the instructions in the documentation and pass the SWP_DEFER­ERASE flag to suppress the WM_SYNC­PAINT message.

  • The Old New Thing

    Raymond's SIFF schedule (2007)


    Nobody asked, but I'm going to post it anyway. These are the movies I'm planning on seeing, along with my twisted take on the plot, based solely on the movie description.

    12:08 East of Bucharest Fri May 25 5:00pm (PP)
    Residents of a Romanian town differ in their recollections of how they overthrew Ceaucescu.
    Fair Play Sat May 26 1:15pm (N)
    Office politics viewed through the filter of "casual" outdoor sporting activities.
    Outsourced (sold out) May 31 7:00pm (LS)
    American's job is outsourced to India. He is sent to train his replacement. She's cute.
    Death at a Funeral Fri Jun 1 7:00pm (LS)
    Fancy funeral. Somebody dies. Hilarity ensues.
    Cashback Sat Jun 2 6:45pm (LS)
    Clerks + Heroes = ?
    Tell No One Sun Jun 3 1:30pm (N)
    French thriller. Man accused of wife's death. Will probably spend a lot of time running.
    Falling Sun Jun 3 9pm (HE)
    High school friends reunite. Drama ensues.¹
    2 Days in Paris Tue Jun 5 7pm (LS)
    French screwball comedy. That's all you need to know.
    Hula Girls Sat Jun 9 4pm (LS)
    Japanese town reinvents itself as a spa. With hula girls.²
    Grandhotel Mon Jun 11 4:30pm (LS)
    Intruiguing rom-com set in a hotel.
    The Boss of It All Wed Jun 13 7pm (LS)
    Company director shifts blame to imaginary superior. Works great until people ask to meet him.³
    Vacation Sun Jun 17 11am (LS)
    Quiet family get-away. Relatives show up unexpectedly. Drama ensues.¹

    ¹These movies were chosen in large part to serve as German lessons.

    ²I have a weak spot for slightly off-kilter Japanese movies set in the modern day.

    ³Swedish class group movie. Even though it's in Danish.

    Since Outsourced is sold out, I'll have to find another movie to replace it. Possibilities include French for Beginners, Mushihi, The Three Musketeers (animated), and Eagle vs. Shark.

  • The Old New Thing

    A simple email introduction: Fan mail


    One of my former colleagues on the Windows kernel team wasn't afraid to make changes all across the system when necessary. If the engineering team decided to upgrade to a new version of the C++ compiler, my colleague was the one who gave it a test-drive on the entire Windows source code, and fixed all the warnings and errors that kick up as well as ensuring that it passed the build verification tests before updating the compiler in the official toolset. Beyond that, my colleague also ran around being a superhero, writing tools that needed to be written, fixing tools that were broken, and generally being somebody.

    Since the effect on the Windows project was so far-reaching, everybody on the team knew this person, or at least recognized the name, and as a result, my colleage ended up receiving a lot of email about all different parts of Windows, be they bug reports, requests for help using a particular component, whatever.

    And when the question was about something outside my colleague's sphere of responsibility, the message was forwarded to the correct people with a simple introduction:

    From: A
    To: XYZ-owners, Y
    Subject: Problem with XYZ

    Fan mail.

    From: Y
    To: A
    Subject: Problem with XYZ

    Blah blah blah blah

    I've used this technique a few times, but it's been a while. I should start using it again.

    Bonus chatter: At least one of you has come out and said that you post your complaints here with the expectation that the complaints will be forwarded to the appropriate team. This expectation is false. No such forwarding occurs. This Web site is not a complaint desk.

  • The Old New Thing

    How do you prevent the linker from discarding a function you want to make available for debugging?


    We saw some time ago that you can ask the Windows symbolic debugger engine to call a function directly from the debugger. To do this, of course, the function needs to exist.

    But what if you want a function for the sole purpose of debugging? It never gets called from the main program, so the linker will declare the code dead and remove it.

    One sledgehammer solution is to disable discarding of unused functions. This the global solution to a local problem, since you are now preventing the discard of any unused function, even though all you care about is one specific function.

    If you are comfortable hard-coding function decorations for specific architectures, you can use the /INCLUDE directive.

    #if defined(_X86_)
    #define DecorateCdeclFunctionName(fn) "_" #fn
    #elif defined(_AMD64_)
    #define DecorateCdeclFunctionName(fn) #fn
    #elif defined(_IA64_)
    #define DecorateCdeclFunctionName(fn) "." #fn
    #elif defined(_ALPHA_)
    #define DecorateCdeclFunctionName(fn) #fn
    #elif defined(_MIPS_)
    #define DecorateCdeclFunctionName(fn) #fn
    #elif defined(_PPC_)
    #define DecorateCdeclFunctionName(fn) ".." #fn
    #error Unknown architecture - don't know how it decorates cdecl.
    #pragma comment(linker, "/include:" DecoratedCdeclFunctionName(TestMe))
    EXTERN_C void __cdecl TestMe(int x, int y)

    If you are not comfortable with that (and I don't blame you), you can create a false reference to the debugging function that cannot be optimized out. You do this by passing a pointer to the debugging function to a helper function outside your module that doesn't do anything interesting. Since the helper function is not in your module, the compiler doesn't know that the helper function doesn't do anything, so it cannot optimize out the debugging function.

    struct ForceFunctionToBeLinked
      ForceFunctionToBeLinked(const void *p) { SetLastError(PtrToInt(p)); }
    ForceFunctionToBeLinked forceTestMe(TestMe);

    The call to Set­Last­Error merely updates the thread's last-error code, but since this is not called at a time where anybody cares about the last-error code, it is has no meaningful effect. The compiler doesn't know that, though, so it has to generate the code, and that forces the function to be linked.

    The nice thing about this technique is that the optimizer sees that this class has no data members, so no data gets generated into the module's data segment. The not-nice thing about this technique is that it is kind of opaque.

  • The Old New Thing

    Why does the copy dialog give me the incorrect total size of the files being copied?


    If you try to copy a bunch of files to a drive that doesn't have enough available space, you get an error message like this:

    1 Interrupted Action

    There is not enough space on Removable Disk (D:). You need an additional 1.50 GB to copy these files.

    ▭  Removable Disk (D:)
    Space free: 2.50 GB
    Total size: 14.9 GB
    Try again Cancel

    "But wait," you say. "I'm only copying 5GB of data. Why does it say Total size: 14.9 GB?"

    This is a case of information being presented out of context and resulting in mass confusion.

    Suppose you saw the information like this:

    ◢ Hard Disk Drives (1)   
    ▭  Windows (C:)
    Space free: 31.5 GB
    Total size: 118 GB
    ◢ Drives with Removable Storage (1)   
    ▭  Removable Disk (D:)
    Space free: 2.50 GB
    Total size: 14.9 GB

    In this presentation, it is clear that Total size refers to the total size of the drive itself.

    So the original dialog is not saying that the total size of data being copied is 14.49 GB. It's trying to say that the total size of the removable disk is 14.9 GB.

    Mind you, the presentation is very confusing since the information about the removable disk is presented without any introductory text. It's just plopped there on the dialog without so much as a hello.

    I'm not sure how I would fix this. Maybe reordering the text elements would help.

    1 Interrupted Action

    There is not enough space on Removable Disk (D:).

    ▭  Removable Disk (D:)
    Space free: 2.50 GB
    Total size: 14.9 GB

    You need an additional 1.50 GB to copy these files.

    Try again Cancel

    However, the design of the dialog may not allow the information tile to be inserted into the middle of the paragraph. It might be restricted to a layout where you can have text, followed by an information tile, followed by buttons. In that case, maybe it could go

    1 Interrupted Action

    You need an additional 1.50 GB to copy these files. There is not enough space on Removable Disk (D:).

    ▭  Removable Disk (D:)
    Space free: 2.50 GB
    Total size: 14.9 GB
    Try again Cancel

    But like I said, I'm not sure about this.

  • The Old New Thing

    Posted messages are processed ahead of input messages, even if they were posted later


    Regardless of which interpretation you use, it remains the case that posted messages are processed ahead of input messages. Under the MSDN interpretation, posted messages and input messages all go into the message queue, but posted messages are pulled from the queue before input messages. Under the Raymond interpretation, posted messages and input messages are kept in separate queues, and the message retrieval functions will look first in the posted message queue before looking in the input queue.

    Let's run an experiment to see posted messages get processed ahead of input messages. Start with the new scratch program and make these changes:

    #include <strsafe.h>
    class RootWindow : public Window
     virtual LPCTSTR ClassName() { return TEXT("Scratch"); }
     static RootWindow *Create();
     void AppendText(LPCTSTR psz)
                          ListBox_AddString(m_hwndChild, psz));
     void AppendFormat(LPCTSTR pszFormat, ...)
      va_list ap;
      va_start(ap, pszFormat);
      TCHAR szMsg[256];
      StringCchVPrintf(szMsg, ARRAYSIZE(szMsg), pszFormat, ap);
     void LogMessage(const MSG *pmsg)
    LRESULT RootWindow::OnCreate()
     m_hwndChild = CreateWindow(
          TEXT("listbox"), NULL,
          0, 0, 0,0, GetHWND(), (HMENU)1, g_hinst, 0);
     return 0;
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       LPSTR lpCmdLine, int nShowCmd)
       while (GetMessage(&msg, NULL, 0, 0)) {
        switch (msg.message) {
        case WM_KEYDOWN:
         PostMessage(prw->GetHWND(), WM_USER, 0, 0);
        case WM_KEYUP:
        case WM_USER:

    This program creates a list box so we can display some output. In the message loop, it sniffs at all the queued messages and does the following:

    • If the message is WM_KEY­UP or WM_USER, then it logs the message timestamp and some parameters.
    • If the message is WM_KEY­DOWN, then it sleeps without processing messages for one second, and then posts a WM_USER message to the main window (which ignores it).

    Run this program, and then tap the shift key.

    The window gets a WM_KEY­DOWN for the shift key. It sleeps for one second (plenty of time for you to release the shift key), and then posts a WM_USER message.

    The WM_USER and WM_KEY­UP messages arrive, and observe via the log window that they arrive out of order. WM_USER message arrived first!

    That's because of the rule that says that posted messages are processed ahead of input messages. (Depending on how you want to look at it, you might say that posted messages are "called out for preferential treatment" in the queue, or you might say that posted messages are placed in a different queue from input messages, and the posted message queue has higher priority.)

    Observe also that the timestamp on the WM_USER message is greater than the timestamp on the WM_KEY­UP message, because the key went up before the WM_USER message was posted. Time has gone backward.

    Make the following change to our program: Change the message we post from WM_USER to WM_KEY­UP:

          PostMessage(hwnd, WM_KEYUP, 0, 0);

    Run the program again, and again tap the shift key. Observe that the posted WM_KEY­UP message is processed ahead of the WM_KEY­UP input message. (You can see the difference because we posted the WM_KEY­UP message with wParam and lParam both zero, whereas the WM_KEY­UP input message has information in those parameters.)

    This little demonstration also reinforces some other things we already knew. For example, it once again shows that the input manager does not wiretap your posted messages. If you post a WM_KEY­UP message, it is treated like a posted message not an input message. We saw earlier that posting a keyboard message does not update internal input states. The keyboard shift states are not updated to match your prank call message. If somebody calls Get­Queue­Status, they will not be told that there is input waiting. It will not wake a Msg­Wait­For­Multiple­Objects function that is waiting for QS_INPUT. And as we saw here today, the message gets processed out of order.

  • The Old New Thing

    How can I detect that a user's SID has changed and recover their old data?


    A customer maintained a database which recorded information per user. The information in the database is keyed by the user's SID. This works out great most of the time, but there are cases in which a user's SID can change.

    "Wait, I thought SIDs don't change."

    While it's true that SIDs don't change, it is also true that the SID associated with a user can change. Since SIDs encode the domain to which they belong, a user which moves from one domain to another within an organization, will need to be assigned a new SID.

    But wait, does that mean that the user lost access to all their stuff? After all, all their stuff was marked "Owned by X\UserName" but the user's SID is now Y\UserName.

    No, the user doesn't lose access to their stuff thanks to SID history, and if you move users around a lot, the SID history can get quite large.

    A token for a user contains not only their current identity but also all of their earlier identities. That is what permits Y\UserName to continue to access things that was marked "Owned by X\UserName": The token for Y\UserName includes an entry that says, "Oh, I used to be X\UserName."

    The customer's database can take advantage of the SID history to match up users with their former selves. Our customer was lucky in that their database recorded only users who had logged into the local machine, so that list is typically pretty small. The simplest solution for this particular customer is just to go through all the users in the database, and for each one, see if the current user has that database user in their SID history. And the easy way to do that is to make the security system do the work for you: To see if the current user has user X in their SID history, create a security descriptor that grants access only to user X, then call Access­Check to see if the current user can access it. If so, then that means that the current user was at one point in the past known as X.

    (If you have a large database where iterating over all users is impractical, you can ask for the current user's SID-History attribute and walk through the previous identities manually.)

  • The Old New Thing

    Horrifically nasty gotcha: FindResource and FindResourceEx


    The Find­Resource­Ex function is an extension of the Find­Resource function in that it allows you to specify a particular language fork in which to search for the resource. Calilng the Find­Resource function is equivalent to calling Find­Resource­Ex and passing zero as the wLanguage.

    Except for the horrible nasty gotcha: The second and third parameters to Find­Resource­Ex are in the opposite order compared to the second and third parameters to Find­Resource!

    In other words, if you are adding custom language support to a program, you cannot just stick a wLanguage parameter on the end when you switch from Find­Resource to Find­Resource­Ex. You also have to flip the second and third parameters.

    Original code Find­Resource(hModule, MAKEINTRESOURCE(IDB_MYBITMAP), RT_BITMAP)
    You change it to Find­Resource­Ex(hModule, MAKEINTRESOURCE(IDB_MYBITMAP), RT_BITMAP, 0)
    You should have changed it to Find­Resource­Ex(hModule, RT_BITMAP, MAKEINTRESOURCE(IDB_MYBITMAP), 0)

    The nasty part of this is that since the second and third parameters are the same type, the compiler won't notice that you got them backward. The only way you find out is that your resource code suddenly stopped working.

Page 369 of 460 (4,594 items) «367368369370371»