• The Old New Thing

    When does STARTF_USESHOWWINDOW override the parameter passed to ShowWindow()?

    • 6 Comments

    kokorozashi wants to know what the rules are which govern when the second parameter to ShowWindow is overridden by the STARTF_USESHOWWINDOW flag.

    The guiding principle is that the parameter is ignored if the window manager thinks that the window you're creating is the application's main window.

    The details behind the implementation of this principle change over time, so everything from here down is implementation detail and should not be relied upon. I'm providing it merely to satisfy your curiosity.

    To reiterate, do not rely on information in the second half of this article because it can and will change.

    In fact, just to emphasize the point, I'm going to give the rules as they once were, not as they are today. So anybody who relies on this information is relying on implementation details of Windows which are no longer true.

    The window manager heuristics for determining whether the second parameter to ShowWindow should be overridden were once as follows:

    Rule zero: If the override has already been used, then don't use it again.

    Rule one: The easy case. If the second parameter was SW_SHOWDEFAULT, then the application was explicitly permitting the second parameter to ShowWindow to be overridden by the STARTF_USESHOWWINDOW flag, so let it happen.

    Rule two: Check the following properties.

    1. The STARTF_USESHOWWINDOW flag was set.
    2. The window was top-level.
    3. The window was not owned.
    4. The window had the WS_CAPTION style.
    5. The window was not system-modal.
    6. The second parameter to ShowWindow was SW_SHOWNORMAL or SW_SHOW.

    Let's look at these heuristics one at a time.

    First, the STARTF_USESHOWWINDOW flag needed to be set: If it wasn't, then there wasn't anything to override with.

    Next, the window needed to be top-level (not a child window). Because a child window clearly is not the application's main window.

    The window also must not have been owned. An owned window is not the main window (the owner would be a much better candidate), and besides, it would be bad to have minimized or hidden an owned window, since that would have left the owner sitting around for apparently no reason. Even worse if the window being created was intended to be modal to the owner: You would have had a disabled window on the screen, and the window you needed to close in order to get that window enabled again was hidden!

    Another rule was that the window had to have a caption. This made it less likely that splash screens and other incidental windows would be misdetected as the application's main window.

    System-modal windows were also excluded, because you didn't want system-critical error messages to be mistaken for the application's main window. (Especially if the action was SW_HIDE!)

    The second parameter to ShowWindow had to be one of the special values SW_SHOW or SW_SHOWNORMAL. These values were most likely to be passed by applications which were not particular about how the window was shown. They would be comparatively unlikely to be upset that their attempt to show the window was overridden.

    Once a window was identified as a likely main window (either by explicitly saying so via SW_SHOWDEFAULT or implicitly via the heuristics), the second parameter to ShowWindow was ignored and replaced with the value specified by STARTF_USESHOWWINDOW.

    There was some other fiddling that happened, but they aren't really important to the topic at hand, so I'll ignore them.

    Again, I reiterate that this information is provided merely to satisfy your curiosity and must not be relied upon by applications, since the heuristics may be tweaked in future versions of Windows. If you want the STARTF_USESHOWWINDOW flag to have an effect on your program, just pass SW_SHOWDEFAULT to ShowWindow. That's the value that says, "No really, I'm asking for it. Lemme have it."

  • The Old New Thing

    Thanks for coming together to enjoy the holiday together, now get off my lawn

    • 6 Comments

    Due to a confluence of circumstances involving parents on an overseas vacation and other older relatives choosing not to attend (probably related to parents being out of town), I ended up being the oldest person at the extended family holiday dinner table.

    This was a first for me, and I resisted the urge to shout "Get off my lawn!" to the assembled masses or to bore younger relatives with rambling pointless stories from my youth.

    I guess this is the converse to a friend of mine who presided over a holiday family gathering at his house some years ago, and who realized that if seating at the dinner table were done strictly by age, he would have to sit at the children's table.

  • The Old New Thing

    I could just use a picture of a regular-sized shopping cart from farther away

    • 6 Comments

    Internet retailer woot! went to CES 2010 and covered it on their blog. (CES category.) But they don't cover what the media elite cover, the big announcements, the hot products. Nope, they cover the weird stuff.

    They have uncrating photos of CES itself, they infiltrate the The Consumer Breakfast Buffet Show, and they take super secret spy pictures of a miniature shopping cart.

  • The Old New Thing

    A simple email introduction: Fan mail

    • 6 Comments

    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

    What's the point of the various ...WhenCallbackReturns functions?

    • 6 Comments

    The thread pool provides a number of functions named ...When­Callback­Returns. What's the point of all these functions? Why can't you just do the operation yourself immediately before returning?

    We saw Free­Library­When­Callback­Returns last time. What's the point of the others?

    Basically, the same thing as Free­Library­When­Callback­Returns. It's a way to release a resource after execution has left the function and the callback is marked as complete. In the case of a synchronization resource, that resource may be what's keeping somebody from unloading your DLL, or it might protect a race condition between the callback function and a function that tries to cancel the callback.

  • The Old New Thing

    It's not a good idea to give multiple controls on a dialog box the same ID

    • 6 Comments

    When you build a dialog, either from a template or by explicitly calling Create­Window, one of the pieces of information about each control is a child window identifier. And it's probably in your best interest to make sure two controls on the dialog don't have the same ID number.

    Of course, one consequence of giving two control the same ID number is that the Get­Dlg­Item function won't know which one to return when you say, "Please give me control number 100." This naturally has a cascade effect on all the other functions which are built atop the Get­Dlg­Item function, such as Set­Dlg­Item­Int.

    Another reason to avoid duplication is that many notification messages include the control ID, and if you have a duplicate, you won't know which one generated the notification. Okay, this isn't actually the case, because the notification messages typically also include the window handle, so you can use the window handle to distinguish between your two controls both with ID=100. Though it means that you can't use a simple switch statement any more.

    (See sidebar for discussion of when duplicate IDs are acceptable.)

    Most of the time, you get away with the duplicate IDs because you can use the window handle to distinguish them. But there is one notable case where duplicate IDs cause problems: Identifying the default pushbutton on the dialog.

    One of the things that the dialog manager does when it builds a dialog box from a template is keep an eye out for a button control with the BS_DEF­PUSH­BUTTON style. When it finds one, it remembers the control ID so that it can restore it as the default pushbutton when focus is on a non-pushbutton control. (When focus is on a pushbutton, then that button becomes the default pushbutton.)

    The dialog manager records the initial default pushbutton by sending itself the DM_SET­DEF­ID message, and the default handler merely records the value in a safe place so it can return it when somebody sends the DM_GET­DEF­ID message. You can send the DM_SET­DEF­ID message yourself if you want to change the default pushbutton, and that's where the trouble comes in.

    The only parameter to the DM_SET­DEF­ID message is the ID of the dialog control you want to be the new default, so if your dialog box has two controls with that ID, then you've created a bit of a problem. When the user hits the Enter key, the dialog manager wants to fire a WM_COMMAND notification for the default button, but it sees two of them and gets confused.

    Actually, it doesn't really get confused. It just picks one of them arbitrarily and ignores the other one.

    And then confusion sets in.

    If the two buttons with conflicting IDs do different things, then your code which receives the WM_COMMAND notification may end up seeing the notification coming from the wrong control. For example, suppose you inadvisedly assign ID number 100 to both the Reformat and Scan buttons (and out of an abundance of caution, set Scan as the default pushbutton). When the user hits Enter, the dialog manager sends a WM_GET­DEF­ID message to say, "Hey, what's the default pushbutton?" The message returns 100, and now the dialog manager is stuck saying to itself, "Um, there are two 100's. Eeny, meeny, miny, moe. Okay, it's the Reformat button." Boom, hard drive reformatted.

    From the same dialog template, suppose you realize, "Oh, I don't want to let the user reformat the hard drive until they've entered a volume label," so you disable the Reformat button if the volume label field is blank. The user hits Enter, and remember, you set Scan as the default button. But since Reformat and Scan have the same control ID, the dialog manager once again plays eeny-meeny-miney-moe, and say it picks the Reformat button. But it also sees that the Reformat button is disabled, so it just beeps.

    And then your user wonders why, when they hit Enter and the Scan button is the default pushbutton, it doesn't scan but instead just beeps.

    Okay, all this discussion seems pretty obvious, doesn't it, but as we dig deeper into the dialog manager, you'll see how the principle of "Don't create a dialog box with conflicting dialog control IDs" has perhaps unexpected consequences.

    Sidebar: If the duplications are all among controls that do not raise notifications and which you do not need to identify programmatically, then you're not going to run into much trouble at all. By convention, the "control ID for controls where I don't care about the ID" is −1, although you can use any number you like; the window manager doesn't care, as long as it doesn't collide with the ID of a control that you do care about.

    Note that some resource management tools (such as translation toolkits, or interactive dialog editors) assume that there are no duplicate IDs aside from the special don't-care value −1, so if you're going to use duplicate IDs because you don't care, you'd be well-served to stick to the −1 convention.

    Bonus chatter: Why doesn't DM_SET­DEF­ID take a window handle instead of a control ID? That would solve the problem of multiple controls with the same ID, since you now have the window handle, which identifies the control uniquely.

    Yeah, it could've done that. Though it would also have created problems if the default pushbutton is destroyed, and that happens more often than you think.

    Remember back in the early 16-bit days, we didn't have parameter validation, so a dangling window handle meant that you crashed when you tried to use it. (Or worse, the window handle could have been reused for another totally unrelated window! Window handle reuse was much more common in 16-bit Windows.) Mapping the window handle back to an ID and then converting the ID to a window on demand meant that you never keep a window handle around, which means that you never have to worry about the handle going bad.

    Making the DM_SET­DEF­ID message handle-based would also make it harder for somebody to pull the "Create two controls with the same ID but hide one of them at runtime" trick described above, because they would also have to remember to send a hypothetical DM_SET­DEF­HWND message whenever they pulled the switcheroo.

    And besides, the only people this design change helps out are people who put multiple visible controls on a dialog box with the same ID. You don't optimize for the case where somebody is mis-using your system.

  • The Old New Thing

    The resource compiler will helpfully add window styles for you, but if you're building a dialog template yourself, you don't get that help

    • 6 Comments

    A customer was having trouble with nested dialogs. They were doing something very similar to a property sheet, with a main frame dialog, and then a collection of child dialogs that take turns appearing inside the frame dialog based on what the user is doing. The customer found that if they created the child dialogs with the Create­Dialog­Param function, everything worked great, but if they built the template at run-time, keyboard navigation wasn't working right. Specifically, one of their child dialogs contained an edit control, and while you could put focus on it with the mouse, it was not possible to tab to the control. On the other hand, a resource template didn't have this problem. Tabbing in and out worked just fine.

    There is logically no difference between a resource-based dialog template and a memory-based one, because the resource-based one is implemented in terms of the memory-based one.

    The real problem is the memory-based template you created differs somehow from the one the resource compiler generated.

    One way to identify this discrepancy is simply to do a memcmp of the two dialog templates, the resource-based one and the memory-based one, and see where they differ. After all, if you want to know why your template doesn't match the one generated by the resource compiler, you can just ask the resource compiler to generate the template and then compare the two versions.

    Instead of explaining this, I decided to invoke my psychic powers.

    My psychic powers tell me that you neglected to provide the WS_TAB­STOP style to the edit control when you created your in-memory template. (You probably didn't realize that you needed to do this, because the resource compile adds that style by default.)

    When you use the resource compiler to generate a dialog template, it sets a bunch of styles by default, depending on the type of control. For example, EDIT­TEXT says "If you do not specify a style, the default style is ES_LEFT | WS_BORDER | WS_TABSTOP."

    Not mentioned is that the default style is in addition to the defaults for all controls: WS_CHILD | WS_VISIBLE.

    If you want to turn off one of the default styles for a control, you do so with the NOT keyword. For example, if you write

       EDITTEXT IDC_AWESOME, 10, 10, 100, 100, ES_MULTILINE | NOT WS_VISIBLE
    

    the resource compiler starts with the default style of

    dwStyle = WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER | WS_TABSTOP;
    

    then it adds ES_MULTI­LINE:

    dwStyle |= ES_MULTILINE;
    // dwStyle value is now
    // WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER | WS_TABSTOP | ES_MULTILINE
    

    and then it removes WS_VISIBLE:

    dwStyle &= ~WS_VISIBLE;
    // dwStyle value is now
    // WS_CHILD | ES_LEFT | WS_BORDER | WS_TABSTOP | ES_MULTILINE
    

    which is the final style applied to the control.

    The resource compiler is trying to help you out by pre-setting the styles that you probably want, but if you don't realize that those defaults are in place, you may not realize that you need to provide them yourself when you don't use the resource compiler. Maybe it was being too helpful and ended up resulting in helplessness.

    The customer was kind enough to write back.

    Thanks! That did the trick.

    For completeness, the default dialog style is WS_POPUP­WINDOW = WS_POPUP | WS_BORDER | WS_SYS­MENU. If you have a custom font, then you also get DS_SET­FONT, and if you have a caption, then you get WS_CAPTION.

  • The Old New Thing

    When you synthesize input with SendInput, you are also synthesizing the timestamp

    • 6 Comments

    A customer was reporting a problem when they used the Send­Input function to simulate a drag/drop operation for automated testing purposes.

    I see the mouse move from one location to another, and the starting and stopping locations are correct on the screen, but the mouse moves instantaneously rather than waiting 500ms between operations. Here's how I'm sending the input.

    INPUT input[3] = { 0 };
    
    // Click
    input[0].type = INPUT_MOUSE;
    input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    input[0].mi.time = 500;
    
    // Drag
    input[1].type = INPUT_MOUSE;
    input[1].mi.dwFlags = MOUSEEVENTF_MOVE;
    input[1].mi.dx = 100;
    input[1].mi.dy = 100;
    input[1].mi.time = 1000;
    
    // Release
    input[2].type = INPUT_MOUSE;
    input[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
    input[2].mi.time = 500;
    
    SendInput(3, input, sizeof(INPUT));
    

    Well, yeah, all the events occur immediately because you submitted them all at once.

    The time field in the MOUSE­INPUT structure is not for introducing delays in playback. Though I'm not sure what the customer thought the time field was. They say that they want a 500ms delay between operations. At first, I thought that they may have misinterpreted it as a delay relative to the time the Send­Input call is made, since they set input[0].mi.time to 500 and input[1].mi.time to 1000. But if thay were the case, then setting input[2].mi.time to 500 would end up going backward in time. But looking at the big picture, it's probably not worth trying to figure out what they were thinking, since that code will have to be scrapped anyway.

    The time field is for letting an input source (typically a hardware device) say, "Hi, um, the mouse left button went down at 9:30 this morning. Yes, I know it's already 10am. The PCI bus got a flat tire, and then the spare was also flat, and really there's no point going into the details. Sorry this message arrived late." The window manager (and anybody else who bothers to check the time member of the MSG structure) uses this information to do things like detect double-clicks. If the input source later reports, "Hi, um, the mouse left button went up at 9:30:00.100 this morning, sorry for the late report," the window manager says, "Well, that was only 100 milliseconds after the button went down thirty minutes ago, so I guess that's a double-click after all. Could you try to be a bit more prompt with this information in the future?" (Sarcasm added.)

    In other words, the time member of the MOUSE­INPUT structure is for backdating input events. They still get delivered immediately, but the timestamp allows the window manager (and other code which looks at the timestamp) to make decisions about how they should respond.

    Note that post-dating the timestamp does not cause the input delivery to be delayed, and back-dating the timestamp does not cause the input to be inserted into the input stream ahead of other input. The input is merely delivered with a timestamp in the future or in the past. (And who knows what sort of havoc that will create if a program checks the timestamps and notices that they are either from the future or have traveled back in time. Maybe you'll get a call from Microsoft Research asking for more information about your time machine.)

    If you want three input events to take place with a 500ms delay between them, then you need to call Send­Input three times, with a 500ms delay between the calls.

  • The Old New Thing

    IShellFolder::BindToObject is a high-traffic method; don't do any heavy lifting

    • 6 Comments

    A customer observed that once the user visited their shell extension, Explorer ran really slowly. (Actually, it took a while just to get to this point, but I'll skip the early investigations because they aren't relevant to the story.) Some investigation showed that Explorer's tree view was calling into the shell extension, which was in turn hanging the shell for several seconds at a time.

    Explorer was calling into the shell extension because the node was in the folder tree view, and Explorer was doing a little bookkeeping to synchronize the folder state with the view. The node referred to a server that was no longer available, so when Explorer asked the shell extension, "Hey, do you have any translucent froodads for me?" the shell extension went off and tried to contact the server (30 second timeout) before returning with the answer, "Um, sorry, I'm not sure what you're talking about."

    The problem was in the shell extension's IShell­Folder::Bind­To­Object method. The Bind­To­Object method is how you navigate from a parent to a child object, but this is supposed to be a lightweight navigation. Don't try to validate that the child still exists. Just bind to the child as if it existed. Only when the client tries to do something interesting should you go check whether the object actually exists.

    You can see this in the shell, for example. Suppose you generate a pidl to a network server. Meanwhile, the network server goes down. If you try to bind to that pidl, the bind will succeeed (and quickly). Only if you then ask a question like Enum­Objects will you find out, "Oh, wait, this server doesn't actually exist."

    (Validating existence on bind doesn't really buy you much anyway, because the server might go down after the bind succeeds but before the Enum­Objects call, so clients have to be prepared anyway for the possibility of a successful bind but a failed enumeration.)

    In the shell, binding is a common operation since it's a prerequisite for talking about objects. As long as the pidl is valid, you should succeed the bind. Try not to hit the disk and definitely don't hit the network. There's plenty of time to do that later. Because the bind may not even have been done with the intention of communicating with the target; the client may have bound to the pidl just to be able to talk about the target. (As in this case, where the shell wasn't interested in the target per se; it just wanted to know if the target had a translucent froodad.)

  • The Old New Thing

    Fabio coming to Redmond. Also: Whey Protein

    • 6 Comments

    Mark your calendars: Fabio Lanzoni, better known to the world as simply Fabio, will be at the Redmond Whole Foods Market on June 21 to promote his whey protein product. (Now made with real Fabio!) And unlike Martha, he will let you take a picture of him.

    By the way, ladies, he's available!

Page 378 of 419 (4,184 items) «376377378379380»