September, 2005

  • The Old New Thing

    Fiddling with the fonts, part 2: Keeping the English font small

    • 14 Comments

    We concluded last time that we wanted the custom large font to apply only to the columns containing Chinese characters and leave the original font in place for the English columns. We do this by carrying two fonts around, choosing the appropriate one for each column.

    class RootWindow : public Window
    {
     ...
    private:
     HWND m_hwndLV;
     HWND m_hwndEdit;
     HWND m_hwndLastFocus;
     HFONT m_hfChinese;
     HFONT m_hfNormal;
     int  m_cyEdit;
     ...
    }
    
    RootWindow::RootWindow()
     : m_hfChinese(NULL)
     , m_hfNormal(NULL)
    {
    }
    
    RootWindow::~RootWindow()
    {
     if (m_hfChinese) DeleteObject(m_hfChinese);
     if (m_hfNormal) DeleteObject(m_hfNormal);
    }
    
    LRESULT RootWindow::OnCreate()
    {
     ...
     ListView_SetExtendedListViewStyleEx(m_hwndLV,
                                         LVS_EX_FULLROWSELECT,
                                         LVS_EX_FULLROWSELECT);
    
     LOGFONT lf;
     if (!GetObject(GetWindowFont(m_hwndLV), sizeof(lf), &lf)) {
      return -1;
     }
     m_hfNormal = CreateFontIndirect(&lf);
     if (!m_hfNormal) return -1;
     lf.lfHeight += lf.lfHeight / 2; // 50% bigger
     m_hfChinese = CreateFontIndirect(&lf);
     if (!m_hfChinese) return -1;
     SetWindowFont(m_hwndLV, m_hfChinese, FALSE);
     ...
    }
    

    Before we change the default font for the list view to the Chinese font, we create a copy of the original font (which we rather presumptuously call "normal") for safekeeping. Next, when the list view asks us to customize a column, we select the appropriate font and return the "I also changed the font" code.

    LRESULT RootWindow::OnLVCustomDraw(NMLVCUSTOMDRAW* pcd)
    {
     ...
     case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
      pcd->clrText = m_clrTextNormal;
      if (pcd->iSubItem == COL_SIMP &&
        pcd->nmcd.dwItemSpec < (DWORD)Length()) {
        const DictionaryEntry& de = Item(pcd->nmcd.dwItemSpec);
        if (de.m_pszSimp) {
          pcd->clrText = RGB(0x80, 0x00, 0x00);
        }
      }
      // break;
      if (pcd->iSubItem == COL_TRAD || pcd->iSubItem == COL_SIMP) {
        SelectFont(pcd->nmcd.hdc, m_hfChinese);
      } else {
        SelectFont(pcd->nmcd.hdc, m_hfNormal);
      }
      return CDRF_NEWFONT;
     ...
    }
    

    There are several important details here.

    First, we set the Chinese font as the "overall" font for the list view. It would have been easier for us not to do this; after all, since we explicitly set the font for each column, why does it matter what the default font is? It also would have removed the need to create a copy of the original font. But if you delete the SetWindowFont(m_hwndLV, m_hfChinese); line, the bottoms of the Chinese characters get cut off. The reason is that the list view uses the default font to decide what the line spacing should be. Therefore, the default font for the list view needs to be the largest font we intend to use for any column.

    Why does the list view use the default font to decide on the line spacing? Because it's not clairevoyant. That's the only font it has, after all. It doesn't know what font you're going to select in your CDDS_ITEMPREPAINT | CDDS_SUBITEM notification handler. All it has is the font you set with SetWindowFont.

    Another important detail is that once we have decided to use different fonts for different columns, we are committed to selecting a font for all columns. The reason for this was discussed when we discussed how to colorize the columns.

    Finally, there is the important detail of returning the CDRF_NEWFONT value when we change the font. For performance reasons, the list view assumes you aren't changing the font on a subitem-by-subitem basis (since very few list views do) and it caches many font properties to avoid having to recalculate them all the time. Returning CDRF_NEWFONT indicates that the list view should look at the font you selected and base its computations on that font instead.

    Since boldface, italics and underline are font attributes, you can use this "select a custom font" technique to make selected items display as boldface, italics, or underline, in addition to using it to change the font size as we did here.

    That's all for this month. Next month will be a rather boring one, adding a status bar to make the Chinese characters even more readable. After that, we'll enhance the dictionary lookup algorithm, which is itself groundwork for dynamic translation, as I may have alluded to in a previous entry.

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    Fiddling with the fonts, part 1: Making the Chinese characters larger

    • 16 Comments

    Let's pay a quick visit to our continuing dictionary project. One of the things you may have noticed is that the Chinese characters are unreadably small. Let's fix that by making them larger.

    class RootWindow : public Window
    {
    public:
     virtual LPCTSTR ClassName() { return TEXT("Scratch"); }
     static RootWindow *Create();
     RootWindow();
     ~RootWindow();
     ...
    private:
     HWND m_hwndLV;
     HWND m_hwndEdit;
     HWND m_hwndLastFocus;
     HFONT m_hfChinese;
     int  m_cyEdit;
     ...
    }
    
    RootWindow::RootWindow()
     : m_hfChinese(NULL)
    {
    }
    
    RootWindow::~RootWindow()
    {
     if (m_hfChinese) DeleteObject(m_hfChinese);
    }
    
    LRESULT RootWindow::OnCreate()
    {
     ...
     ListView_SetExtendedListViewStyleEx(m_hwndLV,
                                         LVS_EX_FULLROWSELECT,
                                         LVS_EX_FULLROWSELECT);
    
     LOGFONT lf;
     if (!GetObject(GetWindowFont(m_hwndLV), sizeof(lf), &lf)) {
      return -1;
     }
     lf.lfHeight += lf.lfHeight / 2; // 50% bigger
     m_hfChinese = CreateFontIndirect(&lf);
     if (!m_hfChinese) return -1;
     SetWindowFont(m_hwndLV, m_hfChinese, FALSE);
    
     LVCOLUMN lvc;
     ...
    }
    

    This magnifies the font in the list view by 50% by taking the current font, increasing the height, and creating a new font, which we select into the list view.

    This works in that the font is indeed bigger, but it's bigger even for the English part, and that larger-than-normal English font looks kind of out of place. The English was perfectly fine at its original size, after all. It was only the Chinese we wanted to enlarge. (This will become more important later on when we turn the program into a dynamic translator.)

    We'll look at this problem next time.

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    Typo patrol at the PDC

    • 10 Comments

    You can find the following typographical errors in the Big Room at the PDC:

    • Intergrated
    • Precison
    • Personel

    If you can locate all three of them and catch me at the PDC (say at the Fundamentals Lounge or at the Ask the Experts table), I'll award you a prize of um (rummaging through my bag) how about a retractable network cable? (Or an autographed business card? But then again, anybody can get one of those just by asking for one.)

  • The Old New Thing

    Like an invention out of Harry Potter, except that Harry's would use a quill

    • 4 Comments

    When I heard this story from Swedish Radio, it struck me as the sort of object that would exist in the imaginary world of Harry Potter. Raymond's bad translation follows:

    Pen can help dyslexics

    With today's cheaper and cheaper microelectronics, it becomes possible for young inventors to produce new products without a lot of resources. A new idea for a smart-pen for speech synthesis and voice recognition is an example of this.

    The pen intended primarily for dyslexics has been produced by Johan Strömbom, a student at the Chalmers University of Technology. It is a pen which produces speech from writing, and which converts your speech into text, explains Johan Strömbom.

    "One can read in text from a piece of paper, which is then heard in a headset, also one can speak text into the headset and have it written out," says Johan Strömbom.

    He was inspired to invent the pen by his mother who teaches dyslexics. The pen has been nominated for an inventor's prize at the Technical Fair in October.

    Of course, the Harry Potter version would be a quill instead of a pen, and it wouldn't need a headset. Or batteries.

    Voice recognition also has a lot of promise for people other than dyslexics. Imagine if you could take a voice recording and send it through a recognizer to produce a transcript. I can read Swedish and German much better than I can listen to it, and having a transcript of a news report as it happens would improve my understanding tremendously.

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    The double-Ctrl+Alt+Del feature is really a kludge

    • 39 Comments

    Most people who care about such things know that you can press Ctrl+Alt+Del twice from the Welcome screen and sometimes you will get a classic logon dialog. (Note: "Sometimes". It works only if the last operation was a restart or log-off, for complicated reasons that are irrelevant to this discussion.)

    The ability to do the double-Ctrl+Alt+Del was added as a fallback just in case there turned out to be some important logon scenario that the new Welcome screen failed to cover, but which the designers had failed to take into account by simple oversight. Scenarios such as smartcard or fingerprint logon.

    In other words, it's a kludge.

    In the time since Windows XP came out, the logon folks have kept an eye out to see if there indeed were any scenarios that weren't covered by the Welcome screen. I think the only one that came up was Kerberos authentication.

    Now that (once they fix the Kerberos problem) they have covered all the bases, the designers are probably going to feel more confident about the new logon design, and the double-Ctrl+Alt+Del panic button will likely be removed.

    So don't get too attached to it.

    This is why the Welcome screen shows that Administrator account if there are no other members of the Administrators group on the system: If it didn't show the Administrator account, you would be locked out of your own computer.

    "No I'm not. I can use the double-Ctrl+Alt+Del trick to log on as the Administrator."

    Well, okay, that works today, but you're relying on a panic button that might not be there tomorrow.

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    Sorry I missed you all this morning

    • 4 Comments

    Sorry to all you PDCers who stopped by the Fundamentals Lounge to see me. The scheduling software that the PDC organizers cooked up is, um, "suboptimal" and listed me for Tuesday 11.30a–2.30p instead of 2.30p–5.30p. During the early afternoon shift, I was actually in the Hands-On Lab. But I'll be in the Lounge on Thursday after my talk all afternoon, really.

    I will try to be in the Lounge on Wednesday, but I may have to take refuge in the Speaker Prep Room to deal with a major last-minute change to my talk.

    Whoa a huge stream of people just flooded in. I'll catch up with you folks later.

  • The Old New Thing

    Annoying renditions of the songs of Elvis

    • 6 Comments

    Jim Nayder of The Annoying Music Show pops by NPR studios every so often—hey, guys, if you changed the locks, this might stop him—whereupon he tortures the general public with musical offenses that should be outlawed by the Geneva Conventions. Most recently, he offered a series of reinterpretations of the works of Elvis Presley. What's really scary is that the songs presented in the segment are among the less awful offerings. For genuine musical pain, check out Love Me Tender [WMV] [Real] as performed by "Sweden's Singin' Sensation" Eilert Pilarm. (Quick tip: "Lyssna" is Swedish for "listen.")

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    Why is there no all-encompassing superset version of Windows?

    • 55 Comments

    Sometimes, I am asked why there is no single version of Windows that contains everything. Instead, as you move up the ladder, say, from Windows XP Professional to Windows Server 2003, you gain server features and lose workstation features. Why lose features when you add others?

    Because it turns out no actual customer wants to keep the workstation features on their servers. Only developers want to have this "all-encompassing" version of Windows, and making it available to them would result in developers testing their programs on a version of Windows no actual customer owns.

    I think one of my colleagues who works in security support explained it best:

    When customers ask why their server has Internet Explorer, NetMeeting, Media Player, Games, Instant Messenger, etc., installed by default, it's hard for the support folks to come up with a good answer. Many customers view each additional installed component as additional risk, and want their servers to have the least possible amount of stuff installed.

    If you're the CIO of a bank, the thought that your servers are capable of playing Quake must give you the heebie-jeebies.

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    Richard E. Grant as Dr. Who

    • 10 Comments

    While waiting for the Ninth and Tenth Doctors to reach the States, I was tipped off to some animated Dr. Who episodes on the BBC web site. These are really well done and managed to slake my Doctor cravings for a little while longer.

    In particular, Richard E. Grant's second turn as the somewhat Earth-obsessed Time Lord in Scream of the Shalka is a must-see. The tired frustration of his Doctor is a refreshing change from the more amiable personas he's had in recent incarnations. After saving the Earth (nay, the universe) from certain destruction for over forty years, you kind of can't blame him for being fed up with the whole thing.

    (Second turn as The Doctor? Yup. Richard E. Grant played The Tenth Doctor in Doctor Who and the Curse of the Fatal Death, a spoof produced for the charity organization Comic Relief. I think Rowan Atkinson makes an excellent Doctor, if I may say so myself.)

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    Understanding the consequences of WAIT_ABANDONED

    • 27 Comments

    One of the important distinctions between mutexes and the other synchronization objects is that mutexes have owners. If the thread that owns a mutex exits without releasing the mutex, the mutex is automatically released on the thread's behalf.

    But if this happens, you're in big trouble.

    One thing many people gloss over is the WAIT_ABANDONED return value from the synchronization functions such as WaitForSingleObject. They typically treat this as a successful wait, because it does mean that the object was obtained, but it also tells you that the previous owner left the mutex abandoned and that the system had to release it on the owner's behalf.

    Why are you in big trouble when this happens?

    Presumably, you created that mutex to protect multiple threads from accessing a shared object while it is an unstable state. Code enters the mutex, then starts manipulating the object, temporarily making it unstable, but eventually restabilizing it and then releasing the mutex so that the next person can access the object.

    For example, you might have code that manages an anchored doubly-linked list in shared memory that goes like this:

    void MyClass::ReverseList()
    {
     WaitForSingleObject(hMutex, INFINITE);
     int i = 0; // anchor
     do {
      int next = m_items[i].m_next;
      m_items[i].m_next = m_items[i].m_prev;
      m_items[i].m_prev = next;
      i = next;
     } while (i != 0);
     ReleaseMutex(hMutex);
    }
    

    There is nothing particularly exciting going on. Basic stuff, right?

    But what if the program crashes while holding the mutex? (If you believe that your programs are bug-free, consider the possiblity that the program is running over the network and the network goes down, leading to an in-page exception. Or simply that the user went to Task Manager and terminated your program while this function is running.)

    In that case, the mutex is automatically released by the operating system, leaving the linked list in a corrupted state. The next program to claim the mutex will receive WAIT_ABANDONED as the status code. If you ignore that status code, you end up operating on a corrupted linked list. Depending on how that linked list is used, it may result in a resource leak or the system creating an unintended second copy of something, or perhaps even a crash. The unfortunate demise of one program causes other programs to start behaving strangely.

    Then again, the question remains, "What do you do, then, if you get WAIT_ABANDONED?" The answer is, "Good question."

    You might try to repair the corruption, if you keep enough auxiliary information around to recover a consistent state. You might even design your data structures to be transactional, so that the death of a thread manipulating the data structures does not leave them in a corrupted state. Or you might just decide that since things are corrupted, you should throw away everything and start over, losing the state of work in progress, but at least allowing new work to proceed unhindered.

    Or you might simply choose to ignore the error and continue onwards with a corrupt data structure, hoping that whatever went wrong won't result in cascade failures down the line. This is what most people do, though usually without even being aware that they're doing it. And it's really hard to debug the crashes that result from this approach.

    Exercise: Why did we use indices instead of pointers in our linked list data structure?

    [Raymond is currently away; this message was pre-recorded.]

Page 3 of 4 (39 items) 1234