• The Old New Thing

    Why can't you use the space bar to select check box and radio button elements from a menu?

    • 17 Comments

    Nektar wants to know why you can't use the space bar to select check box and radio button elements from a menu.

    The short answer is "Because it's a menu, not a dialog box."

    The check mark and radio button are just visual adornments provided by the menu manager as a courtesy; they do not affect the behavior of the menu itself. Notice, for example, that there is no way to specify "radio button groups" in a menu, so the menu manager wouldn't know which items needed to be deselected when you select a radio button menu item. (I guess it could infer them from separators, but then you would have people saying "I want my radio button group to exclude item number 4, but I don't want to put a separator in between; that looks ugly.")

    And then how would a program reject an auto-selected check box or radio button? E.g., the user pushes the space bar to turn on Show the Widget Bar and an error occurs trying to show the Widget Bar. If the program displays an error dialog, that would dismiss the menu. So maybe the program would just silently re-uncheck the box, which leaves the user puzzled as to why the space bar "doesn't work" for turning on the Widget Bar. Or worse, what if hiding the Widget Bar causes the menu to reconfigure itself? (Maybe there are some menu items that are visible only when the Widget Bar is selected; if the user hides the Widget Bar, those menu items need to be deleted.) Windows doesn't have a precedent for menus reconfiguring themselves while they're being displayed. What if one of the items that gets deleted when you hide the Widget Bar is the menu item that contains the Widget Bar checkbox? ("I turned off the Widget Bar, and my menu disappeared!")

    That said, there is no technical reason these design issues couldn't be overcome. You could have a style like MF_GROUP that behaves like WS_GROUP to establish the scope of menu radio buttons; you could have some way to mark a menu item as "this is an unselected check box" or "this is an unselected radio button"; you could come up with a way for a program to reject a user's attempt to change the check box status; you could design a way for menus to be dynamically reconfigured while they are open; you could even design a way for menus to respond in some vaguely reasonable way when the item the user just selected gets dynamically deleted! But all of these features take effort, and they detract from the simple design of a menu as "Here's a list of things you can do. Pick one. Once you pick one, the menu dismisses." Every feature starts out with minus 100 points and needs to beat out the other 200 items on the list.

  • The Old New Thing

    Paul Cézanne and Camille Saint-Saëns may have similar-sounding last names, but they are not the same person

    • 12 Comments

    Next week, the Seattle Symphony Orchestra performs the Saint-Saëns Organ Symphony, but the people responsible for the symphony's radio advertisements don't realize that.

    As the strains of the symphony resound in the background, the announcer proudly announces that tickets are still available for "Cézanne's Organ Symphony."

    The names Cézanne and Saint-Saëns may sound similar, but I can assure you that they are not the same person. Here's a handy chart:

    Cézanne Saint-Saëns
    French X X
    Born in 1830's X X
    Master painter X  
    Master composer   X
    Carnival X  
    The Carnival of the Animals   X
    Died of pneumonia X X

    This is not the first time the Seattle Symphony's radio announcer has made this substitution. I don't know whether somebody is giving him incorrect copy to read, or whether he is performing on-the-fly auto-correction. Or maybe the announcer guy asked, "How do you pronounce Saint-Saëns?" and somebody answered, "It sounds like Cézanne."

    And now that I cleared everything up, let me confuse things some more:

  • The Old New Thing

    What do SizeOfStackReserve and SizeOfStackCommit mean for a DLL?

    • 5 Comments

    Nothing.

    Those fields in the IMAGE_OPTIONAL_HEADER structure are meaningful only when they appear in the EXE. The values provided in DLLs are ignored.

    Size­Of­Heap­Reserve and Size­Of­Heap­Commit fall into the same category. In general, flags and fields which control process settings have no effect when declared in a DLL. We've seen a few examples already, like the /LARGE­ADDRESS­AWARE flag or the markers which indicate the default layout direction.

  • The Old New Thing

    Redistributing computers among offices for heating purposes

    • 25 Comments
    Some time ago, I joked about the people who rearrange computers in their house during the winter in order to use them as space heaters.

    Turns out this happens a lot at Microsoft. One of my friends said that one of his coworkers used a small heater in her office to keep warm. On the other hand, his office always ran warm because of all the computers in it. They hit upon a simple solution to both problems: "Now she's using a 12 core/24 thread space heater that's a lot quieter than her old one."

    At one point in time, I had a large number of computers in my office, including an Itanium prototype. (You knew it was a prototype because it contained Engineering Styrofoam.) The thing generated a lot of heat. My friend across the hall, on the other hand, had a cold office. Solution: With some help from colleagues, we moved the Itanium across the hall. Two problems solved.

  • The Old New Thing

    What does the executable timestamp really mean?

    • 12 Comments

    A customer was looking for some information on the executable timestamp:

    I would like my program to display the date it was linked. The IMAGE_FILE_HEADER.Time­Date­Stamp looks like what I need. Is there an easy way to retrieve this information so I don't have to parse the EXE header myself? Also, what functions exist for formatting this timestamp into something human-readable?

    The customer didn't explain why they needed this information, but presumably it was for diagnostic purposes. For example, the program might display the information in the About box to help the product support team identify which version of the program the end-user is running.

    We'll answer the questions in reverse order, and then answer a question that wasn't even asked.

    The timestamp is a Unix-style time_t timestamp; therefore, you can use the ctime function to convert it to text. If there is a particular format you like, you can use the appropriate time formatting function (though you may have to convert it first).

    If you want to retrieve this value, you can use helper functions in the imagehlp library; the one most applicable here appears to be Image­Nt­Header or even Get­Time­stamp­For­Loaded­Library.

    The unasked question is "Does this in fact give me the date and time that the image was linked?" Fortunately, I don't have to write out the answer to this question, because I answered it last year. The name timestamp is misleading. Its real purpose is to act as a signature so that the operating system can determine whether a DLL against which one set of values was precalculated matches the DLL physically on the system. A better name for it would have been UniqueId.

  • The Old New Thing

    A pathological program which ignores the keyboard, and understanding the resulting behavior based on what we know about the synchronous input

    • 11 Comments
    Today, we'll illustrate the consequences of the way the window manager synchronizes input when two or more threads decide to share an input queue.

    Since I need to keep separate state for the two windows, I'm going to start with the new scratch program and make the following changes:

    #include <strsafe.h>
    
    class RootWindow : public Window
    {
    public:
     virtual LPCTSTR ClassName() { return TEXT("Scratch"); }
     static RootWindow *Create();
    
     void AppendText(LPCTSTR psz)
     {
      ListBox_SetCurSel(m_hwndChild,
                        ListBox_AddString(m_hwndChild, psz));
     }
    
     void LogMessage(const MSG *pmsg)
     {
       TCHAR szMsg[80];
       StringCchPrintf(szMsg, 80, TEXT("%d\t%04x\t%p\t%p"),
                       pmsg->time,
                       pmsg->message,
                       pmsg->wParam,
                       pmsg->lParam);
       AppendText(szMsg);
    }
    
    protected:
     LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
     LRESULT OnCreate();
    private:
     HWND m_hwndChild;
    };
    
    LRESULT RootWindow::OnCreate()
    {
     m_hwndChild = CreateWindow(
          TEXT("listbox"), NULL,
          LBS_HASSTRINGS | LBS_USETABSTOPS |
          WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL,
          0, 0, 0,0, GetHWND(), (HMENU)1, g_hinst, 0);
    
     return 0;
    }
    

    All we did above was add a list box to the window and provide public methods Append­Text to add a string to the list box and Log­Message that adds a string based on the contents of a MSG structure. We're going to use this list box to log what the program is doing.

    bool ShouldLogMessage(UINT uMsg)
    {
     if (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST) return true;
     if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) return true;
     return false;
    }
    

    This helper function above tells us which messages we want to log. For now, let's log keyboard and mouse messages.

    Now, in order to demonstrate input thread attachment, we need two threads. Here comes the second thread:

    DWORD CALLBACK AttachedThreadProc(void *lpParameter)
    {
     RootWindow *prw = RootWindow::Create();
     SetWindowText(prw->GetHWND(), TEXT("Bad window"));
     AttachThreadInput(PtrToInt(lpParameter),
                       GetCurrentThreadId(), TRUE);
     ShowWindow(prw->GetHWND(), SW_SHOW);
    
     BOOL fIgnoreKeyboard = FALSE;
    
     while (true) {
      MSG msg;
      BOOL fMessage;
      if (fIgnoreKeyboard) {
       fMessage =
        PeekMessage(&msg, NULL, 0, WM_KEYFIRST - 1, PM_REMOVE) ||
        PeekMessage(&msg, NULL, WM_KEYLAST + 1, 0xFFFFFFFF, PM_REMOVE);
      } else {
       fMessage = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
      }
    
      if (!fMessage) { WaitMessage(); continue; }
    
      if (msg.message == WM_QUIT) break;
    
      if (ShouldLogMessage(msg.message)) {
       prw->LogMessage(&msg);
      }
    
      if (msg.message == WM_KEYDOWN && msg.wParam == VK_SHIFT) {
       prw->AppendText(TEXT("Stop processing keyboard messages"));
       fIgnoreKeyboard = TRUE;
      }
    
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
     AttachThreadInput(PtrToInt(lpParameter),
                       GetCurrentThreadId(), FALSE);
     return 0;
    }
    

    This second thread is intentionally ill-behaved, so that we can see what happens when there's a bad apple in the barrel. The thread processes messages normally, until you hit the shift key. Once that happens, it goes into another mode where it starts ignoring the keyboard by stubbornly refusing to pump any keyboard messages.

    Normally, this sort of recalcitrant behavior would affect only the thread itself, but since this thread is attached to the main thread, the scope of the damage expands.

    int PASCAL
    WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int nShowCmd)
    {
     g_hinst = hinst;
    
     if (SUCCEEDED(CoInitialize(NULL))) {
      InitCommonControls();
    
      RootWindow *prw = RootWindow::Create();
      if (prw) {
       ShowWindow(prw->GetHWND(), nShowCmd);
    
      DWORD dwId;
      CreateThread(0, 0, AttachedThreadProc,
                   IntToPtr(GetCurrentThreadId()), 0, &dwId);
    
       MSG msg;
       while (GetMessage(&msg, NULL, 0, 0)) {
    
       if (ShouldLogMessage(msg.message)) {
        prw->LogMessage(&msg);
       }
    
        TranslateMessage(&msg);
        DispatchMessage(&msg);
       }
      }
      CoUninitialize();
     }
     return 0;
    }
    

    We modify our main program to create the secondary thread (which attaches itself to the main thread), and then to log messages in its message pump.

    Okay, now run this program and use the mouse to resize and reposition the two windows side by side with no overlap. (This will make it easier to observe what's going on.) Wave the mouse over both windows, and click on each of the windows and do some typing, but don't hit the shift key yet. So far, everything works as you expect: Focus switches back and forth, mouse and keyboard messages are delivered.

    Now put focus on the bad window and tap the shift key. This puts the bad window into fIgnore­Keyboard = TRUE mode, where it stops pumping keyboard messages (but pumps everything else).

    What we just did is leave the WM_KEY­UP message for the shift key in the input queue, and steadfastly refused to process it. The message just sits there forever. Let's see what this does to the input retrieval algorithm.

    Wave the mouse over the bad window. Notice that mouse events are still delivered to the bad window. (Keyboard events are not delivered because the bad thread is not pumping keyboard messages.) This makes sense, because the filtered Peek­Message for WM_KEY­LAST + 1 through 0xFFFFFFFF includes the mouse message range but excludes the keyboard message range, so the loop that looks for a candidate message completely ignores the stuck keyboard message. All it sees are mouse messages, and they are not stuck. The code is taking advantage of the "peek into the future" feature we mentioned yesterday.

    Next thing you notice is that if you wave the mouse over the main window, it does not receive mouse input. That's because the main window performs an unfiltered peek. The stuck keyboard message satisfies the filter, and since that message belongs to another thread and is ahead of all the mouse messages, the input manager will not return the mouse messages until the stuck keyboard message is cleared out.

    This also provides an example of the paradox I alluded to yesterday: The main thread is not receiving any input because it is performing an unfiltered message retrieval, and there is a stuck keyboard message in the input queue. On the other hand, if the main thread had explicitly peeked only for mouse messages, then the stuck keyboard message would not have been taken into consideration, and it would have gotten the mouse messages. The paradox is that under these strange conditions, a filtered message retrieval actually returns messages that an unfiltered retrieval would not!

    Now here's another trick: Click on the main window. (Yes, it's not processing mouse input, but do it anyway.) Now both windows stop responding to input. What happened?

    Back before you clicked on anything, the only stuck input message was that keyboard message. Sure, there were mouse motions that took place, but we saw that WM_MOUSE­MOVE messages are generated on demand rather than being posted into the queue when the mouse moves. Therefore, all that mouse-waving didn't actually leave a stuck mouse message in the queue. On the other hand, when you click, that generates a mouse click event in the queue, and those are generated when the click happens, not on demand. Therefore, when you click on the main window, a click event goes into the input queue.

    Now think about what's in the input queue: There is a stuck keyboard message (for the bad window, which is stuck because the bad window refuses to pump keyboard messages), and there is a stuck mouse message (for the main window, which is stuck because the main window is waiting for the stuck keyboard message to clear out). New keyboard input will not be processed because of the stuck keyboard message, and new mouse input will not be processed because of the stuck mouse message.

    Result: Nobody gets any input.

    Bonus investigation: While you're in this horrible state, open Task Manager. Observe that the scratch program has pegged a CPU. Why is it draining CPU when there is nothing to do?

    There's a little extra step in the overall algorithm that describes how input is processed:

    • If the input queue is waiting for another thread to finish processing an input message, and the current thread is processing an inbound sent message, then mark the input queue as no longer waiting.
    • If the input queue is waiting for another thread to finish processing an input message, then stop and return no message.
    • If the input queue is waiting for the current thread to finish processing an input message, then mark the input queue as no longer waiting.
    • Look at the first message in the input queue which satisfies the message range filter and either belongs to some other thread or belongs to the current thread and matches the window handle filter.
      • If the message belongs to some other thread, then (New!) nudge the other thread to get it to process the message, then stop. Return no message to the caller.
      • Otherwise, mark the input queue as waiting for the current thread to finish processing an input message, and return the message we found.
    • If no such message exists, then there is no input. Return no message.

    Reminder: This is a peek under the hood at how the sausage is made, and the algorithm described above is not contractual.

    If the algorithm cannot return an input message because there is a stuck input message that belongs to another thread, then the algorithm nudges that other thread by setting the appropriate queue state flag (for example, QS_KEY if it is a stuck keyboard message). If the other thread is waiting for that type of message, then the change in queue state will satisfy the wait, and the hope is that other thread will call a message retrieval function to retrieve the stuck message and unclog the input queue.

    That explains why the scratch program is pegging a processor. The bad thread wants to peek out a mouse message, but it can't because of the stuck mouse click event that belongs to the main thread, so it nudges the main thread to say, "Hey, I need you to process that mouse event." The main thread wakes up and tries to pump messages, but it can't retrieve any input because of the stuck keyboard message. The main thread therefore nudges the bad thread to say, "Hey, I need you to process that keyboard event."

    The two threads are therefore busy taking turns yelling at each other, saying, "Hey, you, you need to get out of my way," and together they burn a CPU.

    Now, this is admittedly a pathological program, but it did do a pretty good job of highlighting some of the consequences of synchronous input caused by attaching multiple threads to the same input queue. This is why it's important that threads which share an input queue all be aware of the connection so that they don't accidentally cause trouble for each other.

    Exercise: How would you modify the above program to demonstrate the "waiting for a thread to finish processing a message" part of the input message retrieval algorithm?

  • The Old New Thing

    2013 Q1 link clearance: Microsoft blogger edition

    • 12 Comments

    It's that time again: Linking to other Microsoft bloggers.

  • The Old New Thing

    Who you calling boring?

    • 13 Comments

    A notice was sent out by the real estate department with the provocative subject line Campus notification — Building 7: Marking Boring Locations.

    What? Were the people in the real estate department saying that the people who work in Building 7 need to get some new hobbies? Or maybe they were just going to put up markers like you see in historic districts, but the markers are going to say something like On this spot in 1998, absolutely nothing interesting happened.

    But no, that's not what the message was about. It was announcing that, in preparation for an expansion of the parking garage, there will be markers placed in the courtyard indicating where drilling will be taking place. The message was merely informational, so people won't wonder What are these people doing wandering around the courtyard with surveying equipment and making X's on the patio with spraypaint?

  • The Old New Thing

    Saying that your case is different doesn't make it so

    • 24 Comments

    A customer wanted to do one of those user-hostile things that Windows doesn't make easy to do (the sort of thing I tend to call out on this Web site). After receiving an explanation that Windows doesn't provide a way of doing what they want because it abuses the component in question and goes against user expectations, the customer countered, "Yes, we understand that, but our case is different."

    The customer then proceeded to explain how they intended to use this newfound power (if only they could figure out how to do it) and under what circumstances they intend to invoke it. Their explanation was interesting in that the description could be applied to any other program on the planet.

    Yes, we understand that, but our case is different. We would do this only after the user installs the program or reconfigures it from the Add or Remove Programs control panel. After a few days, we would stop doing it, unless triggered by a reinstall or a reconfiguration.

    So far, there's nothing here that explains why your program should be able to do this, but not, say, Photoshop. There is no evidence that this program is any different from the tens of thousands of other programs out there, many of which probably want to do that very same thing this program wants to do.

    Just because you say that your case is different doesn't make it so.

  • The Old New Thing

    Marking a shortcut to say that it should not be placed on the Windows 8 Start page upon installation or treated as a newly-installed application

    • 15 Comments

    Today's Little Program creates a shortcut on the Start menu but marks it as "Do not put me on the front page upon installation." This is something you should do to any secondary shortcuts your installer creates. And while you're at it, you may as well set the "Don't highlight me as a newly-installed program" attribute used by Windows 7. (Remember, Little Programs do little to no error checking.)

    #define UNICODE
    #define _UNICODE
    #define STRICT
    #include <windows.h>
    #include <shlobj.h>
    #include <atlbase.h>
    #include <propkey.h>
    #include <shlwapi.h>
    
    int __cdecl wmain(int, wchar_t **)
    {
     CCoInitialize init;
    
     CComPtr<IShellLink> spsl;
     spsl.CoCreateInstance(CLSID_ShellLink);
    
     wchar_t szSelf[MAX_PATH];
     GetModuleFileName(GetModuleHandle(nullptr), szSelf, ARRAYSIZE(szSelf));
     spsl->SetPath(szSelf);
    
     PROPVARIANT pvar;
     CComQIPtr<IPropertyStore> spps(spsl);
    
     pvar.vt = VT_UI4;
     pvar.ulVal = APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL;
     spps->SetValue(PKEY_AppUserModel_StartPinOption, pvar);
    
     pvar.vt = VT_BOOL;
     pvar.boolVal = VARIANT_TRUE;
     CComQIPtr<IPropertyStore> spps(spsl);
     spps->SetValue(PKEY_AppUserModel_ExcludeFromShowInNewInstall, pvar);
    
     spps->Commit();
    
     wchar_t szPath[MAX_PATH];
     SHGetSpecialFolderPath(nullptr, szPath, CSIDL_PROGRAMS, FALSE);
     PathAppend(szPath, L"Awesome.lnk");
     CComQIPtr<IPersistFile>(spsl)->Save(szPath, FALSE);
    
     return 0;
    }
    

    First, we create a shell link object.

    Next, we tell the shell link that its target is the currently-running program.

    Now the fun begins. We get the property store of the shortcut and set two new properties.

    We then commit those properties back into the shortcut.

    Finally, we save the shortcut.

Page 370 of 390 (3,899 items) «368369370371372»