• The Old New Thing

    There is no complete list of all notifications balloon tips in Windows

    • 33 Comments

    A customer wanted a complete list of all notifications balloon tips in Windows.

    There is no such list. Each component is responsible for its own balloon tips and is not required to submit their list of balloon tips to any central authority for cataloging. In order to create such a list, somebody would have to go contact every component team and ask them for a list of all their balloon tips, and that component team would probably undertake a search of their code base looking for balloon tips. And figuring out the text of each balloon tip can be tricky since the text may be built dynamically. (And the customer didn't ask for an explanation of the conditions under which each balloon tip may appear, but that's probably going to be their follow-up question.)

    It's like publishing instructions on how to display a message on the message board, and then somebody asking the message board manufacturer, "Please send me a list of all messages that might appear on the message board." The message board manufacturer doesn't know how its customers are using the message board. They would have to go survey their customers and ask each one to build an inventory of every message that could potentially be shown.

    In other words, this customer was asking for a research project to be spun up to answer their question.

    I suspected that this was a case of the for-if antipattern being applied to custom support questions, and I asked what the customer intended to do with this information.

    It turns out that they didn't even want this list of balloon tips. They just had a couple of balloon tips they were interested in, and they wanted to know if there were settings to disable them.

    But even though that was their stated goal, they still reiterated their request for a list of balloon tips. The customer liaison asked, "Is there a possibility is getting a list of balloon tips generated by Windows itself? Even a partial list would be helpful. Can we help with this?"

    What the customer liaison can do is contact each Windows component team and ask them, "Can you give me a list of the balloon tips your component generates? Even a partial list would be helpful." I wished him luck.

    The customer liaison replied, "I passed this information to the customer and will follow up if they have any further questions." No follow-up ever appeared.

  • The Old New Thing

    Horrifically nasty gotcha: FindResource and FindResourceEx

    • 15 Comments

    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.

  • The Old New Thing

    Dreaming up strange inventions: The combination urinal/bidet/washing machine

    • 13 Comments

    I dreamed that a friend of mine was showing me her new appliance: A combination urinal/bidet/washing machine. As we loaded clothes into it, she said, "You okay in there, Stephanie?"

    A muffled voice emerged from within: "Just let me know when you're ready."

  • The Old New Thing

    Proto-Microspeak: Center of value

    • 23 Comments

    I have only one citation, so it may not be proper Microspeak.

    With all of these features covering the scenario end to end, we wanted to create a new center of value.

    I still don't know what it means.

  • The Old New Thing

    When you share an input queue, you have to wait your turn

    • 15 Comments

    Now that we've had a quick introduction to asynchronous input, let's look at some of the details. Remember, this is a peek under the hood at how the sausage is made. The algorithm described here is not part of the API contract and it can change at any time, as long as it services the overall goal of serializing input.

    Let's start by looking at how things worked in the 16-bit world. Even though 16-bit Windows didn't use the term thread (since each application was single-threaded), I will still use the term since that makes the transition to the 32-bit world more straightforward.

    As a point of terminology, I say that a message belongs to a thread if the message targets a window which belongs to that thread. Equivalently, the thread owns the message.

    Now, the goal is to dispatch input messages in chronological order: Only the thread which owns the next input message can retrieve it. All other input must wait their turn to come to the front of the input queue.

    In 16-bit Windows, all input gets added to a system-wide input queue, and the basic algorithm used by Peek­Message and Get­Message for retrieving messages from the input queue goes like this.

    • Look at the first message in the input queue:
      • If the message belongs to some other thread, then stop. Return no message to the caller.
      • Otherwise, return the message we found.
    • If there are no messages in the input queue, then there is no input. Return no message.

    All the rest is tweaking the boundary cases.

    For example, suppose there are two input messages in the input queue, message 1 for thread A, and message 2 for thread B. Thread A calls Get­Message, and the above algorithm returns message 1 to thread A, at which point the new "first message" is message 2, and if thread B calls Get­Message, it will get message 2.

    The catch is that according to the above algorithm, thread B can be told about message 2 before thread A has finished processing message 1. You've introduced a race condition that breaks the rule that input is processed sequentially: Thread B can race ahead of thread A and start processing message 2 before thread A can even get started processing message 1.

    To fix this, we add a new state to the input queue that says "Yea, I just gave somebody an input message, and I'm waiting for that thread to finish processing it before I will hand out another input message."

    • (New!) If the input queue is waiting for another thread to finish processing an input message, then stop and return no message.
    • (New!) 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. (We finished processing it and have come back for more!)
    • Look at the first message in the input queue:
      • If the message belongs to some other thread, then stop. Return no message to the caller.
      • Otherwise, (New!) mark the input queue as waiting for the current thread to finish processing an input message, and return the message we found.
    • If there are no messages in the input queue, then there is no input. Return no message.

    Okay, we fixed a race condition. But now there's a new problem: Suppose thread A retrieves an input message (and therefore puts the input queue into the "waiting for thread A" state), and then thread A sends a message to thread B, and thread B wants to display a dialog box or a menu. According to the rules as we have them so far, we would have a deadlock: That Send­Message call will not return to the first thread until the modal UI is complete, but the modal UI cannot complete because the input queue is waiting for thread A to finish processing the input message.

    The fix for this special case is that if a thread asks for an input message, and it is handling an inbound Send­Message, then the input queue declares that any in-progress input message has finished processing. One way of interpreting this rule is to say, "If a thread sends a message to another thread, then that implicitly completes input processing."

    • (New!) 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:
      • If the message belongs to some other thread, 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 there are no messages in the input queue, then there is no input. Return no message.

    Recall that you are allowed to pass a message range filter and a window handle filter to the Peek­Message and Get­Message functions. The above algorithm was developed on the assumption that there was no message retrieval filter. First, let's add the message range filter to the algorithm:

    • 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 (New!):
      • If the message belongs to some other thread, 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 there are no messages in the input queue which satisfy the message range filter (New!), then there is no input. Return no message.

    That wasn't so hard. If you pass a message range filter, then we care only about messages that pass the filter in determining which one is "at the head of the input queue". Without this additional rule, you wouldn't be able to "peek into the future" to see if, for example, there is a mouse message in the input queue sitting behind the keyboard message that is at the front of the input queue.

    Adding the window handle filter is a little trickier, because we still want to let input be processed in order (among messages which satisfy the message range filter).

    • 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 (New!) 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 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.

    In other words, the window handle is used to control which message is ultimately retrieved, but it does not let you deny another thread access to input which matches the message range filter.

    Whew, that's how 16-bit Windows dispatched input.

    How do we port this to 32-bit Windows and asynchronous input?

    First, we give each thread group its own input queue, rather than having a single system-wide input queue.

    Second, whenever the above algorithm says the input queue, change it to say the calling thread's input queue.

    And that's it!

    In the absence of thread attachment, each thread has its own input queue, so most of the above rules have no effect. For example, You will never see a message that belongs to another thread, because messages that belong to other threads go into those other threads' input queues, not yours. You will never find that your input queue is waiting for another thread, because no other threads have access to your input queue. In the case where there is only one thread associated with an input queue, the algorithm simplifies to

    • Return the first message in the input queue that satisfies both the message range filter and the window handle filter, if any.

    It's only if you start attaching threads to each other that you have multiple threads associated with a single input queue, and then all these extra rules start to have an effect.

    Next time, we'll explore some of the consequences of synchronous input by writing some poorly-behaving code and observing how the system responds. Along the way, we'll discover an interesting paradox introduced by the above algorithm.

    Exercise: The alternative interpretation I gave above does not match the description of the rule, because the rule allows any thread processing an inbound Send­Message to clear the input queue's wait state. Why does the actual rule permit any thread clear the wait state, instead of first checking that the inbound Send­Message is coming from the thread that the input queue is waiting for?

  • The Old New Thing

    Well at least nobody's parking there any more

    • 31 Comments

    There is a field next to the Microsoft building I used to work in, and for a time, people parked their cars out on the field, presumably because finding a proper parking space in the garage became difficult due to overcrowding. To prevent people from parking in the field, Security placed a large log across the access to the field. The technique worked: Nobody parked in the field any more.

    Some months later, our building had a fire drill, and everybody dutifully filed out of the building and waited until the all-clear signal was given to return. Normally, people would wait in the field, because that is the designated assembly area for the building, but oh wait, Security blocked off access to the field. Instead, people waited in the fire lane.

    Good thing this was just a drill, because they would have gotten run over by a fire truck.

    I pointed out to the Life Safety Security representative who was running the fire drill that Parking Security had created a life safety hazard. My observations were duly noted, and it looks like somebody actually paid attention, because a few weeks later, the log was removed. Now if there's a real fire, we can actually reach our designated assembly area.

    I just found it ironic that the Security department created a safety hazard.

  • The Old New Thing

    How to pretend that you attended my talk at UIUC Reflections|Projections 2009

    • 12 Comments

    Step 1: Buy a 1.55-ounce Hershey's Milk Chocolate Bar from a convenience store, supermarket, or (if truly desperate) online.

    Step 2: Print out this candy bar wrapper.

    Step 3: Trim wrapper on registration marks and wrap around candy bar.

    Step 4: Stay up late the night before you plan on watching the video by partying with Ryan North and teaching him how to play beer pong.

    Step 5: Force yourself to wake up the next morning and watch the recorded video of my talk while trying desperately to stay awake. The candy bar might help.

    Note: Although most steps are optional, they are essential if you want an accurate simulation.

  • The Old New Thing

    How do I create a right-aligned toolbar button?

    • 9 Comments

    I didn't think this was all that common a request, but it came in twice from two different customers, so I guess there are still people creating toolbars, and creating them with right-aligned buttons (even though it violates Windows user interface guidelines, though I can't find a citation right now).

    You may have noticed that the toolbar common control doesn't provide a facility for creating right-aligned buttons. Partly because it's a violation of the guidelines anyway, but mostly because the default state of every feature is "not implemented." Adding a feature requires work, and since there is only a finite amount of work available to apply, you have to decide where to spend it. And generally speaking, you don't focus your efforts on helping people violate the guidelines.

    If you want to create a right-aligned toolbar button, you can cobble one together by combining things you already have. I can think of two ways of doing this off the top of my head.

    • Create two toolbars, one for the left-aligned buttons and one for the right-aligned buttons, then size and position them appropriately.
    • Create a separator between the left-aligned buttons and the right-aligned buttons, and set the separator size appropriately.

    Which comes to a third reason why there is no feature for right-aligned toolbar buttons: Because you can already do it yourself without too much effort.

  • The Old New Thing

    Why aren't environment variables being expanded in my RGS file?

    • 12 Comments
    A customer was having trouble with their RGS file.

    I want to include the below line in a .rgs file:

    val HandlerPath = s '%windir%\system32\awesome.dll'.
    

    When I do this, registering of the dll fails with 80002009. Any help? If I change it to

    val HandlerPath = s 'C:\windows\system32\awesome.dll'.
    

    then the registration succeeds (but of course now contains a hard-coded path).

    A common problem people have when asking a question is assuming that the person reading your question has knowledge that is a strict superset of what you know. As a result, people omit details like the answer to the question "How did you register your RGS file?"

    If all else fails read the documentation (which happens to be the #1 hit for "rgs file", or at least was at the time of this writing). And the documentation explains how the % works. And it's not for environment variable expansion.

    Just because you stick something between percent signs doesn't mean that the magical percent sign fairies are going to swoop in and perform environment variable expansion. Wishful thinking does not cause features to spring into existence.

    As the documentation says, you need to use the _ATL_REGMAP_ENTRY macro to create the mapping for replacement variables.

    This type of question reflects a certain mentality which is cute when kids do it, but frustrating when demonstrated by programmers, namely, that features exist merely because you expect them to, rather than because there's any documentation that suggests that they exist.

  • The Old New Thing

    When should I use the FIND_FIRST_EX_LARGE_FETCH flag to FindFirstFileEx?

    • 14 Comments

    Windows 7 introduces a new flag to the Find­First­File­Ex function called FIND_FIRST_EX_LARGE_FETCH. The documentation says that it "uses a larger buffer for directory queries, which can increase performance of the find operation." This is classic MSDN-style normative documentation: It provides "just the facts". Far be it for MSDN to tell you how to write your application; the job of function-level documentation is to document the function. If you want advice, go see a therapist.

    If the reason why you're calling Find­First­File­Ex is to enumerate through the entire directory and look at every entry, then a large buffer is a good thing because it reduces the number of round trips to the underlying medium. If the underlying medium is a network drive halfway around the world, the latency will be high, and reducing the number of calls reduces the overall cost of communication. Another case where you have high latency is if you are enumerating from an optical drive, since those tend to be slow to cough up data, and once you get the medium spinning, you want to get all the information you can before the drive spins the medium back down. On the other hand, if your underlying medium has low latency, then there isn't much benefit to using a large buffer, and it can be a detriment if the channel is low bandwidth, because transferring that large buffer will take a long time, which can result in long pauses on your UI thread.

    But what if you aren't enumerating with the purpose of reading the entire contents but rather are going to abandon the enumeration once you get the answer to your question? For example, maybe your function wants to enumerate the directory to see if it contains more than ten files. Once the tenth call to Find­Next­File succeeds, you're going to abandon the enumeration. In this case, a large buffer means that the underlying medium is going to do work that you will end up throwing away.

    Here's the above discussion summarized in a table, since people seem to like tables so much.

    Scenario Use FIND_FIRST_EX_LARGE_FETCH?
    Enumerating entire directory on UI thread No¹
    on background thread Yes
    Abandoning enumeration prematurely No

    ¹Actually, if you're on a UI thread, you should try to avoid any directory enumeration at all.

Page 377 of 444 (4,432 items) «375376377378379»