• The Old New Thing

    Swamping the thread pool with work faster than it can drain

    • 9 Comments

    This scenarios is real, but details have been changed to protect the guilty.

    Consider a window showing the top of a multi-page document. The developers found that when the user clicks the down-arrow button on the scroll bar, the program locks up for 45 seconds, over a dozen threads are created, and then suddenly everything clears up and the window displays the final paragraph of the document (i.e., it scrolled all the way to the bottom).

    The problem was traced to queueing tasks to the thread pool faster than they can drain.

    The document is an object which, unlike a window, has no thread affinity. (Naturally, there are critical sections around the various document methods so you don't have corruption if two threads try to modify the document at the same time.) The way to display a different portion of the document is to call a method which changes the viewport location.

    When the user clicks the down-arrow button on the scroll bar, the main thread creates a periodic timer at four fifths of the double-click speed, and each time the timer elapses, it does the equivalent of document.ScrollDown(). The code cancels the timer once the user releases the mouse button.

    The catch is that the document was so complicated that it takes a long time to change the view top and redraw the new view. (I don't remember exactly how long, but let's say it was 700ms. The important thing is that it takes longer than 400ms.)

    Given that set-up, you can see what happens when the user clicks the scroll down-arrow. The initial scroll is initiated, and before it can complete, another scroll is queued to the thread pool. The document view keeps trying to update its position, but the periodic timer generates scroll requests faster than the document view can keep up.

    If that description was too terse, here's a longer version.

    The code for scrolling went something like this:

    OnBeginScrollDown()
    {
     // Start a timer to do the scrolling
     CreateTimerQueueTimer(&htimer, NULL, ScrollAgain, NULL,
         0, GetDoubleClickTime() * 4 / 5, WT_EXECUTEDEFAULT);
    }
    
    OnEndScrollDown()
    {
     if (htimer != NULL) {
      DeleteTimerQueueTimer(NULL, htimer, INVALID_HANDLE_VALUE);
      htimer = NULL;
     }
    }
    
    ScrollAgain(...)
    {
     document.ScrollDown();
    }
    

    (In reality, the program didn't use the CreateTimerQueueTimer function—it had a custom timer queue and a custom thread pool—but the effect is the same.)

    At time T = 0, the user clicks on the scroll bar down-arrow. The UI thread starts the timer with an initial delay of zero and a period of 400ms. The timer fires immediately, and a thread pool thread is asked to run ScrollAgain. The ScrollAgain function calls ScrollDown, which begins the process of scrolling the document.

    At time T = 400ms, the periodic timer fires, and a new thread pool thread is created to service it. Pool thread 2 calls ScrollDown() and blocks.

    At time T = 700ms, the ScrollDown call on pool thread 1 completes, and now pool thread 2 can begin its call to ScrollDown().

    At time T = 800ms, the periodic timer fires again, and pool thread 1 (now idle) is asked to handle it. Pool thread 1 calls ScrollDown() and blocks.

    At time T = 1200ms, the periodic timer fires yet again. This time, there are no idle threads in the thread pool, so the thread pool manager creates yet another thread to service the timer. Pool thread 3 calls ScrollDown() and blocks.

    At time T = 1400ms, the ScrollDown() call issued by pool thread 2 completes. Pool thread 2 now returns to idle. Now the call to ScrollDown() from pool thread 1 (issued at time T = 800ms) can start.

    At time T = 1600ms, the periodic timer fires again, and pool thread 2 is chosen to service it. Pool thread 2 calls ScrollDown() and blocks.

    At time T = 2000ms, the periodic timer fires again, and a new pool thread is created to service it. Pool thread 4 calls ScrollDown() and blocks.

    You can see where this is going, I hope. Work is being generated by the periodic timer at a rate of one work item per 400ms, but it takes 700ms to carry out each work item, and the tasks are serialized on the document. It's like Lucy in the chocolate factory. The document is frantically trying to carry out all the work, and it never manages to catch up. Eventually, the document scrolls all the way to the bottom, and the mass of pent-up calls to ScrollDown() all return immediately since there is no more scrolling possible.

    Now that the document is idle, it can paint, and that's where the user finally sees the document, scrolled all the way to the bottom.

    There are a number of possible solutions here.

    One way is not to queue up another scroll while an old one is still running. Instead, just wait for it to finish, and then issue a new scroll that accumulates all the scrolling that had taken place while you were waiting for the first to complete. This results in jerky scrolling, however, and it creates a lag of up to 700ms between the user releasing the mouse button and scrolling actually stopping.

    Another approach is to disable repainting the entire document when you detect that you are in the document is too complex to scroll quickly case and just scroll the scrollbar thumb. When the user stops scrolling, re-enable painting and boom the document appears at the user's chosen location. This preserves responsiveness, but you lose the ability to see the document as you scroll it.

    I don't know what solution the customer finally went with. I was just there to help with the debugging.

    Bonus example: Larry Osterman describes another situation with the same underlying cause.

    Hidden take-away: Observe that both of these examples illustrate one of the subtle consequences of a design which moves all processing off the UI thread.

    Update: Note that Set­Timer wouldn't have helped here.

    case WM_TIMER:
      if (wParam == SCROLLTIMER) {
        QueueUserWorkItem(ScrollAgain, NULL, WT_EXECUTEDEFAULT);
      }
      ...
    

    Since the processing has been moved off the UI thread, the WM_TIMER messages are free to keep flowing in and queue up work faster than the background thread can keep up.

  • The Old New Thing

    How do I control X-Mouse settings (active window tracking)?

    • 38 Comments

    For quite some time, Windows has had a setting officially called active window tracking but which informally goes by the name X-Mouse, because that was the name of the PowerToy which first exposed the feature. (The PowerToy was in turn so-named because it made the mouse behave in a manner similar to many X window managers.) The setting is exposed to end users in Windows 7 on Make the mouse easier to use under Activate a window by hovering over it with the mouse.

    If you want to write your own PowerToy to control this setting, you can do so by calling the SystemParametersInfo function. There are three settings which collectively control the X-Mouse feature:

    1. SPI_SETACTIVEWINDOWTRACKING: This is the master switch which enables the behavior.
    2. SPI_SETACTIVEWNDTRKZORDER: This controls whether the window is brought to the top when it is activated by active window tracking.
    3. SPI_SETACTIVEWNDTRKTIMEOUT: This controls how long the mouse has to be in a window before it is auto-activated.

    Note that X-Mouse is a user preference. Applications should not change the setting without the user's permission.

  • The Old New Thing

    How do I prevent users from pinning my program to the taskbar?

    • 30 Comments

    A customer wanted to prevent users from pinning their application to the taskbar.

    I have an application that is launched as a helper by a main application. Users shouldn't be launching it directly, but rather should be launching the main application. But since the helper shows up in the taskbar, users may be tempted to right-click on the taskbar icon and select "Pin to taskbar." Unfortunately, this pins the helper program to the taskbar instead of the main application, and launching the helper program directly doesn't work. Is there a way I can prevent users from pinning the helper program?

    It so happens that there are a number of ways of marking your helper program as Don't pin me. Given the description above, the most direct way is probably to set the System.App­User­Model.Prevent­Pinning property on the window created by the helper program.

    Take our scratch program and make the following changes:

    #include <shellapi.h>
    #include <propsys.h>
    #include <propkey.h>
    
    HRESULT MarkWindowAsUnpinnable(HWND hwnd)
    {
     IPropertyStore *pps;
     HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
     if (SUCCEEDED(hr)) {
      PROPVARIANT var;
      var.vt = VT_BOOL;
      var.boolVal = VARIANT_TRUE;
      hr = pps->SetValue(PKEY_AppUserModel_PreventPinning, var);
      pps->Release();
     }
     return hr;
    }
    
    
    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
     MarkWindowAsUnpinnable(hwnd);
     return TRUE;
    }
    

    I set the PROP­VARIANT manually instead of using Init­Prop­Variant­From­Boolean just to emphasize that the boolVal must be VARIANT_TRUE and not TRUE. In real life, I probably would have used Init­Prop­Variant­From­Boolean.

    Run this program and observe that "Pin this program to taskbar" does not appear on the menu when you right-click on the taskbar button.

    Even better would be to permit pinning, but set the System.App­User­Model.Relaunch­Command, .Relaunch­Display­Name­Resource and optionally .Relaunch­Icon­Resource properties so that if the user tries to pin the helper, it actually pins the main application.

    Start with a new scratch program and make these changes:

    #include <shellapi.h>
    #include <propsys.h>
    #include <propkey.h>
    #include <propvarutil.h>
    
    HRESULT IPropertyStore_SetValue(IPropertyStore *pps,
        REFPROPERTYKEY pkey, PCWSTR pszValue)
    {
     PROPVARIANT var;
     HRESULT hr = InitPropVariantFromString(pszValue, &var);
     if (SUCCEEDED(hr))
     {
      hr = pps->SetValue(pkey, var);
      PropVariantClear(&var);
     }
     return hr;
    }
    
    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
     IPropertyStore *pps;
     HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
     if (SUCCEEDED(hr)) {
      IPropertyStore_SetValue(pps,
        PKEY_AppUserModel_ID, L"Contoso.Scratch");
      IPropertyStore_SetValue(pps,
        PKEY_AppUserModel_RelaunchCommand,
        L"notepad.exe %windir%\\system.ini");
      IPropertyStore_SetValue(pps,
        PKEY_AppUserModel_RelaunchDisplayNameResource,
        L"C:\\full\\path\\to\\scratch.exe,-1");
      // optionally also set PKEY_AppUserModel_RelaunchIconResource
      pps->Release();
     }
     return TRUE;
    }
    
    // resource file
    STRINGTABLE BEGIN
     1 "Open system.ini"
    END
    
    

    I'm pulling a fast one here and pretending that Notepad is my main application. Obviously you'd use your actual main application. (I'm also hard-coding the path to my scratch program.)

    When you run this program, right-click on the taskbar button. Observe that the option to run a new copy of the program is called Open system.ini and if you pick it (or use the middle-mouse-button shortcut), Notepad runs. If you pin the program, the pinned icon runs Notepad.

    Even if you don't need to redirect the pinned item to another program, you can use this second technique to pass a custom command line for the pinned icon.

  • The Old New Thing

    My evil essence revealed

    • 21 Comments

    I found it amusing that somebody considered the fact that Microsoft employees can read my queued-up blog entries before the articles are published to be further evidence of Microsoft's evil essence as a monopoly.

    Just for the record, this is not evidence of Microsoft's evil essence as a monopoly. Rather, it's evidence of Raymond's evil essence as a monopoly, because the monopoly on blog articles written by Raymond Chen that haven't yet been published belongs to me.

  • The Old New Thing

    Just for fun: Sample user names in Windows 7

    • 45 Comments

    There are a few places in Windows where you are asked to enter your name in order to set up an account. Just for fun, I went through all the localized versions I could find and extracted the sample names. Some locales did not get around to translating all the strings. If the string was left untranslated (which can happen for LIPs), then I left the box blank. (Locales which did not translate either string have been omitted from the table.)

    My reactions after the table.

    ID English name for example, John For example: John Smith
    af-za Afrikaans byvoorbeeld, John Voorbeeld: Jan Smit
    am-et Amharic ለምሳሌ: አበበ ከበደ
    ar-sa Arabic على سبيل المثال، أمجد على سبيل المثال: أشرف ماهر
    as-in Assamese উদাহৰণৰ কাৰণে, প্ৰবিন উদাহৰণ স্বৰূপে: জন স্মিথ
    az-latn-az Azerbaijani məsələn, Fərhad Məsələn: Vüsal Tahirov
    bg-bg Bulgarian например Kiril Например: Пламен Христов
    bn-bd Bengali (Bangladesh) উদাহরণস্বরূপ, জন উদাহরণস্বরূপ: জন স্মিথ
    bn-in Bengali (India) উদাহরণস্বরূপ, রাম উদাহরণস্বরূপ: জন স্মিথ
    bs-cyrl-ba Bosnian (Cyrillic) напримјер John Напримјер: Алмир Алмировић
    bs-latn-ba Bosnian (Latin) naprimjer John Naprimjer: Almir Almirović
    ca-es Catalan per exemple, Jordi Per exemple: Pau Solà
    cs-cz Czech například Tereza Příklad: Jan Novák
    cy-gb Welsh Siôn, er enghraifft Er enghraifft: Siân Jones
    da-dk Danish f.eks. Claus Eksempel: Jens Jensen
    de-de German N/A Beispiel: Jens Mander
    el-gr Greek για παράδειγμα, Γιάννης Για παράδειγμα: Γεώργιος Βασιλείου
    en-us English for example, John For example: John Smith
    es-es Spanish por ejemplo, Juan Por ejemplo: Jorge López
    et-ee Estonian näiteks Jaan Näiteks: Mati Kask
    eu-es Basque esaterako, Rafa Adibidez: Ane Lizarralde
    fa-ir Persian برای مثال، John
    fi-fi Finnish esimerkiksi Juha Esimerkki: Henri Rautiainen
    fil-ph Filipino halimbawa, Juan Halimbawa: Juan dela Cruz
    fr-fr French par exemple Rosalie Par exemple : Marie Dubois
    ga-ie Irish mar shampla, Seán Mar shampla: Seán Ó Murchú
    gl-es Galician por exemplo: Xiana Por exemplo: Duarte Vidal
    gu-in Gujarati ઉદાહરણ તરીકે, કમલેશ ઉદાહરણ તરીકે: કમલેશ દવે
    ha-latn-ng Hausa A misali:John Smith
    he-il Hebrew לדוגמה, John לדוגמה: משה כהן
    hi-in Hindi उदाहरण के लिए, अमित उदाहरण के लिए: जनमेजय सिंह सिकरवार
    hr-hr Croatian na primjer, Zdenko Primjerice: Ivan Kovač
    hu-hu Hungarian például Lilian Például: Tót Béla
    hy-am Armenian օրինակ՝ Արամ Օրինակ. Արմեն Արմենյան
    id-id Indonesian misalnya, John Misalnya: John Smith
    ig-ng Igbo iji maatụ, Chukwubike-ụgbaja Ọmụmatụ̀: Ụgbaja Chukwubuike Greg
    is-is Icelandic til dæmis Jón Dæmi: Jón Jónsson
    it-it Italian ad esempio, Luca Ad esempio: Valeria Dal Monte
    iu-latn-ca Inuktitut Suurlu: John Smith
    ja-jp Japanese 例: John 例: Taro Chofu
    ka-ge Georgian მაგ. Nino მაგალითად: დიმიტრი გოგელია
    kk-kz Kazakh мысалы, Джон Мысалы: Аманбайқызы Айнұр
    km-kh Khmer ឧទាហរណ៍ John ឧទាហរណ៍: John Smith
    kn-in Kannada ಉದಾಹರಣೆಗೆ, ಜಾನ್ ಉದಾಹರಣೆಗೆ: ಜಾನ್ ಸ್ಮಿತ್
    ko-kr Korean 예: John 예: 홍길동
    kok-in Konkani देखीक, जॉन देखीक: जॉन स्मिथ
    ky-kg Kirghiz Мисалы: Тилек Чубаков
    lb-lu Luxembourgish z. Bsp., John Zum Beispill: Marc Majerus
    lt-lt Lithuanian pvz., Jonas Pvz., Jonas Jonaitis
    lv-lv Latvian piemēram, Jānis Piemēram: Jānis Zariņš
    mi-nz Māori Hei tauira: Hone Mete
    mk-mk Macedonian на пример, Зоран На пример: Бранко Стојановски
    ml-in Malayalam ഉദാ: ജോണ് ഉദാഹരണമായി: John Smith
    mr-in Marathi उदाहरणासाठी, जॉन उदाहरणार्थ: जॉन स्मिथ
    ms-bn Malay (Brunei) sebagai contoh, John Sebagai contoh: John Smith
    ms-my Malay (Malaysia) sebagai contoh, John Sebagai contoh: John Smith
    mt-mt Maltese pereżempju, John Eżempju: John Pace
    nb-no Norwegian (Bokmål) for eksempel Kim Eksempel: Jens Jensen
    ne-np Nepali उदाहरणार्थ, जोन उदाहरणको निमित्त: राम बहादुर
    nl-nl Dutch bijvoorbeeld: Emma Bijvoorbeeld: Jan Smit
    nn-no Norwegian (Nynorsk) for eksempel Kim Eksempel: Jens Jensen
    nso-za Northern Sotho go fa mohlala, John Mohlala: John Smith
    or-in Oriya ଉଦାହରଣ ସ୍ଵରୂପ, ଜୋନ୍ ଉଦାହରଣ ସ୍ଵରୂପ: ଦିପ୍ତି ରଞ୍ଜନ
    pa-in Panjabi ਉਦਾਹਰਣ ਦੇ ਲਈ, ਜੌਨ ਉਦਾਹਰਣ ਦੇ ਲਈ: John Smith
    pl-pl Polish na przykład: Tomek Na przykład: Jan Kowalski
    pt-br Portuguese (Brazil) por exemplo, Marcio Por exemplo: João Silva
    pt-pt Portuguese (Portugal) por exemplo: Rui Por exemplo: Jorge Santos
    qps-ploc Pseudo ƒŏг єжåмþľę, Јσĥň [sqymS][₣óя εхдmþĺĕ: Јǿћη Šмīťђ !!! !!]
    qps-mirr Pseudo (Mirrored) [For example: John Smith]
    quz-pe Quechua qatina, Juan Kay hina: Jorge Lopez
    ro-ro Romanian Ion, de exemplu De exemplu: Ion Popescu
    ru-ru Russian например, Андрей Например: Иван Петров
    si-lk Sinhala නිදසුන් ලෙස, නිමල් නිදසුනක් ලෙස: Don Lasith
    sk-sk Slovak napríklad Ján Príklad: Peter Kováč
    sl-si Slovene na primer Janez Na primer: Janez Kranjski
    sq-al Albanian për shembull, Vehbi Për shembull: Vehbi Neziri
    sr-cyrl-cs Serbian (Cyrillic) на пример, Јован На пример: Петар Петровић
    sr-latn-cs Serbian (Latin) na primer, Jovan Na primer: Petar Petrović
    sv-se Swedish t.ex. Rebecca Exempel: John Smith
    sw-ke Swahili kwa mfano, Yohana Kwa mfano: Mussa Joseph
    ta-in Tamil எடுத்துக்காட்டாக, ஜான் உதாரணத்திற்கு: குமார்
    te-in Telugu ఉదాహరణకు, వేణు ఉదాహరణకు: రామ్ లక్ష్మణ్
    th-th Thai ตัวอย่างเช่น John ตัวอย่างเช่น: John Smith
    tn-za Tswana sekai, Tidimalo Sekao jaaka: P‌ule Molefe
    tr-tr Turkish örneğin, Can Örneğin: Kemal Etikan
    tt-ru Tatar мәсәлән, Фәрит Мәсәлән: Гали Вәлиев
    uk-ua Ukrainian наприклад, Тарас Наприклад: Тарас Руденко
    ur-pk Urdu مثال کے طور پر, امجد مثال کے طور پر: صفدر رشيد
    uz-latn-uz Uzbek masalan, Akmal Masalan: Adham Soliyev
    vi-vn Vietnamese ví dụ, John Ví dụ: John Smith
    yo-ng Yoruba bí àpẹẹrẹ, Jòhánù Bí àpẹẹrẹ: John Smith
    zh-cn Chinese (PRC) 例如: John 例如: 李建国
    zh-hk Chinese (Hong Kong) 例如,John
    zh-tw Chinese (Taiwan) 例如,John 範例: 祝英台
    zu-za Zulu isibonelo, John Isibonelo: John Smith

    Some observations:

    • Many languages translated the words "for example" but left the name as John or John Smith. I'm looking at you, Sweden. "John Smith"? Really? You couldn't have changed it to Sven Svensson?
    • Some languages chose generic names (like Jan Novák), keeping to the spirit of the English sample name. Others chose to substitute a real name (like Marie Dubois). [Update: See correction from Hardt.]
    • German doesn't provide a sample first name. My guess is that they ran out of room! The string Geben Sie einen Benutzernamen ein: probably took up all the space in the dialog, leaving no room for an example. [Update: See explanation of name Jens Mander from Roland.]
    • This information (and plenty of other translation goodness) is publically available for non-commercial use.

    Related: The Locales of Windows 7, all divvied up.

  • The Old New Thing

    Why are custom properties created on Windows 2000 lost when I view the file from newer versions of Windows?

    • 25 Comments

    In Windows 2000, Explorer let you add properties like Summary and Author to nearly any file type. But when you view the files from a machine running Windows XP or later, those properties are lost. Where did they go?

    Most file types do not have extensibility points for adding metadata. For example, every byte of a plain text files is devoted to text data; there is nowhere to put metadata like Author or Summary. In Windows 2000, the shell chose to store this extra information in NTFS alternate data streams (or more accurately, the shell chose to use the STGFMT_FILE storage format, which is implemented in terms of NTFS alternate data streams.) Storing the information in alternate data streams attaches the data to the file without affecting the file contents.

    This was a clever idea, taking advantage of NTFS's ability to attach arbitrary data to a file, but it also had a serious problem: Alternate streams are not preserved by simple and common operations like sending the file by email, copying the file to a (FAT-formatted) USB thumb drive, uploading or downloading the file from a Web site, or burning the file to a CD. Basically, once the file leaves the comfortable confines of your local hard drive, there's a good chance that the metadata will be destroyed.

    To avoid this problem, Windows XP switched to storing the metadata in the file contents itself. Doing this, however, requires support from the file format. Each file type can have registered for it a property handler which describes how to read and write properties for a file. (Windows itself comes with a few such handlers, such as for JPEG images and MP3 files, with more recent versions of Windows supporting more properties.) If no such property handler is registered, the shell will use structured storage, provided the file format is compatible with structured storage.

    The data you added in Windows 2000 are still there. It's just that newer versions of Windows don't bother looking for them. (If you were sufficiently resourceful, you could write a program which opens the file in STGFMT_FILE mode, reads the properties, then reopens the file via the shell namespace and writes the properties back out.)

    For lots of programming goodness about the shell property system, check out Ben Karas's blog, which I have been liberally linking to.

  • The Old New Thing

    How do IsThemeActive, IsAppThemed, and IsCompositionActive differ?

    • 16 Comments

    There are three functions which test very similar things, and sometimes applications pick the wrong one. Here's the rundown:

    • IsThemeActive checks whether visual styles are enabled for the user. This is not an application-specific setting; it tells you whether visual styles are enabled in general. Note that this does not tell you whether the current application is using visual styles.
    • IsAppThemed checks whether visual styles are enabled for the current application. Windows may disable visual styles for a specific application (even though they are enabled in general) for compatiblity reasons.
    • IsCompositionActive checks whether desktop composition is enabled for the current application. As with visual styles, Windows may disable desktop composition for a specific application (even though it is enabled in general) for compatiblity reasons.
    Note that these functions do not answer the question "Is the application using the visual-styles-enabled version of the common controls library?" That question is harder to answer because the decision to use the visual-styles-enabled version of the common controls library is not a process-wide one but is rather made on a window-by-window basis. You can have an application where half of the button controls are the old non-visual-styles version and half of the button controls participate in visual styles. (You may have seen this in action in Explorer, where the OK button on the Run dialog participates in visual styles, yet a button in a shell extension does not.)

    How can you tell whether a particular button is an old-school button or a fancy new button? I don't know either.

    Bonus emphasis: From the comments, it appears that people have confused "a window was created with the visual-styles-enabled version of the common controls library" with "themes are enabled". The two are independent concepts. All four combinations are possible. I thought I called this out in the article, but apparently I didn't call it out clearly enough.

  • The Old New Thing

    WinMain is just the conventional name for the Win32 process entry point

    • 35 Comments

    WinMain is the conventional name for the user-provided entry point in a Win32 program. Just like in 16-bit Windows, where the complicated entry point requirements were converted by language-provided startup code into a call to the the user's WinMain function, the language startup code for 32-bit programs also does the work of converting the raw entry point into something that calls WinMain (or wWinMain or main or _wmain).

    The raw entry point for 32-bit Windows applications has a much simpler interface than the crazy 16-bit entry point:

    DWORD CALLBACK RawEntryPoint(void);
    

    The operating system calls the function with no parameters, and the return value (if the function ever returns) is passed to the ExitThread function. In other words, the operating system calls your entry point like this:

    ...
      ExitThread(RawEntryPoint());
      /*NOTREACHED*/
    

    Where do the parameters to WinMain come from, if they aren't passed to the raw entry point?

    The language startup code gets them by asking the operating system. The instance handle for the executable comes from GetModuleHandle(NULL), the command line comes from GetCommandLine, and the nCmdShow comes from GetStartupInfo. (As we saw before, the hPrevInstance is always NULL.)

    If you want to be hard-core, you can program to the raw entry point. Mind you, other parts of your program may rely upon the work that the language startup code did before calling your WinMain. For example, the C++ language startup code will run global constructors before calling into WinMain, and both C and C++ will initialze the so-called security cookie used as part of stack buffer overrun detection. Bypass the language startup code at your peril.

    Bonus chatter: Notice that if you choose to return from your entry point function, the operating system passes the return value to ExitThread and not ExitProcess. For this reason, you typically don't want to return from your raw entry point but instead want to call ExitProcess directly. Otherwise, if there are background threads hanging around, they will prevent your process from exiting.

  • The Old New Thing

    Microspeak: PowerPoint Karaoke and the eye chart

    • 27 Comments

    The game PowerPoint-Karaoke was invented in 2006 by Zentrale Ingelligenz Agentur. In this game, contestants are called upon to give a PowerPoint presentation based on a slide deck they have never seen. (The German spelling uses a hyphen between the two words. When "translated" into English, the hyphen is often omitted.)

    At Microsoft, the term has been extended to refer to giving a presentation from slides prepared by somebody else, usually on short notice and therefore with little preparation.

    Bob is out sick today, so I'll be giving the overview. Sorry for the PowerPoint Karaoke.

    This is shorthand for "Sorry if this presentation is a bit clumsy, but I'm stepping in on short notice, and I'm not completely familiar with this slide deck."

    In the context of PowerPoint presentations, an eye chart is a slide so dense with text that reading it is a test of visual acuity. The term is usually used as part of an apology for having created such a horrible slide in the first place.

    More generally, the term eye chart refers to any presentation of data in a ridiculously small font. For example, over in the sales/marketing part of Microsoft, there are spreadsheets with titles like FY05 Sales Forecast Eye Chart. Here's what one of them might look like:

    Region Jul 2005   Aug 2005   Sep 2005   Q1   Oct 2005   Nov 2005   Dec 2005   Q2 ...
    FY05 Fcst FY04 Y-Y FY05 Fcst FY04 Y-Y FY05 Fcst FY04 Y-Y FY05 Fcst FY04 Y-Y FY05 Fcst FY04 Y-Y FY05 Fcst FY04 Y-Y FY05 Fcst FY04 Y-Y FY05 Fcst FY04 Y-Y
    Northeast Widgets 57 57 0% 74 61 22% 85 92 −8% 216 209 3% 71 70 2% 53 59 −11% 90 89 1% 215 218 −2%
    Doodads 41 52 −22% 79 81 −3% 100 85 18% 219 218 1% 49 52 −7% 93 84 12% 87 79 10% 229 214 7%
    Gizmos 64 55 16% 95 88 7% 56 67 −16% 215 211 2% 59 79 −25% 58 69 −16% 68 83 −18% 186 231 −20%
    Northeast Total 159 157 2% 138 168 −18% 170 212 −20% 467 537 −13% 205 224 −8% 209 177 18% 194 244 −21% 608 645 −6%
    Southeast Widgets 80 70 15% 75 82 -8% 57 60 -5% 212 212 0% 63 83 -24% 49 53 -8% 92 75 23% 204 211 -3%
    Doodads 59 69 -14% 60 73 -17% 67 68 -1% 187 210 -11% 54 70 -23% 98 86 14% 58 50 16% 210 206 2%
    Gizmos 75 89 -15% 59 61 -3% 124 100 24% 259 250 3% 80 89 -10% 84 96 -13% 40 52 -23% 204 237 -14%
    Southeast Total 204 239 -15% 220 189 17% 151 187 -19% 576 615 -6% 172 175 -2% 168 210 -20% 122 152 -20% 462 538 -14%
    ... etc for about a bajillion more rows ...

    (The abbreviation Y-Y is being used correctly for once. Writing the program to generate all this fake data took far, far longer than writing the rest of this posting! It got a lot easier once I realized that, since this is just fake data, the totals don't have to add up.)

    Giving the spreadsheet the title Eye Chart lets people know that this is the spreadsheet crammed with data to the point of information overload. If that's what you're looking for.

    I wouldn't be surprised if these uses of the terms PowerPoint Karaoke and eye chart are also popular at other companies.

    Bonus chatter: Last year, I was asked to give a repeat of a presentation I hadn't given in several months. I had only a little bit of time to prepare, and there were times where I lost my place and had to refer to my notes (which I thankfully remembered to keep). It's embarrassing to find yourself playing PowerPoint Karaoke to your own slide deck.

  • The Old New Thing

    If it's possible to do something, then it's possible to do something WRONG

    • 22 Comments

    Once you make it possible to do something, you have to accept that you also made it possible to do something wrong.

    When the window manager was originally designed, it made it possible for programs to override many standard behaviors. They could handle the WM_NC­HIT­TEST message so a window can be dragged by grabbing any part of the window, not just the caption bar. They could handle the WM_NC­PAINT message to draw custom title bars. The theory was that making all of these things possible permitted smart people to do clever things.

    The downside is that it also permits stupid people to do dumb things.

    Changing the window procedure model from call Def­Window­Proc to get default behavior to return whether you handled the message wouldn't have helped. First of all, the handled/not-handled model is too restrictive: It requires you to do everything (handled) or nothing (not handled). There is no option to do a little bit. (Imagine if C++ didn't let you call the base class implementation of an overridden method.)

    Doing a little bit is a very common pattern. The WM_NC­HITTEST technique mentioned above, for example, uses the default hit-testing implementation, and then tweaks the result slightly:

    case WM_NCHITTEST:
     // call base class first
     lres = DefWindowProc(hwnd, uMsg, wParam, lParam);
     // tweak the result
     if (lres == HTCLIENT) lres = HTCAPTION;
     return lres;
    

    How would you do this with the handled/not-handled model?

    case WM_NCHITTEST:
     if (not handling this message would have resulted in HTCLIENT) {
      lres = HTCAPTION;
      handled = TRUE;
     } else {
      handled = FALSE;
     }
     break;
    

    The trick about that bit in parentheses is that it requires the research department to finish the final details on that time machine they've been working on. It's basically saying, "Return not handled, then follow the message until handling is complete and if the final result is HTCLIENT, then fire up the time machine and rewind to this point so I can change my mind and return handled instead."

    And even if the research department comes through with that time machine, the handled/not-handled model doesn't even solve the original problem!

    The original problem was people failing to call Def­Window­Proc when they decided that they didn't want to handle a message. In the handled/not-handled model, the equivalent problem would be people returning handled = TRUE unconditionally.

    BOOL NewStyleWindowProc(HWND hwnd, UINT uMsg,
     WPARAM wParam, LPARAM lParam, LRESULT& lres)
    {
     BOOL handled = TRUE;
     switch (uMsg) {
     case WM_THIS: ...; break;
     case WM_THAT: ...; break;
     // no "default: handled = FALSE; break;"
     }
     return handled;
    }
    

    (Side note: The dialog manager uses the handled/not-handled model, and some people would prefer that it use the Def­Xxx­Proc model, so you might say "We tried that, and some people didn't like it.")

    This topic raises another one of those "No matter what you do, somebody will call you an idiot" dilemmas. On the one side, there's the Windows should perform extra testing at runtime to detect bad applications school, and on the other side, there's the Windows should get rid of all the code whose sole purpose in life is to detect bad applications school.

Page 119 of 444 (4,433 items) «117118119120121»