Holy cow, I wrote a book!
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.
MF_GROUP
WS_GROUP
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:
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:
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.
IMAGE_OPTIONAL_HEADER
SizeOfHeapReserve and SizeOfHeapCommit 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 /LARGEADDRESSAWARE flag or the markers which indicate the default layout direction.
SizeOfHeapReserve
SizeOfHeapCommit
/LARGEADDRESSAWARE
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.
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.TimeDateStamp 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?
IMAGE_FILE_HEADER.TimeDateStamp
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).
time_t
ctime
If you want to retrieve this value, you can use helper functions in the imagehlp library; the one most applicable here appears to be ImageNtHeader or even GetTimestampForLoadedLibrary.
imagehlp
ImageNtHeader
GetTimestampForLoadedLibrary
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.
UniqueId
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 AppendText to add a string to the list box and LogMessage 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.
AppendText
LogMessage
MSG
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 fIgnoreKeyboard = TRUE mode, where it stops pumping keyboard messages (but pumps everything else).
fIgnoreKeyboard = TRUE
What we just did is leave the WM_KEYUP 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.
WM_KEYUP
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 PeekMessage for WM_KEYLAST + 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.
PeekMessage
WM_KEYLAST + 1
0xFFFFFFFF
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_MOUSEMOVE 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.
WM_MOUSEMOVE
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:
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.
QS_KEY
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?
It's that time again: Linking to other Microsoft bloggers.
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?
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.
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.
APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL
VARIANT_TRUE
We then commit those properties back into the shortcut.
Finally, we save the shortcut.