• The Old New Thing

    Deftly solving compatibility problems by withholding information


    One of the continuing compatibility problems that plagued Direct3D was the way it reported texture formats. Historically, the way an application checked which texture formats were available was by calling Enum­Z­Buffer­Formats and passing a callback function which is called once for each supported format. The application's callback made some sort of decision based on the information it received. The problem was that any time a new format was added, a bunch of programs ended up not working. Either the new format confused them, or the change in the order of the formats violated some assumption. For example, they may have assumed that if a video card supports R8G8B8 format, then it will always be the first one in the list.

    For a time, the compatibility strategy was to try to detect what flavor of Direct3D the application was designed for and manipulating the list of supported formats in a way to keep that application happy, say by ordering the formats in a particular way or suppressing some formats from the list.

    In Direct3D 8, a new direction was taken: Ask, don't tell.

    Instead of the application being told what formats are available, the application asks, "Do you support format X?" and Direct3D answers "Yes" or "No." This solves both types of problems: Applications never saw a format they didn't expect, because if they didn't expect it, they would never ask for it in the first place. And applications always saw the supported formats in the order they requested, because the application chose what order they asked for them.

    The main casualties of the new design were diagnostic programs which listed technical details of your video card. They no longer were able to get a list of all supported formats; instead, they had to have a table of all the formats and ask for them one at a time.

    There was one company that objected to this new design because they wanted their program to support all texture formats, even the ones that didn't exist at the time the program was written. This is just another variation of Sure, we do that. "Oh look, this video card supports pixel format 826. I'm going to use it! Just one question, though. What's pixel format 826?"

    They broke their problem into two parts and were asking for help with the first part, unaware that even if the managed to solve that part, they were stuck with the impossible second part!

  • The Old New Thing

    Microspeak: Offline (noun)


    Sure, any noun can be verbed, and any verb can be nouned. But today, we're going to noun an adjective.

    I have no written citations of this usage; the only report was via a colleague who overheard it in a hallway conversion.

    I had some offlines with Fred about that.

    In Microspeak, offline is an adjective which means "outside this meeting." In order to keep a meeting on track, the meeting organizer may advise the people engaged in the discussion of a side topic or a topic of limited interest to take it offline, please, meaning discuss this amongst yourselves after the meeting, please. In other words, "Let's not waste valuable meeting time on this topic."

    The above citation converts the adjective offline into a noun, an offline presumably being shorthand for an offline conversation (or other type of communication). The translation would therefore be something like "I had some private conversations with Fred about that."

  • The Old New Thing

    Paint messages will come in as fast as you let them


    There is a class of messages which are generated on demand rather than explicitly posted into a message queue. If you call Get­Message or Peek­Message and the queue is empty, then the window manager will look to see if one of these generated-on-demand messages is due, messages like WM_TIMER, WM_MOUSE­MOVE, and WM_PAINT.

    Neil wonders, "In that program that called Invalidate­Rect 100,000 times, how many paint messages were generated?"

    The Zen answer to this question is "Yes."

    A more practical answer is "As many as you can get."

    When somebody calls Invalidate­Rect, the window manager adds the specified rectangle to the window's invalid region (or invalidates the entire client area if no rectangle is provided) and sets a flag that says "Yo, there's painting to be done!" (It's not actually a flag, but you can think of it that way.)

    When a message retrieval function finds that there are no incoming sent messages to be dispatched nor any applicable messages in the queue to be retrieved, it looks at these extra flags to see if it should generate a message on the fly. If the "Yo, there's painting to be done!" flag is set on a window that the thread is responsible for, a WM_PAINT message is generated for that window. (Similarly, a WM_TIMER is generated if a timer has elapsed, and a WM_MOUSE­MOVE is generated if the mouse has moved since the last time this thread retrieved a mouse message.)

    Therefore, the number of WM_PAINT messages by 100,000 invalidations is not deterministic, but it'll be at least one and may be as high as 100,000. It's basically just a race between the invalidation thread and the paint thread.

    Get­Message (retrieves WM_PAINT)
    WM_PAINT dispatched
    Get­Message (waits for a message)
    Get­Message (returns with WM_PAINT)
    WM_PAINT dispatched
    Get­Message (retrieves WM_PAINT)
    WM_PAINT dispatched
    Get­Message (retrieves WM_PAINT)
    WM_PAINT dispatched
    Get­Message (retrieves WM_PAINT)
    WM_PAINT dispatched
    Get­Message (waits for a message)

    If the thread doing the painting manages to call Get­Message between each call to Invalidate­Rect, then it will see every invalidation. On the other hand (which is more likely), it only manages to call Get­Message after a few invalidations have taken place, it will see the accumulated invalidation in a single WM_PAINT message.

    Now that you understand how generated messages work, you can answer this question which sometimes comes in:

    If the user is continuously moving the mouse, how many WM_MOUSE­MOVE messages will I get?
  • The Old New Thing

    Programmatically controlling which handles are inherited by new processes in Win32


    In unix, file descriptors are inherited by child processes by default. This wasn't so much an active decision as it was a consequence of the fork/exec model. To exclude a file descriptor from being inherited by children, you set the FD_CLO­EXEC flag on the file descriptor.

    Win32 sort of works like that, but backwards, and maybe a little upside-down. And in high heels.

    In Win32, handles default to not inherited. Ways to make a handle inherited during Create­Process have grown during the evolution of Win32.

    As far as I can tell, back in the old days, inheritability of handles was established at handle creation time. For most handle creation functions, you do this by passing a SECURITY_ATTRIBUTES structure with bInherit­Handle set to TRUE. Functions which created handles from existing objects don't have a SECURITY_ATTRIBUTES parameter, so they instead have an explicit bInherit­Handle parameter. (For examples, see Open­Event and Duplicate­Handle.)

    But just marking a handle as inheritable isn't good enough to get it inherited. You also have to pass TRUE as the bInherit­Handles parameter to Create­Process. A handle will be inherited only if if the bInherit­Handles parameter is TRUE and the handle is marked as inheritable. Miss either of those steps, and you don't get your inheritance. (To make sure you get your inheritance IRL, be nice to your grandmother.)

    In Windows 2000, Win32 gained the ability to alter the inheritability of a handle after it is created. The Set­Handle­Information function lets you turn the HANDLE_FLAG_INHERIT flag on and off on a handle.

    But all this inheritability fiddling still had a fatal flaw: What if two threads within the same process both call Create­Process but disagree on which handles they want to be inherited? For example, suppose you have a function Create­Process­With­Shared­Memory whose job it is to launch a process, passing it a custom-made shared memory block. Suppose two threads run this function simultaneously.

    A   B
    CreateFileMapping(inheritable=TRUE) CreateFileMapping(inheritable=TRUE)
    returns handle H1 returns handle H2
    CreateProcess("A", bInheritHandles=TRUE) CreateProcess("B", bInheritHandles=TRUE)
    CloseHandle(H1) CloseHandle(H2)

    What just happened? Since inheritability is a property of the handle, processes A and B inherited both handles H1 and H2, even though what we wanted was for process A to inherit handle H1 and for process B to inherit handle H2.

    For a long time, the answer to this problem was the unsatisfactory "You'll just have to serialize your calls to Create­Process­With­Shared­Memory so that thread B won't accidentally cause a handle from thread A to be inherited by process B." Actually, the answer was even worse. You had to serialize all functions that created inheritable handles from the time the handle was created, through the call to Create­Process, and waiting until after all those inheritable handles were made no longer inheritable.

    This was a serious problem since who knows what other parts of your program are going to call Create­Process with bInherit­Handles set to TRUE? Sure you can control the calls that your own code made, but what about calls from plug-ins or other unknown components? (This is another case of kernel-colored glasses.)

    Windows Vista addresses this problem by allowing you to pass an explicit list of handles you want the bInherit­Handles parameter to apply to. (If you pass an explicit list, then you must pass TRUE for bInherit­Handles.) And as before, for a handle to be inherited, it must be also be marked as inheritable.

    Passing the list of handles you want to inherit is a multi-step affair. Let's walk through it:

    BOOL CreateProcessWithExplicitHandles(
      __in_opt     LPCTSTR lpApplicationName,
      __inout_opt  LPTSTR lpCommandLine,
      __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,
      __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,
      __in         BOOL bInheritHandles,
      __in         DWORD dwCreationFlags,
      __in_opt     LPVOID lpEnvironment,
      __in_opt     LPCTSTR lpCurrentDirectory,
      __in         LPSTARTUPINFO lpStartupInfo,
      __out        LPPROCESS_INFORMATION lpProcessInformation,
        // here is the new stuff
      __in         DWORD cHandlesToInherit,
      __in_ecount(cHandlesToInherit) HANDLE *rgHandlesToInherit)
     BOOL fSuccess;
     BOOL fInitialized = FALSE;
     SIZE_T size = 0;
     fSuccess = cHandlesToInherit < 0xFFFFFFFF / sizeof(HANDLE) &&
                lpStartupInfo->cb == sizeof(*lpStartupInfo);
     if (!fSuccess) {
     if (fSuccess) {
      fSuccess = InitializeProcThreadAttributeList(NULL, 1, 0, &size) ||
                 GetLastError() == ERROR_INSUFFICIENT_BUFFER;
     if (fSuccess) {
      lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>
                                    (HeapAlloc(GetProcessHeap(), 0, size));
      fSuccess = lpAttributeList != NULL;
     if (fSuccess) {
      fSuccess = InitializeProcThreadAttributeList(lpAttributeList,
                        1, 0, &size);
     if (fSuccess) {
      fInitialized = TRUE;
      fSuccess = UpdateProcThreadAttribute(lpAttributeList,
                        0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
                        cHandlesToInherit * sizeof(HANDLE), NULL, NULL);
     if (fSuccess) {
      ZeroMemory(&info, sizeof(info));
      info.StartupInfo = *lpStartupInfo;
      info.StartupInfo.cb = sizeof(info);
      info.lpAttributeList = lpAttributeList;
      fSuccess = CreateProcess(lpApplicationName,
                               dwCreationFlags | EXTENDED_STARTUPINFO_PRESENT,
     if (fInitialized) DeleteProcThreadAttributeList(lpAttributeList);
     if (lpAttributeList) HeapFree(GetProcessHeap(), 0, lpAttributeList);
     return fSuccess;

    After some initial sanity checks, we start doing real work.

    Initializing a PROC_THREAD_ATTRIBUTE_LIST is a two-step affair. First you call Initialize­Proc­Thread­Attribute­List with a NULL attribute list in order to determine how much memory you need to allocate for a one-entry attribute list. After allocating the memory, you call Initialize­Proc­Thread­Attribute­List a second time to do the actual initialization.

    After creating the attribute list, you set the one entry by calling Update­Proc­Thread­Attribute­List.

    And then it's off to the races. Put that attribute list in a STARTUP­INFO­EX structure, set the EXTENDED_STARTUPINFO_PRESENT flag, and hand everything off to Create­Process.

  • The Old New Thing

    Not even making it to the airtight hatchway: Execution even before you get there


    Today's dubious security vulnerability comes from somebody who reported that the Load­Keyboard­Layout function had a security vulnerability which could lead to arbitrary code execution. This is a serious issue, but reading the report made us wonder if something was missing.

    // sample program to illustrate the vulnerability.
    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    int __cdecl main(int argc, char **argv)
     LoadKeyboardLayout("whatever", system("notepad.exe"));
     return 0;

    According to the report, this sample program illustrates that the Load­Keyboard­Layout function will execute whatever you pass as its second parameter. In this case, the program chose to launch Notepad, but obviously an attacker could change the code to something more dangerous.

    We had trouble trying to figure out what the person was trying to say. After all, it's not the Load­Keyboard­Layout function that is executing the second parameter. It's the sample program that's doing it, and using the return value as the second parameter to the Load­Keyboard­Layout function. I mean, you can use this "technique" on the function

    void donothing(int i) { }

    to demonstrate that the donothing function has the same "vulnerability":


    Logically, the compiler decomposes the call to Load­Keyboard­Layout function as

     auto param2 = system("notepad.exe");
     LoadKeyboardLayout("whatever", param2);

    and now it's clear that it's not the Load­Keyboard­Layout function which is executing its second parameter; it's you.

    This is like taking a printed picture of your friend into a secured area, then saying, "See, I have a picture! Your security failed to stop me from taking a picture!" That picture was taken outside the secured area. What you have is not a security vulnerability because the picture was taken on the other side of the airtight hatchway.

    Before contacting the submitter, we want to be sure that we weren't missing something, but after looking at it from every angle, we still couldn't see what the issue was. We ran the alleged exploit under the kernel debugger and traced through the entire Load­Keyboard­Layout function (both the user-mode part and the kernel-mode part) to confirm that the function never launched Notepad on its own. We repeated the investigation on all service packs on all versions of Windows still under support (and even some that are no longer supported). Still nothing.

    Stumped, we contacted the submitter. "From what we can tell, the call to system takes place before you call the Load­Keyboard­Layout function. Can you elaborate on how this constitutes a vulnerability in the Load­Keyboard­Layout function?"

    Apparently, the submitter didn't quite understand what we were after, because the response was just more of the same. "I have discovered that the Visual Basic MsgBox function has a similar vulnerability:

    Module Program
    Sub Main()
    End Sub
    End Module

    The MsgBox method will execute whatever you pass as its parameter, as long as the result is a string. (You can even pass something that isn't a string, but it'll throw an exception after executing it.) The documentation for MsgBox clearly states that the function displays a message box with the specified text. It should therefore display a string and not execute a program!"

    At this point, we had to give up. We couldn't figure out what the person was trying to report, and our attempt to obtain a clarification was met with another version of what appeared to be the same nonsense. As I recall, this entire investigation took five days to complete, plus another day or two to complete the necessary paperwork. Each year, 200,000 vulnerability reports are received, and each one is taken seriously, even the bogus-looking ones, because there might be a real issue hiding behind a bogus-looking report. Sort of how the people in the emergency communication center have to follow through on every 911 call, even the ones that they strongly suspect are bogus, and even though dealing with the suspected-bogus ones slows down the overall response time for everyone.

    These sort-of-but-not-quite reports are among the most frustrating. There's enough sense in the report that it makes you wonder if there's a real vulnerability lurking in there, but which remains elusive because the author is unable (perhaps due to a language barrier) to articulate it clearly. They live in the shadowy ground between the reports that are clearly crackpot and the reports which are clear enough that you can evaluate them with confidence. These middle-ground reports are just plausible enough to be dangerous. As a result, you close them out with trepidation, because there's the risk that there really is something there, but you just aren't seeing it. Then you have nightmares that the finder has taken the report public, and the vulnerability report you rejected as bogus is now headline news all over the technology press. (Or worse, exploits start showing up taking advantage of the vulnerability you rejected as bogus two months ago.)

    Update: Sure, this looks like something you can reject out of hand. But maybe there's something there after all. Perhaps the system call somehow "primed the pump" and left the system in just the right state so that an uninitialized variable resulted in Notepad being launched a second time or editing its token to have higher privileges. In that case, you rejected a genuine security vulnerability, and then when hackers start using it to build a botnet, somebody will go back into the vulnerability investigation logs, and the only entry will be "Rejected without investigation by Bob."

  • The Old New Thing

    The peculiar cadence of executive mail messages


    When there's a piece of mail sent from a senior executive to the entire product team, it tends to follow a set pattern. It starts out with a history of Group X and commends them on what a great job they have been doing. Congratulating Group X is not the actual purpose of the message; it's just part of the template. Once you've seen enough of these executive mail messages, reading one that starts out congratulating some group sets your spidey-sense a-tingling.

    Eventually, the penny drops about halfway down the message: Some major reorganization is going on in Group X, usually in the form of its head leaving the group, although sometimes it's something even more substantial, like the group being absorbed into another group or even being disbanded.

    Here's an example message I just made up. The real-life messages are usually much, much longer.

    From: Important Person
    Subject: Advancing Project Nosebleed to the next generation

    The Nosebleed team has been developing orange crayons for industrial stick-figure drawings since it was formed in 2003 as a small team of just two people. Since then, its orange crayons have emerged as the leader in industrial stick-figure drawing, and under the leadership of Bob Smith, its recent release of Orange 3.1 sets a new standard for orangeness, one that will set the direction of the industry for years.

    With the release of Orange 3.1, Bob has decided to step down as head of the Nosebleed team in order to pursue his lifelong dream of circumnavigating the globe on a Big Wheel, leaving the Nosebleed team in the capable hands of Alice Jones.

    Alice has been an important part of the Nosebleed team since its inception. It was she who observed that the color orange was under-represented in the industrial stick figure crayon space and has led the technical design team in targeting that market opportunity. Alice's background in both technical design and color analysis will prove an enormous asset as the Nosebleed team moves forward into the next decade.

    Please join me in personally congratulating Bob for his years of service and welcoming Alice to her new position. If you wish to discuss this change in leadership, feel free to come by my office at any time.

    The standard cadence, as you can see, goes like this:

    • First, give the message a vague but ominous title.
    • In the message itself, describe what the team has been doing up until now. The longer and more detailed the description, the greater the suspense. It's like the opening scene of an action movie that consists of the hero at home doing something completely ordinary. The longer he just sits there reading the newspaper and eating his breakfast, the greater the suspense that something is going to explode.
    • Next, state the major upheaval. In action movie terms, this is where a car crashes through the wall without warning.
    • Describe the aftermath of the major upheaval.
    • Conclude with a round of congratulations and follow-ups.

    A disturbingly high percentage of executive messages follow this pattern, and once you learn it, you start to get more and more nervous the longer the opening section gets.

    Back in the days when people actually used Word's Autosummarize feature, one of the games you could play was to Autosummarize a document and laugh at what Word decided was the most important part. (For best effect, tell Autosummarize to try to summarize lengthy works of creative writing in one hundred words.) I played that game with one of those executive mail messages. When I fed the message into Autosummarize and asked it to rank the sentences in order of most important to least important, it decided that the least important sentence was the "Bob has decided to step down" one.

    You might say that this is more proof that Autosummarize is a joke, but my take-away was somewhat different: It led me to realize that this style of executive message is specifically designed to hide the most important information.

    Bonus: ObDilbert.

  • The Old New Thing

    Online gift ordering + enthusiastic kids at the keyboard + Unicode, wait... Unicode?


    I was completing an online gift order for my young nephew's birthday, and I was in the middle of typing Happy birthday into the gift card message when an enthusiastic child reached for the keyboard and held down the "a" key as I typed the final "a" in "birthday".

    I wanted to capture the spontaneous enthusiasm in the gift tag, but I had no idea what font or format rectangle was going to be used, so I couldn't be sure where to put hyphens so that they will ensure line breaks at visually-pleasing locations. And if I didn't insert hyphens at all, then the line would just run off the end of the gift tag and end up truncated.

    Unicode to the rescue!

    First, I fired up charmap and went to character U+00AD SOFT HYPHEN. I double-clicked the character in the grid, thereby copying it invisibly to the Characters to copy box. I then clicked the Copy button to copy the invisible soft hyphen to the clipboard. Then I switched back to my Web browser and pasted the soft hyphen into the long string of a's every six or so characters, to provide a hyphenation point.


    Happy birthd­aaaaaa­aaaaaa­aaaaaa­aaaaaa­aaaaaa­aaaaaa­aaaaaa­aaaaaa­aaaaaa­aaaaaa­y!

    When the gift reached its destination, my brother said, "Nice job on the hyphens. How did you know where to put them?"

    I then let him in on the secret. And now I'm sharing it with you.

    Anybody know whether Amazon supports the creative use of Unicode to create elaborate smiley faces?

  • The Old New Thing

    What is the API for accessing content on SkyDrive?


    The last time I mentioned programmatic access to SkyDrive was last June, where I noted that the interface was given the confusing name Messenger Connect. At least now they renamed it to Live Connect, which is slightly less confusing.

    The SkyDrive folks have been pretty busy lately. A few days ago, Dare Obasanjo announced a new Live Connect SDK, which means that you now have even more ways of accessing your SkyDrive content programmatically. (I like how the block diagram screen shot still has the red underline squiggles under the word skydrive.) Check it out at the Live Connect Developer Center.

    You can watch the //build/ presentation online if you want to watch somebody try to write code on stage. (I don't risk it myself. You may have noticed that none of my presentations involve on-stage typing!)

    Dare Obasanjo trivia: His dad was President of Nigeria, which means that when he gets email titled I am a Nigerian government official, and I need your help, he can't just delete it like the rest of us do. They might actually be for him!

  • The Old New Thing

    How can I tell whether a window is modal?


    A customer wanted a way to determine whether a particular window is modal. They listed a few methods they had tried but found that it didn't work and asked for assistance.

    As Eric Lippert is fond of saying, "First, write your spec." Until you know what you want, you won't know how to get it.

    First, you need to define what you mean by a modal window. There are multiple competing definitions.

    The customer decided that the definition of modal window they want is this one:

    A modal window is a child window that requires the user to interact with it before they can return to operating the parent application, thus preventing any work on the application main window.

    One thing you notice in this definition is that it talks both about windows and applications. You have a child window, a parent application, and even an application main window. This implies that a modal window must be in a different application from its parent. (If it were in the same application, then it vacuously does not prevent you from interacting with the parent application because it is the parent application.) But modality is a user interface concept, not a process management concept, so it's unclear why process considerations appear in the definition. End-users sitting in front of a user interface see windows, not processes. I'm going to assume that the use of the term application here is a mistake, and that all we're talking about is windows.

    The second thing you realize from this definition is that it is describing something impossible. In Windows, child windows cannot be interacted with when their parent window is disabled. This definition appears to be using a common abuse of terminology, using the words child and parent instead of the more accurate but clumsier owned and owner. This common abuse of terminology rarely causes trouble among people with experience programming the Windows user interface, but it is often a source of confusion for beginners, which is why I try to use the precise terminology rather than the casual terminology. And this question was clearly asked by a beginner.

    Another thing you notice about this definition is that it involves not two but three windows: The child window, the parent window, and the application main window. Consider the situation where you have an application main window (which is interactive), a secondary window (which is not interactive), and a tertiary window which is a child of by the secondary window with which the user must interact in order to return to operating the secondary window.

    main window
    Secondary Solid borders represent interactive windows;
    dotted borders represent non-interactive windows.
    Lines connect children (below) to parents (above).

    Is the tertiary window modal, according to this definition? I'm not sure. It is not clear to me whether the clause "thus preventing any work on the application main window" is an additional constraint or is merely elaborative. If the clause is an additional contraint, then the situation is not modal, because the application main window is still interactive. On the other hand, if the clause is merely elaborative, then the situation is modal, because the tertiary window prevents the user from interacting with the secondary window.

    The fourth thing you realize from this definition is that it requires predicting the future. How do you know that the owner window will be available for use once you dismiss the owned window? Mabe the application does "if (time(NULL) % 2) make_parent_available();". (Perhaps we can call upon the graduates of the DePaul University with a degree in predicting the future to help us here.)

    Even if the result doesn't depend on predicting the future, determining whether the window will re-enable its parent requires a level of code understanding beyond what can easily be achieved programmatically. (You would have to find the code in the other program and study it to determine whether it re-enables the parent window as part of its interaction. This can be hard to do by a human being with source code, much less by a computer program with only object code, especially if the object code is in an interpreted language, since you now have to reverse-engineer the interpreter too!)

    No wonder the problem is so difficult: The spec uses imprecise terminology, is unclear on its criteria, and requires metaphysical certitude beyond the current level of scientific understanding.

    Let's see what we can salvage from this definition. First, let's make the terminology more precise:

    A modal window is an owned window that requires the user to interact with it before they can return to operating the owner window, thus preventing any work on the application main window.

    Next, let's delete the clause whose meaning is unclear.

    A modal window is an owned window that requires the user to interact with it before they can return to operating the owner window.

    Finally, let's remove the part that requires predicting the future. Instead of describing future behavior (which is hard to predict), we'll make our requirements based on present behavior (which can be observed without the aid of a time machine).

    A modal window is an owned window whose owner window cannot be interacted with.

    The revised spec says that a modal window is an owned window whose owner is disabled. Bingo, there's your algorithm for detecting whether a window is modal. Once you have a good spec, the code pretty much writes itself:

    BOOL IsModalWindowAccordingToThisParticularSpec(HWND hwnd)
     // child windows cannot have owners
     if (GetWindowStyle(hwnd) & WS_CHILD) return FALSE;
     HWND hwndOwner = GetWindow(hwnd, GW_OWNER);
     if (hwndOwner == NULL) return FALSE; // not an owned window
     if (IsWindowEnabled(hwndOwner)) return FALSE; // owner is enabled
     return TRUE; // an owned window whose owner is disabled

    Mind you, this spec may still not be what you actually want. Consider the Notepad program. Type Ctrl+F to call up the Find dialog. This is a modeless dialog: The main window is still interactive. While the Find dialog is up, call up the About dialog from the Help menu. You now have the main Notepad window with two owned windows, an About dialog that will re-enable the main Notepad window when it is dismissed, and a Find dialog that will not.

    Notepad main window A connector is solid if the owned window re-enables the owner, dotted if it does not. (Remember, whether the line is dotted or not cannot be determined algorithmically.)
    About Find

    According to our spec, which of these windows is modal? Does that match your intuitive sense?

    Here's another case: From Notepad's Open dialog, type the name of a file that does not exist.

    Notepad main window
    File not found

    Which of these windows is a modal window?

    Still unresolved is whether this is the right definition for the customer's needs. The customer never explained why they needed to identify modal windows, and once we gave them the Is­Modal­Window­According­To­This­Particular­Spec function, they never wrote back.

    If they were trying to identify modal windows so they could try to close them, then in the File not found case above, they may try to close the Open window, when the correct window to close first is the File not found window, because you need to respect a window's disabled state.

    Since the customer never wrote back, we will never know.

  • The Old New Thing

    Sure, I'm supposed to pass WT_EXECUTELONGFUNCTION if my function takes a long time, but how long is long?


    A customer contacted me to tell a story and ask a question. The customer discovered that in their code base, all calls to Queue­User­Work­Item passed the WT_EXECUTE­LONG­FUNCTION flag, regardless of whether the function actually took a long time or not. Their program creates a large number of work items at startup, and the result of passing WT_EXECUTE­LONG­FUNCTION for all of them was that the thread pool created a new thread for each queued work item, resulting in a bloated thread pool that thrashed the CPU.

    When he asked the other people on his team why they were passing the WT_EXECUTE­LONG­FUNCTION flag unconditionally, they pointed to this article from 2005 on the importance of passing the WT_EXECUTE­LONG­FUNCTION flag to the Queue­User­Work­Item function.

    As I've mentioned before, Good advice comes with a rationale so you can tell when it becomes bad advice, but the people who applied my advice didn't understand the rationale and merely concluded, "It is important always to pass the WT_EXECUTE­LONG­FUNCTION flag!"

    The WT_EXECUTE­LONG­FUNCTION flag is two-edged. If you pass the flag when queueing a task, then the thread pool will more aggressively create a new thread when that task is running. The upside is that other tasks don't get stuck waiting for your long-running task. The downside is that this creates more threads. And if you set the flag for all of your tasks, then you don't really have a thread pool at all, since you basically told the thread pool, "Run every task on its own thread, stat!"

    But this raises the question of "How long is long?" How long does a task need to run before you declare it a long-running task?

    There is no magic number here.

    The definition of a long-running task depends on the nature of your application. Let's consider, for concreteness, a task that takes one second. If this task is not marked as a long-running task, then the thread pool will wait for it to complete rather than creating a new thread. What are the consequences for your application of the thread pool choosing to wait for one second rather than creating a new thread? If your application doesn't generate tasks at such a high rate that a one-second pause would be a significant problem, then it's not a long-running task.

    On the other hand, if your application is a service that is handling thousands of requests per second, then waiting for a one-second tasks means that a thousand tasks pile up in the meantime, and that may be enough to push your service to the brink of death because it has started falling behind on its processing and may never catch up.

    Which category does your application fall in? That's for you to determine.

Page 123 of 464 (4,639 items) «121122123124125»