May, 2011

  • 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.

  • The Old New Thing

    One engineer's interpretation of the Segway as a hybrid vehicle

    • 12 Comments

    In a discussion of commuting options (in which I was primarily an observer), the following exchange took place:

    A: I would rather get kicked in the shins every morning than bike up the massive hill that sits between my apartment and main campus.

    B: Have you tried an electric bike?

    C: If you're going to eliminate pedaling, then you may as well go all the way and get a Segway.

    B: Actually, I used to own a Segway. I was floored by the engineering achievement of creating a device that combined the speed benefits of walking with the exercise benefits of driving, and for just the cost of a used Honda!

    Note, of course, that this is just one engineer's opinion. Some people at Microsoft are quite enamored of the Segway device.

    Public Service Announcement: Today is Bike to Work Day. A number of commute stations will be set up along popular bike routes in the greater Seattle area to hand out bicycling information and snacks. And if you pass a guy creeping up the 520 hill at around 8:00am, that was probably me. Sorry I forgot to wave.

  • The Old New Thing

    BeginBufferedPaint: It's not just for buffered painting any more

    • 10 Comments

    I covered the BeginBufferedPaint function in my 2008 PDC presentation, but one thing I didn't mention is that the buffered paint functions are very handy even if you have no intention of painting.

    Since the buffered paint functions maintain a cache (provided that you remembed to call Buffered­Paint­Init), you can use Begin­Buffered­Paint to get a temporary bitmap even if you have no intention of actually painting to the screen. You might want a bitmap to do some off-screen composition, or for some other temporary purpose, in which case you can ask Begin­Buffered­Paint to give you a bitmap, use the bitmap for whatever you like, and then pass fUpdateTarget = FALSE when you call End­Buffered­Paint to say "Ha ha, just kidding."

    One thing to have to be aware of is that the bitmap provided by Begin­Buffered­Paint is not guaranteed to be exactly the size you requested; it only promises that the bitmap will be at least the size you requested. Most of the time, your code won't care (there are just pixels out there that you aren't using), but if you use the Get­Buffered­Paint­Bits function to obtain direct access to the bits, don't forget to take the stride into account.

    Consider this artifical example of a program that uses Create­DIB­Section to create a temporary 32bpp bitmap for the purpose of updating a layered window. Start with the scratch program and make these changes:

    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
     BOOL fRc = FALSE;
     HDC hdcWin = GetDC(hwnd);
     if (hdcWin) {
      HDC hdcMem = CreateCompatibleDC(hdcWin);
      if (hdcMem) {
       const int cx = 200;
       const int cy = 200;
       RECT rc = { 0, 0, cx, cy };
       BITMAPINFO bmi = { 0 };
       bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
       bmi.bmiHeader.biWidth = cx;
       bmi.bmiHeader.biHeight = cy;
       bmi.bmiHeader.biPlanes = 1;
       bmi.bmiHeader.biBitCount = 32;
       bmi.bmiHeader.biCompression = BI_RGB;
       RGBQUAD *prgbBits;
       HBITMAP hbm = CreateDIBSection(hdcWin, &bmi,
                 DIB_RGB_COLORS, &reinterpret_cast<void*&>(prgbBits),
                                                            NULL, 0);
       if (hbm) {
        HBITMAP hbmPrev = SelectBitmap(hdcMem, hbm);
    
        // Draw a simple picture
        FillRect(hdcMem, &rc,
                         reinterpret_cast<HBRUSH>(COLOR_INFOBK + 1));
        rc.left = cx / 4;
        rc.right -= rc.left;
        rc.top = cy / 4;
        rc.bottom -= rc.top;
        FillRect(hdcMem, &rc,
                       reinterpret_cast<HBRUSH>(COLOR_INFOTEXT + 1));
    
        // Apply the alpha channel (and premultiply)
        for (int y = 0; y < cy; y++) {
         for (int x = 0; x < cx; x++) {
          RGBQUAD *prgb = &prgbBits[y * cx + x];
          BYTE bAlpha = static_cast<BYTE>(cx * x / cx);
          prgb->rgbRed = static_cast<BYTE>(prgb->rgbRed * bAlpha / 255);
          prgb->rgbBlue = static_cast<BYTE>(prgb->rgbBlue * bAlpha / 255);
          prgb->rgbGreen = static_cast<BYTE>(prgb->rgbGreen * bAlpha / 255);
          prgb->rgbReserved = bAlpha;
         }
        }
    
        // update the layered window
        POINT ptZero = { 0, 0 };
        SIZE siz = { cx, cy };
        BLENDFUNCTION bf =  { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
        fRc = UpdateLayeredWindow(hwnd, NULL, &ptZero, &siz, hdcMem,
                                  &ptZero, 0, &bf, ULW_ALPHA);
        SelectBitmap(hdcMem, hbmPrev);
        DeleteObject(hbm);
       }
       DeleteDC(hdcMem);
      }
      ReleaseDC(hwnd, hdcWin);
     }
     return fRc;
    }
    

    Pretty standard stuff. But let's convert this to use the buffered paint functions to take advantage of the buffered paint bitmap cache.

    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
     BOOL fRc = FALSE;
     HDC hdcWin = GetDC(hwnd);
     if (hdcWin) {
      HDC hdcMem;
      // HDC hdcMem = CreateCompatibleDC(hdcWin);
      // if (hdcMem) {
       const int cx = 200;
       const int cy = 200;
       RECT rc = { 0, 0, cx, cy };
       // BITMAPINFO bmi = { 0 };
       // bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
       // bmi.bmiHeader.biWidth = cx;
       // bmi.bmiHeader.biHeight = cy;
       // bmi.bmiHeader.biPlanes = 1;
       // bmi.bmiHeader.biBitCount = 32;
       // bmi.bmiHeader.biCompression = BI_RGB;
       RGBQUAD *prgbBits;
       BP_PAINTPARAMS params = { sizeof(params), BPPF_NOCLIP };
       HPAINTBUFFER hbp = BeginBufferedPaint(hdcWin, &rc,
                                  BPBF_TOPDOWNDIB, &params, &hdcMem);
       if (hbp) {
        int cxRow;
        if (SUCCEEDED(GetBufferedPaintBits(hpb, &prgbBits, &cxRow))) {
       // HBITMAP hbm = CreateDIBSection(hdcWin, &bmi,
       //        DIB_RGB_COLORS, &reinterpret_cast<void*&>(prgbBits),
       //                                                   NULL, 0);
       // if (hbm) {
        // HBITMAP hbmPrev = SelectBitmap(hdcMem, hbm);
    
        // Draw a simple picture
        FillRect(hdcMem, &rc,
                         reinterpret_cast<HBRUSH>(COLOR_INFOBK + 1));
        rc.left = cx / 4;
        rc.right -= rc.left;
        rc.top = cy / 4;
        rc.bottom -= rc.top;
        FillRect(hdcMem, &rc,
                       reinterpret_cast<HBRUSH>(COLOR_INFOTEXT + 1));
    
        // Apply the alpha channel (and premultiply)
        for (int y = 0; y < cy; y++) {
         for (int x = 0; x < cx; x++) {
          RGBQUAD *prgb = &prgbBits[y * cxRow + x];
          BYTE bAlpha = static_cast<BYTE>(cx * x / cx);
          prgb->rgbRed = static_cast<BYTE>(prgb->rgbRed * bAlpha / 255);
          prgb->rgbBlue = static_cast<BYTE>(prgb->rgbBlue * bAlpha / 255);
          prgb->rgbGreen = static_cast<BYTE>(prgb->rgbGreen * bAlpha / 255);
          prgb->rgbReserved = bAlpha;
         }
        }
    
        // update the layered window
        POINT ptZero = { 0, 0 };
        SIZE siz = { cx, cy };
        BLENDFUNCTION bf =  { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
        fRc = UpdateLayeredWindow(hwnd, NULL, &ptZero, &siz, hdcMem,
                                  &ptZero, 0, &bf, ULW_ALPHA);
        // SelectBitmap(hdcMem, hbmPrev);
        // DeleteObject(hbm);
       }
       EndBufferedPaint(hpb, FALSE);
       // DeleteDC(hdcMem);
      }
      ReleaseDC(hwnd, hdcWin);
     }
     return fRc;
    }
    
    // changes to WinMain
     if (SUCCEEDED(BufferedPaintInit())) {
     // if (SUCCEEDED(CoInitialize(NULL))) {/* In case we use COM */
      hwnd = CreateWindowEx(WS_EX_LAYERED,
      // hwnd = CreateWindow(
      ...
      BufferedPaintUnInit();
      // CoUninitialize();
      ...
    

    We're using the buffered paint API not for buffered painting but just as a convenient way to get a bitmap and a DC at one shot. It saves some typing (you don't have to create the bitmap and the DC and select the bitmap in and out), and when you return the paint buffer to the cache, some other window that calls Begin­Buffered­Paint may be able to re-use that bitmap.

    There are a few tricky parts here. First, if you're going to be accessing the bits directly, you need to call Get­Buffered­Paint­Bits and use the cxRow to determine the bitmap stride. Next, when we're done, we pass FALSE to End­Buffered­Paint to say, "Yeah, um, thanks for the bitmap, but don't Bit­Blt the results back into the DC we passed to Begin­Buffered­Paint. Sorry for the confusion."

    A less obvious trick is that we used BPPF_NOCLIP to get a full bitmap. By default, Begin­Buffered­Paint returns you a bitmap which is clipped to the DC you pass as the first parameter. This is an optimization to avoid allocating memory for pixels that can't be seen anyway when End­Buffered­Paint goes to copy the bits back to the original DC. We don't want this optimization, however, since we have no intention of blitting the results back to the original DC. The clip region of the original DC is irrelevant to us because we just want a temporary bitmap for some internal calculations.

    Anyway, there you have it, an example of using Begin­Buffered­Paint to obtain a temporary bitmap. It doesn't win much in this example (since we call it only once, at window creation time), but if you have code which creates a lot of DIB sections for temporary use, you can use this trick to take advantage of the buffered paint cache and reduce the overhead of bitmap creation and deletion.

    Pre-emptive snarky comment: "How dare you show us an alternative method that isn't available on Windows 2000!"

  • The Old New Thing

    Why is my program terminating with exit code 3?

    • 20 Comments

    There is no standard for process exit codes. You can pass anything you want to Exit­Process, and that's what Get­Exit­Code­Process will give back. The kernel does no interpretation of the value. If youw want code 42 to mean "Something infinitely improbable has occurred" then more power to you.

    There is a convention, however, that an exit code of zero means success (though what constitutes "success" is left to the discretion of the author of the program) and a nonzero exit code means failure (again, with details left to the discretion of the programmer). Often, higher values for the exit code indicate more severe types of failure. The command processor ERROR­LEVEL keyword was designed with these convention in mind.

    There are cases where your process will get in such a bad state that a component will take it upon itself to terminate the process. For example, if a process cannot locate the DLLs it imports from, or one of those DLLs fails to initialize, the loader will terminate the process and use the status code as the process exit code. I believe that when a program crashes due to an unhandled exception, the exception code is used as the exit code.

    A customer was seeing their program crash with an exit code of 3 and couldn't figure out where it was coming from. They never use that exit code in their program. Eventually, the source of the magic number 3 was identified: The C runtime abort function terminates the process with exit code 3.

Page 1 of 3 (26 items) 123